diff options
296 files changed, 10888 insertions, 5493 deletions
diff --git a/Android.bp b/Android.bp index 9645ba6b85a1..763d242a12f9 100644 --- a/Android.bp +++ b/Android.bp @@ -212,6 +212,11 @@ java_library { "core/java/android/nfc/INfcUnlockHandler.aidl", "core/java/android/nfc/INfcDta.aidl", "core/java/android/nfc/ITagRemovedCallback.aidl", + "core/java/android/se/omapi/ISecureElementService.aidl", + "core/java/android/se/omapi/ISecureElementListener.aidl", + "core/java/android/se/omapi/ISecureElementChannel.aidl", + "core/java/android/se/omapi/ISecureElementReader.aidl", + "core/java/android/se/omapi/ISecureElementSession.aidl", "core/java/android/os/IBatteryPropertiesListener.aidl", "core/java/android/os/IBatteryPropertiesRegistrar.aidl", "core/java/android/os/ICancellationSignal.aidl", @@ -332,6 +337,8 @@ java_library { "core/java/android/view/IPinnedStackController.aidl", "core/java/android/view/IPinnedStackListener.aidl", "core/java/android/view/IRemoteAnimationRunner.aidl", + "core/java/android/view/IRecentsAnimationController.aidl", + "core/java/android/view/IRecentsAnimationRunner.aidl", "core/java/android/view/IRemoteAnimationFinishedCallback.aidl", "core/java/android/view/IRotationWatcher.aidl", "core/java/android/view/IWallpaperVisibilityListener.aidl", @@ -595,6 +602,7 @@ java_library { "core/java/android/speech/tts/EventLogTags.logtags", "core/java/android/net/EventLogTags.logtags", "core/java/android/webkit/EventLogTags.logtags", + "core/java/com/android/internal/app/EventLogTags.logtags", "core/java/com/android/internal/logging/EventLogTags.logtags", "core/java/com/android/server/DropboxLogTags.logtags", "core/java/org/chromium/arc/EventLogTags.logtags", diff --git a/Android.mk b/Android.mk index 32e4bfa57030..35b5f92be310 100644 --- a/Android.mk +++ b/Android.mk @@ -825,6 +825,16 @@ include $(BUILD_STATIC_JAVA_LIBRARY) # ==== hiddenapi lists ======================================= +# Copy blacklist and dark greylist over into the build folder. +# This is for ART buildbots which need to mock these lists and have alternative +# rules for building them. Other rules in the build system should depend on the +# files in the build folder. + +$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\ + $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))) +$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\ + $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))) + # Generate light greylist as private API minus (blacklist plus dark greylist). $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) diff --git a/api/current.txt b/api/current.txt index 70add6585cd0..2b6da9f5652a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -73,7 +73,6 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -13570,6 +13569,7 @@ package android.graphics { public static class ImageDecoder.ImageInfo { method public java.lang.String getMimeType(); method public android.util.Size getSize(); + method public boolean isAnimated(); } public static class ImageDecoder.IncompleteException extends java.io.IOException { @@ -14440,6 +14440,22 @@ package android.graphics.drawable { method public void onAnimationStart(android.graphics.drawable.Drawable); } + public class AnimatedImageDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable2 { + ctor public AnimatedImageDrawable(); + method public void clearAnimationCallbacks(); + method public void draw(android.graphics.Canvas); + method public int getOpacity(); + method public boolean isRunning(); + method public void registerAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback); + method public void setAlpha(int); + method public void setColorFilter(android.graphics.ColorFilter); + method public void setLoopCount(int); + method public void start(); + method public void stop(); + method public boolean unregisterAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback); + field public static final int LOOP_INFINITE = -1; // 0xffffffff + } + public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable { ctor public AnimatedStateListDrawable(); method public void addState(int[], android.graphics.drawable.Drawable, int); @@ -38068,6 +38084,59 @@ package android.sax { } +package android.se.omapi { + + public class Channel { + method public void close(); + method public byte[] getSelectResponse(); + method public android.se.omapi.Session getSession(); + method public boolean isBasicChannel(); + method public boolean isClosed(); + method public boolean selectNext() throws java.io.IOException; + method public byte[] transmit(byte[]) throws java.io.IOException; + } + + public abstract interface ISecureElementListener implements android.os.IInterface { + method public abstract void serviceConnected() throws android.os.RemoteException; + } + + public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener { + ctor public ISecureElementListener.Stub(); + method public android.os.IBinder asBinder(); + method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder); + method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; + } + + public class Reader { + method public void closeSessions(); + method public java.lang.String getName(); + method public android.se.omapi.SEService getSEService(); + method public boolean isSecureElementPresent(); + method public android.se.omapi.Session openSession() throws java.io.IOException; + } + + public class SEService { + ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener); + method public android.se.omapi.Reader[] getReaders(); + method public java.lang.String getVersion(); + method public boolean isConnected(); + method public void shutdown(); + } + + public class Session { + method public void close(); + method public void closeChannels(); + method public byte[] getATR(); + method public android.se.omapi.Reader getReader(); + method public boolean isClosed(); + method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException; + method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException; + method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException; + method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException; + } + +} + package android.security { public final class AttestedKeyPair { @@ -43794,13 +43863,19 @@ package android.text.style { public class QuoteSpan implements android.text.style.LeadingMarginSpan android.text.ParcelableSpan { ctor public QuoteSpan(); ctor public QuoteSpan(int); + ctor public QuoteSpan(int, int, int); ctor public QuoteSpan(android.os.Parcel); method public int describeContents(); method public void drawLeadingMargin(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, boolean, android.text.Layout); method public int getColor(); + method public int getGapWidth(); method public int getLeadingMargin(boolean); method public int getSpanTypeId(); + method public int getStripeWidth(); method public void writeToParcel(android.os.Parcel, int); + field public static final int STANDARD_COLOR = -16776961; // 0xff0000ff + field public static final int STANDARD_GAP_WIDTH_PX = 2; // 0x2 + field public static final int STANDARD_STRIPE_WIDTH_PX = 2; // 0x2 } public class RelativeSizeSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { diff --git a/api/removed.txt b/api/removed.txt index 77088e5bdab7..2aab223ed281 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -265,7 +265,6 @@ package android.os { method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int); method public android.graphics.drawable.Drawable getBadgedIconForUser(android.graphics.drawable.Drawable, android.os.UserHandle); method public java.lang.CharSequence getBadgedLabelForUser(java.lang.CharSequence, android.os.UserHandle); - method public deprecated boolean trySetQuietModeEnabled(boolean, android.os.UserHandle); } } diff --git a/api/system-current.txt b/api/system-current.txt index 91270599f62b..0375fe1dde36 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3523,12 +3523,17 @@ package android.os { public class HidlSupport { method public static boolean deepEquals(java.lang.Object, java.lang.Object); method public static int deepHashCode(java.lang.Object); + method public static int getPidIfSharable(); method public static boolean interfacesEqual(android.os.IHwInterface, java.lang.Object); } public abstract class HwBinder implements android.os.IHwBinder { method public static final void configureRpcThreadpool(long, boolean); + method public static void enableInstrumentation(); + method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException; + method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException; method public static final void joinRpcThreadpool(); + method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; } public class HwBlob { @@ -3628,6 +3633,8 @@ package android.os { public abstract interface IHwBinder { method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long); + method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String); + method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient); } @@ -4739,7 +4746,9 @@ package android.telephony { method public int describeContents(); method public int getAccessNetworkTechnology(); method public int[] getAvailableServices(); + method public android.telephony.CellIdentity getCellIdentity(); method public int getDomain(); + method public int getReasonForDenial(); method public int getRegState(); method public int getTransportType(); method public boolean isEmergencyEnabled(); diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp index 519852dbe88b..cdec6a01d086 100644 --- a/cmds/incident/main.cpp +++ b/cmds/incident/main.cpp @@ -148,9 +148,19 @@ find_section(const char* name) static int get_dest(const char* arg) { - if (strcmp(arg, "LOCAL") == 0) return 0; - if (strcmp(arg, "EXPLICIT") == 0) return 1; - if (strcmp(arg, "AUTOMATIC") == 0) return 2; + if (strcmp(arg, "L") == 0 + || strcmp(arg, "LOCAL") == 0) { + return DEST_LOCAL; + } + if (strcmp(arg, "E") == 0 + || strcmp(arg, "EXPLICIT") == 0) { + return DEST_EXPLICIT; + } + if (strcmp(arg, "A") == 0 + || strcmp(arg, "AUTO") == 0 + || strcmp(arg, "AUTOMATIC") == 0) { + return DEST_AUTOMATIC; + } return -1; // return the default value } diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 5db2239810e2..44adaecfe97f 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -67,8 +67,14 @@ PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } PrivacySpec new_spec_from_args(int dest) { - if (dest < 0) return PrivacySpec(); - return PrivacySpec(dest); + switch (dest) { + case android::os::DEST_AUTOMATIC: + case android::os::DEST_EXPLICIT: + case android::os::DEST_LOCAL: + return PrivacySpec(dest); + default: + return PrivacySpec(); + } } PrivacySpec get_default_dropbox_spec() { return PrivacySpec(android::os::DEST_AUTOMATIC); }
\ No newline at end of file diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c9902965ced0..99e871f0849f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -22,6 +22,9 @@ option java_package = "com.android.os"; option java_outer_classname = "AtomsProto"; import "frameworks/base/core/proto/android/app/enums.proto"; +import "frameworks/base/core/proto/android/os/enums.proto"; +import "frameworks/base/core/proto/android/telephony/enums.proto"; +import "frameworks/base/core/proto/android/view/enums.proto"; /** * The master atom class. This message defines all of the available @@ -162,18 +165,8 @@ message AttributionNode { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message ScreenStateChanged { - // TODO: Use the real screen state. - enum State { - STATE_UNKNOWN = 0; - STATE_OFF = 1; - STATE_ON = 2; - STATE_DOZE = 3; - STATE_DOZE_SUSPEND = 4; - STATE_VR = 5; - STATE_ON_SUSPEND = 6; - } - // New screen state. - optional State display_state = 1; + // New screen state, from frameworks/base/core/proto/android/view/enums.proto. + optional android.view.DisplayStateEnum state = 1; } /** @@ -196,7 +189,6 @@ message UidProcessStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message ProcessLifeCycleStateChanged { - // TODO: Use the real (mapped) process states. optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation // TODO: What is this? @@ -412,13 +404,9 @@ message CameraStateChanged { message WakelockStateChanged { repeated AttributionNode attribution_node = 1; - // Type of wakelock. - enum Type { - PARTIAL = 0; - FULL = 1; - WINDOW = 2; - } - optional Type type = 2; + // The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock. + // From frameworks/base/core/proto/android/os/enums.proto. + optional android.os.WakeLockLevelEnum level = 2; // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). optional string tag = 3; @@ -530,15 +518,8 @@ message BatteryLevelChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message ChargingStateChanged { - // TODO: Link directly to BatteryManager.java's constants (via a proto). - enum State { - BATTERY_STATUS_UNKNOWN = 1; - BATTERY_STATUS_CHARGING = 2; - BATTERY_STATUS_DISCHARGING = 3; - BATTERY_STATUS_NOT_CHARGING = 4; - BATTERY_STATUS_FULL = 5; - } - optional State charging_state = 1; + // State of the battery, from frameworks/base/core/proto/android/os/enums.proto. + optional android.os.BatteryStatusEnum state = 1; } /** @@ -548,18 +529,8 @@ message ChargingStateChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message PluggedStateChanged { - // TODO: Link directly to BatteryManager.java's constants (via a proto). - enum State { - // Note that NONE is not in BatteryManager.java's constants. - BATTERY_PLUGGED_NONE = 0; - // Power source is an AC charger. - BATTERY_PLUGGED_AC = 1; - // Power source is a USB port. - BATTERY_PLUGGED_USB = 2; - // Power source is wireless. - BATTERY_PLUGGED_WIRELESS = 4; - } - optional State plugged_state = 1; + // Whether the device is plugged in, from frameworks/base/core/proto/android/os/enums.proto. + optional android.os.BatteryPluggedStateEnum state = 1; } /** @@ -613,13 +584,8 @@ message MobileRadioPowerStateChanged { // TODO: Add attribution instead of uid? optional int32 uid = 1; - // TODO: Reference telephony/java/android/telephony/DataConnectionRealTimeInfo.java states. - enum PowerState { - DC_POWER_STATE_LOW = 1; - DC_POWER_STATE_MEDIUM = 2; - DC_POWER_STATE_HIGH = 3; - } - optional PowerState power_state = 2; + // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. + optional android.telephony.DataConnectionPowerStateEnum state = 2; } /** @@ -633,13 +599,8 @@ message WifiRadioPowerStateChanged { // TODO: Add attribution instead of uid? optional int32 uid = 1; - // TODO: Reference telephony/java/android/telephony/DataConnectionRealTimeInfo.java states. - enum PowerState { - DC_POWER_STATE_LOW = 1; - DC_POWER_STATE_MEDIUM = 2; - DC_POWER_STATE_HIGH = 3; - } - optional PowerState power_state = 2; + // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. + optional android.telephony.DataConnectionPowerStateEnum state = 2; } /** @@ -679,15 +640,8 @@ message WifiLockStateChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message WifiSignalStrengthChanged { - // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states. - enum SignalStrength { - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; - SIGNAL_STRENGTH_POOR = 1; - SIGNAL_STRENGTH_MODERATE = 2; - SIGNAL_STRENGTH_GOOD = 3; - SIGNAL_STRENGTH_GREAT = 4; - } - optional SignalStrength signal_strength = 1; + // Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto. + optional android.telephony.SignalStrengthEnum signal_strength = 1; } /** @@ -729,15 +683,8 @@ message WifiMulticastLockStateChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message PhoneSignalStrengthChanged { - // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states. - enum SignalStrength { - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; - SIGNAL_STRENGTH_POOR = 1; - SIGNAL_STRENGTH_MODERATE = 2; - SIGNAL_STRENGTH_GOOD = 3; - SIGNAL_STRENGTH_GREAT = 4; - } - optional SignalStrength signal_strength = 1; + // Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto. + optional android.telephony.SignalStrengthEnum signal_strength = 1; } /** @@ -890,15 +837,7 @@ message AppStartChanged { // Device uptime when activity started. optional int64 activity_start_msec = 7; - // TODO: Update android/app/ActivityManagerInternal.java constants to depend on our proto enum. - enum TransitionReason { - APP_START_TRANSITION_REASON_UNKNOWN = 0; - SPLASH_SCREEN = 1; - WINDOWS_DRAWN = 2; - TIMEOUT = 3; - SNAPSHOT = 4; - } - optional TransitionReason reason = 8; + optional android.app.AppTransitionReasonEnum reason = 8; optional int32 transition_delay_msec = 9; // -1 if not set. diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 83b72d966a39..00d86582951f 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -63,7 +63,7 @@ static void parseFileName(char* name, int64_t* result) { } static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) { - return StringPrintf("%s/%lld-%d-%lld", path, (long long)timestamp, (int)uid, + return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid, (long long)configID); } diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index eda16a2ffbfd..4504a95c8ef0 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -123,11 +123,13 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); auto screenTurnedOnEvent = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 2); + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 2); auto screenTurnedOffEvent = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200); + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 200); auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2 * bucketSizeNs - 100); auto syncOnEvent1 = diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index e656b98c66ad..1186a166dfab 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -73,11 +73,13 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); auto screenTurnedOnEvent = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 1); + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 1); auto screenTurnedOffEvent = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200); + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 200); auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 500); std::vector<AttributionNode> attributions1 = @@ -156,7 +158,8 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { events.clear(); events.push_back(CreateScreenStateChangedEvent( - ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs + 90)); + android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 2 * bucketSizeNs + 90)); events.push_back(CreateAcquireWakelockEvent( attributions1, "wl3", bucketStartTimeNs + 2 * bucketSizeNs + 100)); events.push_back(CreateReleaseWakelockEvent( diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 718b2e177d52..9f4582dc6994 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -48,7 +48,7 @@ AtomMatcher CreateReleaseWakelockAtomMatcher() { } AtomMatcher CreateScreenStateChangedAtomMatcher( - const string& name, ScreenStateChanged::State state) { + const string& name, android::view::DisplayStateEnum state) { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); @@ -60,11 +60,13 @@ AtomMatcher CreateScreenStateChangedAtomMatcher( } AtomMatcher CreateScreenTurnedOnAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", ScreenStateChanged::STATE_ON); + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", + android::view::DisplayStateEnum::DISPLAY_STATE_ON); } AtomMatcher CreateScreenTurnedOffAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", ScreenStateChanged::STATE_OFF); + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", + ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); } AtomMatcher CreateSyncStateChangedAtomMatcher( @@ -209,7 +211,7 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) } std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const ScreenStateChanged::State state, uint64_t timestampNs) { + const android::view::DisplayStateEnum state, uint64_t timestampNs) { auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs); EXPECT_TRUE(event->write(state)); event->init(); @@ -221,7 +223,7 @@ std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( const WakelockStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs); event->write(attributions); - event->write(WakelockStateChanged::PARTIAL); + event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); event->write(wakelockName); event->write(state); event->init(); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 1fc33ded13c1..ff8fef0c46b6 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -84,7 +84,7 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId, // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const ScreenStateChanged::State state, uint64_t timestampNs); + const android::view::DisplayStateEnum state, uint64_t timestampNs); // Create log event for app moving to background. std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java index d4b2aa4ff241..5dcce9acb401 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java @@ -90,7 +90,7 @@ public class SequencePusher { case 2: case 10: StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_CHARGING + StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_CHARGING /* charging_state */); break; case 3: @@ -103,7 +103,7 @@ public class SequencePusher { case 4: case 12: StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_NOT_CHARGING + StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING /* charging_state */); break; case 5: @@ -115,7 +115,7 @@ public class SequencePusher { break; case 6: StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_ON /* display_state */); + StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON /* display_state */); break; case 7: for (int i = 0; i < mBurst; i++) { @@ -125,7 +125,7 @@ public class SequencePusher { break; case 14: StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_OFF /* display_state */); + StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */); break; case 15: for (int i = 0; i < mBurst; i++) { @@ -147,14 +147,14 @@ public class SequencePusher { public void finish() { // Screen goes back to off. This will ensure that conditions get back to false. StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_OFF /* display_state */); + StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */); for (int i = 0; i < mBurst; i++) { StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */, StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */); } // Stop charging, to ensure the corresponding durations are closed. StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_NOT_CHARGING + StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING /* charging_state */); // Stop scanning GPS, to ensure the corresponding conditions get back to false. for (int i = 0; i < mBurst; i++) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index cd029c06b91d..b58c523f7927 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -129,6 +129,8 @@ import com.android.internal.app.WindowDecorActionBar; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneWindow; +import dalvik.system.VMRuntime; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -2136,11 +2138,15 @@ public class Activity extends ContextThemeWrapper * @param params non-null parameters to be combined with previously set parameters when entering * picture-in-picture. * - * @return true if the system puts this activity into picture-in-picture mode or was already - * in picture-in-picture mode (@see {@link #isInPictureInPictureMode()) + * @return true if the system successfully put this activity into picture-in-picture mode or was + * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device + * does not support picture-in-picture, return false. */ public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) { try { + if (!deviceSupportsPictureInPictureMode()) { + return false; + } if (params == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture params"); } @@ -2168,6 +2174,9 @@ public class Activity extends ContextThemeWrapper */ public void setPictureInPictureParams(@NonNull PictureInPictureParams params) { try { + if (!deviceSupportsPictureInPictureMode()) { + return; + } if (params == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture params"); } @@ -2190,6 +2199,13 @@ public class Activity extends ContextThemeWrapper } } + /** + * @return Whether this device supports picture-in-picture. + */ + private boolean deviceSupportsPictureInPictureMode() { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE); + } + void dispatchMovedToDisplay(int displayId, Configuration config) { updateDisplay(displayId); onMovedToDisplay(displayId, config); @@ -7099,11 +7115,12 @@ public class Activity extends ContextThemeWrapper mFragments.dispatchStart(); mFragments.reportLoaderStart(); - // This property is set for all builds except final release - boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1; boolean isAppDebuggable = (mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + // This property is set for all builds except final release + boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1; + if (isAppDebuggable || isDlwarningEnabled) { String dlwarning = getDlWarning(); if (dlwarning != null) { @@ -7124,6 +7141,28 @@ public class Activity extends ContextThemeWrapper } } + // We might disable this for final builds. + boolean isApiWarningEnabled = true; + + if (isAppDebuggable || isApiWarningEnabled) { + if (VMRuntime.getRuntime().hasUsedHiddenApi()) { + String appName = getApplicationInfo().loadLabel(getPackageManager()) + .toString(); + String warning = "Detected problems with API compatiblity\n" + + "(please consult log for detail)"; + if (isAppDebuggable) { + new AlertDialog.Builder(this) + .setTitle(appName) + .setMessage(warning) + .setPositiveButton(android.R.string.ok, null) + .setCancelable(false) + .show(); + } else { + Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show(); + } + } + } + mActivityTransitionState.enterReady(this); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index da9f72855e09..5ee7edee9db7 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -43,25 +43,29 @@ public abstract class ActivityManagerInternal { * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew * the splash screen. */ - public static final int APP_TRANSITION_SPLASH_SCREEN = 1; + public static final int APP_TRANSITION_SPLASH_SCREEN = + AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1 /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all * app windows were drawn */ - public static final int APP_TRANSITION_WINDOWS_DRAWN = 2; + public static final int APP_TRANSITION_WINDOWS_DRAWN = + AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2 /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a * timeout. */ - public static final int APP_TRANSITION_TIMEOUT = 3; + public static final int APP_TRANSITION_TIMEOUT = + AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3 /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a * we drew a task snapshot. */ - public static final int APP_TRANSITION_SNAPSHOT = 4; + public static final int APP_TRANSITION_SNAPSHOT = + AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4 /** * The bundle key to extract the assist data. diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 4bcd677e1f4e..fee58274a5fc 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -207,6 +207,12 @@ public class ActivityOptions { "android.activity.taskOverlayCanResume"; /** + * See {@link #setAvoidMoveToFront()}. + * @hide + */ + private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront"; + + /** * Where the split-screen-primary stack should be positioned. * @hide */ @@ -307,6 +313,7 @@ public class ActivityOptions { private boolean mDisallowEnterPictureInPictureWhileLaunching; private boolean mTaskOverlay; private boolean mTaskOverlayCanResume; + private boolean mAvoidMoveToFront; private AppTransitionAnimationSpec mAnimSpecs[]; private int mRotationAnimationHint = -1; private Bundle mAppVerificationBundle; @@ -923,6 +930,7 @@ public class ActivityOptions { mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false); + mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false); mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT); mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean( @@ -1239,6 +1247,25 @@ public class ActivityOptions { return mTaskOverlayCanResume; } + /** + * Sets whether the activity launched should not cause the activity stack it is contained in to + * be moved to the front as a part of launching. + * + * @hide + */ + public void setAvoidMoveToFront() { + mAvoidMoveToFront = true; + } + + /** + * @return whether the activity launch should prevent moving the associated activity stack to + * the front. + * @hide + */ + public boolean getAvoidMoveToFront() { + return mAvoidMoveToFront; + } + /** @hide */ public int getSplitScreenCreateMode() { return mSplitScreenCreateMode; @@ -1416,6 +1443,7 @@ public class ActivityOptions { b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId); b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay); b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume); + b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront); b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode); b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, mDisallowEnterPictureInPictureWhileLaunching); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 934b0f3cd4e4..3dedeea1a1b0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5114,6 +5114,8 @@ public final class ActivityThread extends ClientTransactionHandler { // as that method uses the same check on the activity config override as well. final boolean equivalent = config != null && mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config)); + final Theme systemTheme = getSystemContext().getTheme(); + final Theme systemUiTheme = getSystemUiContext().getTheme(); synchronized (mResourcesManager) { if (mPendingConfiguration != null) { @@ -5146,12 +5148,10 @@ public final class ActivityThread extends ClientTransactionHandler { configDiff = mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); - final Theme systemTheme = getSystemContext().getTheme(); if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { systemTheme.rebase(); } - final Theme systemUiTheme = getSystemUiContext().getTheme(); if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) { systemUiTheme.rebase(); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b25deeac75cb..6dcecf197ed2 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -66,6 +66,7 @@ import android.os.PersistableBundle; import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; +import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationDefinition; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; @@ -443,8 +444,9 @@ interface IActivityManager { in Bundle options, int userId); int startAssistantActivity(in String callingPackage, int callingPid, int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId); - int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options, - in Bundle activityOptions, int userId); + void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver, + in IRecentsAnimationRunner recentsAnimationRunner); + void cancelRecentsAnimation(); int startActivityFromRecents(int taskId, in Bundle options); Bundle getActivityOptions(in IBinder token); List<IBinder> getAppTasks(in String callingPackage); diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index 998ac5f2a487..c34f4d9f0fe8 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -16,6 +16,8 @@ package android.app; +import android.app.ActivityThread.ActivityClientRecord; +import android.app.servertransaction.PendingTransactionActions; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Binder; @@ -141,6 +143,21 @@ public class LocalActivityManager { } r.window = r.activity.getWindow(); r.instanceState = null; + + final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r); + final PendingTransactionActions pendingActions; + + if (!r.activity.mFinished) { + // This matches pending actions set in ActivityThread#handleLaunchActivity + pendingActions = new PendingTransactionActions(); + pendingActions.setOldState(clientRecord.state); + pendingActions.setRestoreInstanceState(true); + pendingActions.setCallOnPostCreate(true); + } else { + pendingActions = null; + } + + mActivityThread.handleStartActivity(clientRecord, pendingActions); r.curState = STARTED; if (desiredState == RESUMED) { diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index ea0fd75bec90..256c47934dc5 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -471,6 +471,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #onStart} and returns either {@link #START_STICKY} * or {@link #START_STICKY_COMPATIBILITY}. * + * <p>If you need your application to run on platform versions prior to API + * level 5, you can use the following model to handle the older {@link #onStart} + * callback in that case. The <code>handleCommand</code> method is implemented by + * you as appropriate: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java + * start_compatibility} + * * <p class="caution">Note that the system calls this on your * service's main thread. A service's main thread is the same * thread where UI operations take place for Activities running in the @@ -679,10 +687,6 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #startService(Intent)} first to tell the system it should keep the service running, * and then use this method to tell it to keep it running harder.</p> * - * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request - * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use - * this API.</p> - * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index bd978e3df863..5d6a989ffef8 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -146,6 +146,14 @@ public abstract class UsageStatsManagerInternal { * allowed to do work even if they're idle or in a low bucket. */ public abstract void onParoleStateChanged(boolean isParoleOn); + + /** + * Optional callback to inform the listener that the app has transitioned into + * an active state due to user interaction. + */ + public void onUserInteractionStarted(String packageName, @UserIdInt int userId) { + // No-op by default + } } /** Backup/Restore API */ @@ -212,4 +220,17 @@ public abstract class UsageStatsManagerInternal { * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}. */ public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId); + + /** + * Report a few data points about an app's job state at the current time. + * + * @param packageName the app whose job state is being described + * @param userId which user the app is associated with + * @param numDeferredJobs the number of pending jobs that were deferred + * due to bucketing policy + * @param timeSinceLastJobRun number of milliseconds since the last time one of + * this app's jobs was executed + */ + public abstract void reportAppJobState(String packageName, @UserIdInt int userId, + int numDeferredJobs, long timeSinceLastJobRun); } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 746a090264c2..f6697e8148a0 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -602,6 +602,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_VENDOR = 1 << 18; + /** + * Value for {@linl #privateFlags}: whether this app is pre-installed on the + * product partition of the system image. + * @hide + */ + public static final int PRIVATE_FLAG_PRODUCT = 1 << 19; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = { PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, @@ -619,6 +626,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_OEM, PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE, PRIVATE_FLAG_PRIVILEGED, + PRIVATE_FLAG_PRODUCT, PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, PRIVATE_FLAG_STATIC_SHARED_LIBRARY, PRIVATE_FLAG_VENDOR, @@ -1699,6 +1707,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } + /** @hide */ + public boolean isProduct() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; + } + /** * Returns whether or not this application was installed as a virtual preload. */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 3bb812b7ac83..3d26af1f15bb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,7 +54,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1595,19 +1594,21 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; + AssetManager assets = null; XmlResourceParser parser = null; try { - try { - apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, false, false) - : ApkAssets.loadFromPath(apkPath); - } catch (IOException e) { + assets = newConfiguredAssetManager(); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); + if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1635,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + IoUtils.closeQuietly(assets); } } @@ -6393,6 +6394,11 @@ public class PackageParser { } /** @hide */ + public boolean isProduct() { + return applicationInfo.isProduct(); + } + + /** @hide */ public boolean isPrivileged() { return applicationInfo.isPrivilegedApp(); } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java deleted file mode 100644 index b087c48d8d4c..000000000000 --- a/core/java/android/content/res/ApkAssets.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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.content.res; - -import android.annotation.NonNull; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * The loaded, immutable, in-memory representation of an APK. - * - * The main implementation is native C++ and there is very little API surface exposed here. The APK - * is mainly accessed via {@link AssetManager}. - * - * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, - * making the creation of AssetManagers very cheap. - * @hide - */ -public final class ApkAssets implements AutoCloseable { - @GuardedBy("this") private long mNativePtr; - @GuardedBy("this") private StringBlock mStringBlock; - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { - return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) - throws IOException { - return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, - boolean forceSharedLibrary) throws IOException { - return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. - * - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * - * @param fd The FileDescriptor of an open, readable APK. - * @param friendlyName The friendly name used to identify this ApkAssets when logging. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) - throws IOException { - return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); - } - - /** - * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path - * is encoded within the IDMAP. - * - * @param idmapPath Path to the IDMAP of an overlay APK. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) - throws IOException { - return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); - } - - private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException { - Preconditions.checkNotNull(path, "path"); - mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, - boolean forceSharedLib) throws IOException { - Preconditions.checkNotNull(fd, "fd"); - Preconditions.checkNotNull(friendlyName, "friendlyName"); - mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - @NonNull String getAssetPath() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssetPath(mNativePtr); - } - } - - CharSequence getStringFromPool(int idx) { - synchronized (this) { - ensureValidLocked(); - return mStringBlock.get(idx); - } - } - - /** - * Retrieve a parser for a compiled XML file. This is associated with a single APK and - * <em>NOT</em> a full AssetManager. This means that shared-library references will not be - * dynamically assigned runtime package IDs. - * - * @param fileName The path to the file within the APK. - * @return An XmlResourceParser. - * @throws IOException if the file was not found or an error occurred retrieving it. - */ - public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); - synchronized (this) { - ensureValidLocked(); - long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); - try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { - XmlResourceParser parser = block.newParser(); - // If nativeOpenXml doesn't throw, it will always return a valid native pointer, - // which makes newParser always return non-null. But let's be paranoid. - if (parser == null) { - throw new AssertionError("block.newParser() returned a null parser"); - } - return parser; - } - } - } - - /** - * Returns false if the underlying APK was changed since this ApkAssets was loaded. - */ - public boolean isUpToDate() { - synchronized (this) { - ensureValidLocked(); - return nativeIsUpToDate(mNativePtr); - } - } - - /** - * Closes the ApkAssets and destroys the underlying native implementation. Further use of the - * ApkAssets object will cause exceptions to be thrown. - * - * Calling close on an already closed ApkAssets does nothing. - */ - @Override - public void close() { - synchronized (this) { - if (mNativePtr == 0) { - return; - } - - mStringBlock = null; - nativeDestroy(mNativePtr); - mNativePtr = 0; - } - } - - @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nativeDestroy(mNativePtr); - } - } - - private void ensureValidLocked() { - if (mNativePtr == 0) { - throw new RuntimeException("ApkAssets is closed"); - } - } - - private static native long nativeLoad( - @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException; - private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLib) - throws IOException; - private static native void nativeDestroy(long ptr); - private static native @NonNull String nativeGetAssetPath(long ptr); - private static native long nativeGetStringBlock(long ptr); - private static native boolean nativeIsUpToDate(long ptr); - private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; -} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78370f4d56a8..78665609bdd4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,11 +18,9 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; -import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -30,18 +28,10 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.BufferedReader; -import java.io.FileInputStream; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.channels.FileLock; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; /** @@ -52,17 +42,7 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - private static final String TAG = "AssetManager"; - private static final boolean DEBUG_REFS = false; - - private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; - - private static final Object sSync = new Object(); - - // Not private for LayoutLib's BridgeAssetManager. - @GuardedBy("sSync") static AssetManager sSystem = null; - - @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how @@ -85,279 +65,87 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - @GuardedBy("this") private final TypedValue mValue = new TypedValue(); - @GuardedBy("this") private final long[] mOffsets = new long[2]; - - // Pointer to native implementation, stuffed inside a long. - @GuardedBy("this") private long mObject; - - // The loaded asset paths. - @GuardedBy("this") private ApkAssets[] mApkAssets; + private static final String TAG = "AssetManager"; + private static final boolean localLOGV = false || false; + + private static final boolean DEBUG_REFS = false; + + private static final Object sSync = new Object(); + /*package*/ static AssetManager sSystem = null; - // Debug/reference counting implementation. - @GuardedBy("this") private boolean mOpen = true; - @GuardedBy("this") private int mNumRefs = 1; - @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks; + private final TypedValue mValue = new TypedValue(); + private final long[] mOffsets = new long[2]; + + // For communication with native code. + private long mObject; + private StringBlock mStringBlocks[] = null; + + private int mNumRefs = 1; + private boolean mOpen = true; + private HashMap<Long, RuntimeException> mRefStacks; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * @hide + * {@hide} */ public AssetManager() { - final ApkAssets[] assets; - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - assets = sSystemApkAssets; - } - - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + init(false); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); + ensureSystemAssets(); } - - // Always set the framework resources. - setApkAssets(assets, false /*invalidateCaches*/); } - /** - * Private constructor that doesn't call ensureSystemAssets. - * Used for the creation of system assets. - */ - @SuppressWarnings("unused") - private AssetManager(boolean sentinel) { - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + private static void ensureSystemAssets() { + synchronized (sSync) { + if (sSystem == null) { + AssetManager system = new AssetManager(true); + system.makeStringBlocks(null); + sSystem = system; + } } } - - /** - * This must be called from Zygote so that system assets are shared by all applications. - * @hide - */ - private static void createSystemAssetsInZygoteLocked() { - if (sSystem != null) { - return; - } - - // Make sure that all IDMAPs are up to date. - nativeVerifySystemIdmaps(); - - try { - ArrayList<ApkAssets> apkAssets = new ArrayList<>(); - apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); - - // Load all static RROs. - try (FileInputStream fis = new FileInputStream( - "/data/resource-cache/overlays.list"); - BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { - // Acquire a lock so that any idmap scanning doesn't impact the current set. - try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, - true /*shared*/)) { - for (String line; (line = br.readLine()) != null; ) { - String idmapPath = line.split(" ")[1]; - apkAssets.add( - ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); - } - } + + private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); } - - sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); - sSystem = new AssetManager(true /*sentinel*/); - sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); - } catch (IOException e) { - throw new IllegalStateException("Failed to create system AssetManager", e); } + init(true); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * @hide + * {@hide} */ public static AssetManager getSystem() { - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - return sSystem; - } + ensureSystemAssets(); + return sSystem; } /** * Close this asset manager. */ - @Override public void close() { - synchronized (this) { - if (!mOpen) { - return; - } - - mOpen = false; - decRefsLocked(hashCode()); - } - } - - /** - * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} - * family of methods. - * - * @param apkAssets The new set of paths. - * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. - * Set this to false if you are appending new resources - * (not new configurations). - * @hide - */ - public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { - Preconditions.checkNotNull(apkAssets, "apkAssets"); - synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); - if (invalidateCaches) { - // Invalidate all caches. - invalidateCachesLocked(-1); - } - } - } - - /** - * Invalidates the caches in this AssetManager according to the bitmask `diff`. - * - * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. - * @see ActivityInfo.Config - */ - private void invalidateCachesLocked(int diff) { - // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. - } - - /** - * @hide - */ - public @NonNull ApkAssets[] getApkAssets() { - synchronized (this) { - ensureValidLocked(); - return mApkAssets; - } - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPath(String path) { - return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addOverlayPath(String path) { - return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); - } - - private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureOpenLocked(); - final int count = mApkAssets.length; - for (int i = 0; i < count; i++) { - if (mApkAssets[i].getAssetPath().equals(path)) { - return i + 1; - } - } - - final ApkAssets assets; - try { - if (overlay) { - // TODO(b/70343104): This hardcoded path will be removed once - // addAssetPathInternal is deleted. - final String idmapPath = "/data/resource-cache/" - + path.substring(1).replace('/', '@') - + "@idmap"; - assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); - } else { - assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); - } - } catch (IOException e) { - return 0; + synchronized(this) { + //System.out.println("Release: num=" + mNumRefs + // + ", released=" + mReleased); + if (mOpen) { + mOpen = false; + decRefsLocked(this.hashCode()); } - - final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); - newApkAssets[count] = assets; - setApkAssets(newApkAssets, true); - return count + 1; - } - } - - /** - * Ensures that the native implementation has not been destroyed. - * The AssetManager may have been closed, but references to it still exist - * and therefore the native implementation is not destroyed. - */ - private void ensureValidLocked() { - if (mObject == 0) { - throw new RuntimeException("AssetManager has been destroyed"); - } - } - - /** - * Ensures that the AssetManager has not been explicitly closed. If this method passes, - * then this implies that ensureValidLocked() also passes. - */ - private void ensureOpenLocked() { - if (!mOpen) { - throw new RuntimeException("AssetManager has been closed"); - } - } - - /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. - * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise - */ - boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeGetResourceValue( - mObject, resId, (short) densityDpi, outValue, resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; } } @@ -368,7 +156,8 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceText(@StringRes int resId) { + @Nullable + final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -383,15 +172,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId the index into the bag to load + * @param bagEntryId * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable + final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { - ensureValidLocked(); final TypedValue outValue = mValue; - final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); - if (cookie <= 0) { + final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); + if (block < 0) { return null; } @@ -400,60 +189,52 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mApkAssets[cookie - 1].getStringFromPool(outValue.data); + return mStringBlocks[block].get(outValue.data); } return outValue.coerceToString(); } } - int getResourceArraySize(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArraySize(mObject, resId); - } - } - /** - * Populates `outData` with array elements of `resId`. `outData` is normally - * used with - * {@link TypedArray}. - * - * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} - * long, - * with the indices of the data representing the type, value, asset cookie, - * resource ID, - * configuration change mask, and density of the element. - * - * @param resId The resource ID of an array resource. - * @param outData The array to populate with data. - * @return The length of the array. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @see TypedArray#STYLE_TYPE - * @see TypedArray#STYLE_DATA - * @see TypedArray#STYLE_ASSET_COOKIE - * @see TypedArray#STYLE_RESOURCE_ID - * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS - * @see TypedArray#STYLE_DENSITY + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { - Preconditions.checkNotNull(outData, "outData"); - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArray(mObject, resId, outData); - } + @Nullable + final String[] getResourceStringArray(@ArrayRes int resId) { + return getArrayStringResource(resId); } /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise */ - @Nullable String[] getResourceStringArray(@ArrayRes int resId) { + final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceStringArray(mObject, resId); + final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); + if (block < 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mStringBlocks[block].get(outValue.data); + } + return true; } } @@ -463,48 +244,26 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - ensureValidLocked(); - final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); + final int[] rawInfoArray = getArrayStringInfo(resId); if (rawInfoArray == null) { return null; } - final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; + int block; + int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - int cookie = rawInfoArray[i]; - int index = rawInfoArray[i + 1]; - retArray[j] = (index >= 0 && cookie > 0) - ? mApkAssets[cookie - 1].getStringFromPool(index) : null; + block = rawInfoArray[i]; + index = rawInfoArray[i + 1]; + retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; } return retArray; } } - @Nullable int[] getResourceIntArray(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceIntArray(mObject, resId); - } - } - - /** - * Get the attributes for a style resource. These are the <item> - * elements in - * a <style> resource. - * @param resId The resource ID of the style - * @return An array of attribute IDs. - */ - @AttrRes int[] getStyleAttributes(@StyleRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetStyleAttributes(mObject, resId); - } - } - /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -518,88 +277,73 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, - resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - - void dumpTheme(long theme, int priority, String tag, String prefix) { - synchronized (this) { - ensureValidLocked(); - nativeThemeDump(mObject, theme, priority, tag, prefix); + final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); + if (block < 0) { + return false; } - } - @Nullable String getResourceName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceName(mObject, resId); - } - } + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); - @Nullable String getResourcePackageName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourcePackageName(mObject, resId); + if (outValue.type == TypedValue.TYPE_STRING) { + final StringBlock[] blocks = ensureStringBlocks(); + outValue.string = blocks[block].get(outValue.data); } + return true; } - @Nullable String getResourceTypeName(@AnyRes int resId) { + /** + * Ensures the string blocks are loaded. + * + * @return the string blocks + */ + @NonNull + final StringBlock[] ensureStringBlocks() { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceTypeName(mObject, resId); + if (mStringBlocks == null) { + makeStringBlocks(sSystem.mStringBlocks); + } + return mStringBlocks; } } - @Nullable String getResourceEntryName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceEntryName(mObject, resId); + /*package*/ final void makeStringBlocks(StringBlock[] seed) { + final int seedNum = (seed != null) ? seed.length : 0; + final int num = getStringBlockCount(); + mStringBlocks = new StringBlock[num]; + if (localLOGV) Log.v(TAG, "Making string blocks for " + this + + ": " + num); + for (int i=0; i<num; i++) { + if (i < seedNum) { + mStringBlocks[i] = seed[i]; + } else { + mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); + } } } - @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType, - @Nullable String defPackage) { + /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) { synchronized (this) { - ensureValidLocked(); - // name is checked in JNI. - return nativeGetResourceIdentifier(mObject, name, defType, defPackage); + // Cookies map to string blocks starting at 1. + return mStringBlocks[cookie - 1].get(id); } } - CharSequence getPooledStringForCookie(int cookie, int id) { - // Cookies map to ApkAssets starting at 1. - return getApkAssets()[cookie - 1].getStringFromPool(id); - } - /** * Open an asset using ACCESS_STREAMING mode. This provides access to * files that have been bundled with an application as assets -- that is, * files placed in to the "assets" directory. * - * @param fileName The name of the asset to open. This name can be hierarchical. + * @param fileName The name of the asset to open. This name can be + * hierarchical. * * @see #open(String, int) * @see #list */ - public @NonNull InputStream open(@NonNull String fileName) throws IOException { + public final InputStream open(String fileName) throws IOException { return open(fileName, ACCESS_STREAMING); } @@ -609,7 +353,8 @@ public final class AssetManager implements AutoCloseable { * with an application as assets -- that is, files placed in to the * "assets" directory. * - * @param fileName The name of the asset to open. This name can be hierarchical. + * @param fileName The name of the asset to open. This name can be + * hierarchical. * @param accessMode Desired access mode for retrieving the data. * * @see #ACCESS_UNKNOWN @@ -619,40 +364,34 @@ public final class AssetManager implements AutoCloseable { * @see #open(String) * @see #list */ - public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + public final InputStream open(String fileName, int accessMode) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long asset = nativeOpenAsset(mObject, fileName, accessMode); - if (asset == 0) { - throw new FileNotFoundException("Asset file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + long asset = openAsset(fileName, accessMode); + if (asset != 0) { + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } - final AssetInputStream assetInputStream = new AssetInputStream(asset); - incRefsLocked(assetInputStream.hashCode()); - return assetInputStream; } + throw new FileNotFoundException("Asset file: " + fileName); } - /** - * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}. - * This provides access to files that have been bundled with an application as assets -- that - * is, files placed in to the "assets" directory. - * - * The asset must be uncompressed, or an exception will be thrown. - * - * @param fileName The name of the asset to open. This name can be hierarchical. - * @return An open AssetFileDescriptor. - */ - public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + public final AssetFileDescriptor openFd(String fileName) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); - if (pfd == null) { - throw new FileNotFoundException("Asset file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); + if (pfd != null) { + return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } - return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } + throw new FileNotFoundException("Asset file: " + fileName); } /** @@ -667,121 +406,90 @@ public final class AssetManager implements AutoCloseable { * * @see #open */ - public @Nullable String[] list(@NonNull String path) throws IOException { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureValidLocked(); - return nativeList(mObject, path); - } - } + public native final String[] list(String path) + throws IOException; /** + * {@hide} * Open a non-asset file as an asset using ACCESS_STREAMING mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. - * - * @param fileName Name of the asset to retrieve. - * + * * @see #open(String) - * @hide */ - public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException { + public final InputStream openNonAsset(String fileName) throws IOException { return openNonAsset(0, fileName, ACCESS_STREAMING); } /** + * {@hide} * Open a non-asset file as an asset using a specific access mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. - * - * @param fileName Name of the asset to retrieve. - * @param accessMode Desired access mode for retrieving the data. - * - * @see #ACCESS_UNKNOWN - * @see #ACCESS_STREAMING - * @see #ACCESS_RANDOM - * @see #ACCESS_BUFFER + * * @see #open(String, int) - * @hide */ - public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode) - throws IOException { + public final InputStream openNonAsset(String fileName, int accessMode) + throws IOException { return openNonAsset(0, fileName, accessMode); } /** + * {@hide} * Open a non-asset in a specified package. Not for use by applications. - * + * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. - * @hide */ - public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName) - throws IOException { + public final InputStream openNonAsset(int cookie, String fileName) + throws IOException { return openNonAsset(cookie, fileName, ACCESS_STREAMING); } /** + * {@hide} * Open a non-asset in a specified package. Not for use by applications. - * + * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. * @param accessMode Desired access mode for retrieving the data. - * @hide */ - public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode) - throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + public final InputStream openNonAsset(int cookie, String fileName, int accessMode) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); - if (asset == 0) { - throw new FileNotFoundException("Asset absolute file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + long asset = openNonAssetNative(cookie, fileName, accessMode); + if (asset != 0) { + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } - final AssetInputStream assetInputStream = new AssetInputStream(asset); - incRefsLocked(assetInputStream.hashCode()); - return assetInputStream; } + throw new FileNotFoundException("Asset absolute file: " + fileName); } - /** - * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. - * This provides direct access to all of the files included in an application - * package (not only its assets). Applications should not normally use this. - * - * The asset must not be compressed, or an exception will be thrown. - * - * @param fileName Name of the asset to retrieve. - */ - public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName) + public final AssetFileDescriptor openNonAssetFd(String fileName) throws IOException { return openNonAssetFd(0, fileName); } - - /** - * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. - * This provides direct access to all of the files included in an application - * package (not only its assets). Applications should not normally use this. - * - * The asset must not be compressed, or an exception will be thrown. - * - * @param cookie Identifier of the package to be opened. - * @param fileName Name of the asset to retrieve. - */ - public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName) - throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + + public final AssetFileDescriptor openNonAssetFd(int cookie, + String fileName) throws IOException { synchronized (this) { - ensureOpenLocked(); - final ParcelFileDescriptor pfd = - nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); - if (pfd == null) { - throw new FileNotFoundException("Asset absolute file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + ParcelFileDescriptor pfd = openNonAssetFdNative(cookie, + fileName, mOffsets); + if (pfd != null) { + return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } - return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } + throw new FileNotFoundException("Asset absolute file: " + fileName); } /** @@ -789,7 +497,7 @@ public final class AssetManager implements AutoCloseable { * * @param fileName The name of the file to retrieve. */ - public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName) + public final XmlResourceParser openXmlResourceParser(String fileName) throws IOException { return openXmlResourceParser(0, fileName); } @@ -800,265 +508,270 @@ public final class AssetManager implements AutoCloseable { * @param cookie Identifier of the package to be opened. * @param fileName The name of the file to retrieve. */ - public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName) - throws IOException { - try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) { - XmlResourceParser parser = block.newParser(); - // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with - // a valid native pointer, which makes newParser always return non-null. But let's - // be paranoid. - if (parser == null) { - throw new AssertionError("block.newParser() returned a null parser"); - } - return parser; - } + public final XmlResourceParser openXmlResourceParser(int cookie, + String fileName) throws IOException { + XmlBlock block = openXmlBlockAsset(cookie, fileName); + XmlResourceParser rp = block.newParser(); + block.close(); + return rp; } /** - * Retrieve a non-asset as a compiled XML file. Not for use by applications. + * {@hide} + * Retrieve a non-asset as a compiled XML file. Not for use by + * applications. * * @param fileName The name of the file to retrieve. - * @hide */ - @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException { + /*package*/ final XmlBlock openXmlBlockAsset(String fileName) + throws IOException { return openXmlBlockAsset(0, fileName); } /** + * {@hide} * Retrieve a non-asset as a compiled XML file. Not for use by * applications. * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. - * @hide */ - @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); + /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) + throws IOException { synchronized (this) { - ensureOpenLocked(); - final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); - if (xmlBlock == 0) { - throw new FileNotFoundException("Asset XML file: " + fileName); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + long xmlBlock = openXmlAssetNative(cookie, fileName); + if (xmlBlock != 0) { + XmlBlock res = new XmlBlock(this, xmlBlock); + incRefsLocked(res.hashCode()); + return res; } - final XmlBlock block = new XmlBlock(this, xmlBlock); - incRefsLocked(block.hashCode()); - return block; } + throw new FileNotFoundException("Asset XML file: " + fileName); } - void xmlBlockGone(int id) { + /*package*/ void xmlBlockGone(int id) { synchronized (this) { decRefsLocked(id); } } - void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, - @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, - long outIndicesAddress) { - Preconditions.checkNotNull(inAttrs, "inAttrs"); - synchronized (this) { - // Need to synchronize on AssetManager because we will be accessing - // the native implementation of AssetManager. - ensureValidLocked(); - nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress, - outIndicesAddress); - } - } - - boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, - @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, - @NonNull int[] outIndices) { - Preconditions.checkNotNull(inAttrs, "inAttrs"); - Preconditions.checkNotNull(outValues, "outValues"); - Preconditions.checkNotNull(outIndices, "outIndices"); - synchronized (this) { - // Need to synchronize on AssetManager because we will be accessing - // the native implementation of AssetManager. - ensureValidLocked(); - return nativeResolveAttrs(mObject, - themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices); - } - } - - boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs, - @NonNull int[] outValues, @NonNull int[] outIndices) { - Preconditions.checkNotNull(parser, "parser"); - Preconditions.checkNotNull(inAttrs, "inAttrs"); - Preconditions.checkNotNull(outValues, "outValues"); - Preconditions.checkNotNull(outIndices, "outIndices"); - synchronized (this) { - // Need to synchronize on AssetManager because we will be accessing - // the native implementation of AssetManager. - ensureValidLocked(); - return nativeRetrieveAttributes( - mObject, parser.mParseState, inAttrs, outValues, outIndices); - } - } - - long createTheme() { - synchronized (this) { - ensureValidLocked(); - long themePtr = nativeThemeCreate(mObject); - incRefsLocked(themePtr); - return themePtr; - } - } - - void releaseTheme(long themePtr) { + /*package*/ final long createTheme() { synchronized (this) { - nativeThemeDestroy(themePtr); - decRefsLocked(themePtr); + if (!mOpen) { + throw new RuntimeException("Assetmanager has been closed"); + } + long res = newTheme(); + incRefsLocked(res); + return res; } } - void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) { + /*package*/ final void releaseTheme(long theme) { synchronized (this) { - // Need to synchronize on AssetManager because we will be accessing - // the native implementation of AssetManager. - ensureValidLocked(); - nativeThemeApplyStyle(mObject, themePtr, resId, force); + deleteTheme(theme); + decRefsLocked(theme); } } - @Override protected void finalize() throws Throwable { - if (DEBUG_REFS && mNumRefs != 0) { - Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs); - if (mRefStacks != null) { - for (RuntimeException e : mRefStacks.values()) { - Log.w(TAG, "Reference from here", e); + try { + if (DEBUG_REFS && mNumRefs != 0) { + Log.w(TAG, "AssetManager " + this + + " finalized with non-zero refs: " + mNumRefs); + if (mRefStacks != null) { + for (RuntimeException e : mRefStacks.values()) { + Log.w(TAG, "Reference from here", e); + } } } - } - - if (mObject != 0) { - nativeDestroy(mObject); + destroy(); + } finally { + super.finalize(); } } - - /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread - safe and it does not rely on AssetManager once it has been created. It completely owns the - underlying Asset. */ + public final class AssetInputStream extends InputStream { - private long mAssetNativePtr; - private long mLength; - private long mMarkPos; - /** * @hide */ public final int getAssetInt() { throw new UnsupportedOperationException(); } - /** * @hide */ public final long getNativeAsset() { - return mAssetNativePtr; + return mAsset; } - - private AssetInputStream(long assetNativePtr) { - mAssetNativePtr = assetNativePtr; - mLength = nativeAssetGetLength(assetNativePtr); + private AssetInputStream(long asset) + { + mAsset = asset; + mLength = getAssetLength(asset); } - - @Override public final int read() throws IOException { - ensureOpen(); - return nativeAssetReadChar(mAssetNativePtr); + return readAssetChar(mAsset); } - - @Override - public final int read(@NonNull byte[] b) throws IOException { - ensureOpen(); - Preconditions.checkNotNull(b, "b"); - return nativeAssetRead(mAssetNativePtr, b, 0, b.length); + public final boolean markSupported() { + return true; } - - @Override - public final int read(@NonNull byte[] b, int off, int len) throws IOException { - ensureOpen(); - Preconditions.checkNotNull(b, "b"); - return nativeAssetRead(mAssetNativePtr, b, off, len); + public final int available() throws IOException { + long len = getAssetRemainingLength(mAsset); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + } + public final void close() throws IOException { + synchronized (AssetManager.this) { + if (mAsset != 0) { + destroyAsset(mAsset); + mAsset = 0; + decRefsLocked(hashCode()); + } + } + } + public final void mark(int readlimit) { + mMarkPos = seekAsset(mAsset, 0, 0); + } + public final void reset() throws IOException { + seekAsset(mAsset, mMarkPos, -1); + } + public final int read(byte[] b) throws IOException { + return readAsset(mAsset, b, 0, b.length); + } + public final int read(byte[] b, int off, int len) throws IOException { + return readAsset(mAsset, b, off, len); } - - @Override public final long skip(long n) throws IOException { - ensureOpen(); - long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); - if ((pos + n) > mLength) { - n = mLength - pos; + long pos = seekAsset(mAsset, 0, 0); + if ((pos+n) > mLength) { + n = mLength-pos; } if (n > 0) { - nativeAssetSeek(mAssetNativePtr, n, 0); + seekAsset(mAsset, n, 0); } return n; } - @Override - public final int available() throws IOException { - ensureOpen(); - final long len = nativeAssetGetRemainingLength(mAssetNativePtr); - return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; + protected void finalize() throws Throwable + { + close(); } - @Override - public final boolean markSupported() { - return true; - } + private long mAsset; + private long mLength; + private long mMarkPos; + } - @Override - public final void mark(int readlimit) { - ensureOpen(); - mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); + /** + * Add an additional set of assets to the asset manager. This can be + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPath(String path) { + return addAssetPathInternal(path, false); + } + + /** + * Add an application assets to the asset manager and loading it as shared library. + * This can be either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, true); + } + + private final int addAssetPathInternal(String path, boolean appAsLib) { + synchronized (this) { + int res = addAssetPathNative(path, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void reset() throws IOException { - ensureOpen(); - nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); + private native final int addAssetPathNative(String path, boolean appAsLib); + + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void close() throws IOException { - if (mAssetNativePtr != 0) { - nativeAssetDestroy(mAssetNativePtr); - mAssetNativePtr = 0; + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); - synchronized (AssetManager.this) { - decRefsLocked(hashCode()); - } - } + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + + public final int addOverlayPath(String idmapPath) { + synchronized (this) { + int res = addOverlayPathNative(idmapPath); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - protected void finalize() throws Throwable { - close(); + /** + * See addOverlayPath. + * + * {@hide} + */ + public native final int addOverlayPathNative(String idmapPath); + + /** + * Add multiple sets of assets to the asset manager at once. See + * {@link #addAssetPath(String)} for more information. Returns array of + * cookies for each added asset with 0 indicating failure, or null if + * the input array of paths is null. + * {@hide} + */ + public final int[] addAssetPaths(String[] paths) { + if (paths == null) { + return null; } - private void ensureOpen() { - if (mAssetNativePtr == 0) { - throw new IllegalStateException("AssetInputStream is closed"); - } + int[] cookies = new int[paths.length]; + for (int i = 0; i < paths.length; i++) { + cookies[i] = addAssetPath(paths[i]); } + + return cookies; } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * @hide + * {@hide} */ - public boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { - return false; - } - } - return true; - } + public native final boolean isUpToDate(); /** * Get the locales that this asset manager contains data for. @@ -1071,12 +784,7 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public String[] getLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, false /*excludeSystem*/); - } - } + public native final String[] getLocales(); /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -1086,57 +794,131 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * @hide + * {@hide} */ - public String[] getNonSystemLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, true /*excludeSystem*/); - } - } + public native final String[] getNonSystemLocales(); - /** - * @hide - */ - Configuration[] getSizeConfigurations() { - synchronized (this) { - ensureValidLocked(); - return nativeGetSizeConfigurations(mObject); - } - } + /** {@hide} */ + public native final Configuration[] getSizeConfigurations(); /** - * Change the configuration used when retrieving resources. Not for use by + * Change the configuation used when retrieving resources. Not for use by * applications. - * @hide + * {@hide} */ - public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, - int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, - int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, - int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { - synchronized (this) { - ensureValidLocked(); - nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, - keyboard, keyboardHidden, navigation, screenWidth, screenHeight, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, - colorMode, majorVersion); - } - } + public native final void setConfiguration(int mcc, int mnc, String locale, + int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, + int screenLayout, int uiMode, int colorMode, int majorVersion); /** - * @hide + * Retrieve the resource identifier for the given resource name. */ - public SparseArray<String> getAssignedPackageIdentifiers() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssignedPackageIdentifiers(mObject); - } - } + /*package*/ native final int getResourceIdentifier(String name, + String defType, + String defPackage); - private void incRefsLocked(long id) { + /*package*/ native final String getResourceName(int resid); + /*package*/ native final String getResourcePackageName(int resid); + /*package*/ native final String getResourceTypeName(int resid); + /*package*/ native final String getResourceEntryName(int resid); + + private native final long openAsset(String fileName, int accessMode); + private final native ParcelFileDescriptor openAssetFd(String fileName, + long[] outOffsets) throws IOException; + private native final long openNonAssetNative(int cookie, String fileName, + int accessMode); + private native ParcelFileDescriptor openNonAssetFdNative(int cookie, + String fileName, long[] outOffsets) throws IOException; + private native final void destroyAsset(long asset); + private native final int readAssetChar(long asset); + private native final int readAsset(long asset, byte[] b, int off, int len); + private native final long seekAsset(long asset, long offset, int whence); + private native final long getAssetLength(long asset); + private native final long getAssetRemainingLength(long asset); + + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceValue(int ident, short density, TypedValue outValue, + boolean resolve); + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, + boolean resolve); + /*package*/ static final int STYLE_NUM_ENTRIES = 6; + /*package*/ static final int STYLE_TYPE = 0; + /*package*/ static final int STYLE_DATA = 1; + /*package*/ static final int STYLE_ASSET_COOKIE = 2; + /*package*/ static final int STYLE_RESOURCE_ID = 3; + + /* Offset within typed data array for native changingConfigurations. */ + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + + /*package*/ static final int STYLE_DENSITY = 5; + /*package*/ native static final void applyStyle(long theme, + int defStyleAttr, int defStyleRes, long xmlParser, + int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); + /*package*/ native static final boolean resolveAttrs(long theme, + int defStyleAttr, int defStyleRes, int[] inValues, + int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final boolean retrieveAttributes( + long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final int getArraySize(int resource); + /*package*/ native final int retrieveArray(int resource, int[] outValues); + private native final int getStringBlockCount(); + private native final long getNativeStringBlock(int block); + + /** + * {@hide} + */ + public native final String getCookieName(int cookie); + + /** + * {@hide} + */ + public native final SparseArray<String> getAssignedPackageIdentifiers(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetCount(); + + /** + * {@hide} + */ + public native static final String getAssetAllocations(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetManagerCount(); + + private native final long newTheme(); + private native final void deleteTheme(long theme); + /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); + /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final void clearTheme(long theme); + /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, + TypedValue outValue, + boolean resolve); + /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); + /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); + + private native final long openXmlAssetNative(int cookie, String fileName); + + private native final String[] getArrayStringResource(int arrayRes); + private native final int[] getArrayStringInfo(int arrayRes); + /*package*/ native final int[] getArrayIntResource(int arrayRes); + /*package*/ native final int[] getStyleAttributes(int themeRes); + + private native final void init(boolean isSystem); + private native final void destroy(); + + private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<>(); + mRefStacks = new HashMap<Long, RuntimeException>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -1144,117 +926,16 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private void decRefsLocked(long id) { + + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - if (mNumRefs == 0 && mObject != 0) { - nativeDestroy(mObject); - mObject = 0; + //System.out.println("Dec streams: mNumRefs=" + mNumRefs + // + " mReleased=" + mReleased); + if (mNumRefs == 0) { + destroy(); } } - - // AssetManager setup native methods. - private static native long nativeCreate(); - private static native void nativeDestroy(long ptr); - private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, - boolean invalidateCaches); - private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, - @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, - int uiMode, int colorMode, int majorVersion); - private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( - long ptr); - - // File native methods. - private static native @Nullable String[] nativeList(long ptr, @NonNull String path) - throws IOException; - private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, - @NonNull String fileName, long[] outOffsets) throws IOException; - private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, - int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, - @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; - private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); - - // Primitive resource native methods. - private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, - @NonNull TypedValue outValue, boolean resolveReferences); - private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, - @NonNull TypedValue outValue); - - private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, - @StyleRes int resId); - private static native @Nullable String[] nativeGetResourceStringArray(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, - @NonNull int[] outValues); - - // Resource name/ID native methods. - private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, - @Nullable String defType, @Nullable String defPackage); - private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourcePackageName(long ptr, - @AnyRes int resid); - private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); - private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); - private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); - - // Style attribute retrieval native methods. - private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, - @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, - long outValuesAddress, long outIndicesAddress); - private static native boolean nativeResolveAttrs(long ptr, long themePtr, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - - // Theme related native methods - private static native long nativeThemeCreate(long ptr); - private static native void nativeThemeDestroy(long themePtr); - private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, - boolean force); - static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); - static native void nativeThemeClear(long themePtr); - private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, - @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); - private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, - String prefix); - static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); - - // AssetInputStream related native methods. - private static native void nativeAssetDestroy(long assetPtr); - private static native int nativeAssetReadChar(long assetPtr); - private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); - private static native long nativeAssetSeek(long assetPtr, long offset, int whence); - private static native long nativeAssetGetLength(long assetPtr); - private static native long nativeAssetGetRemainingLength(long assetPtr); - - private static native void nativeVerifySystemIdmaps(); - - // Global debug native methods. - /** - * @hide - */ - public static native int getGlobalAssetCount(); - - /** - * @hide - */ - public static native String getAssetAllocations(); - - /** - * @hide - */ - public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 8f58891ed556..e173653cd961 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); + int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getResourceArraySize(id); + int len = impl.getAssets().getArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().getResourceArray(id, array.mData); + array.mLength = impl.getAssets().retrieveArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,7 +1789,8 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, + array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 2a4b278bdca8..97cb78bc4243 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,6 +168,7 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); + mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1273,7 +1274,8 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); + mThemeResId = resId; mKey.append(resId, force); } @@ -1282,7 +1284,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1305,10 +1307,12 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, - array.mDataAddress, array.mIndicesAddress); + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; + return array; } } @@ -1325,7 +1329,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1345,14 +1349,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.nativeThemeGetChangingConfigurations(mTheme); + AssetManager.getThemeChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - mAssets.dumpTheme(mTheme, priority, tag, prefix); + AssetManager.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1381,13 +1385,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.nativeThemeClear(mTheme); + AssetManager.clearTheme(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index cbb3c6df0558..f33c75168a5f 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,15 +61,6 @@ public class TypedArray { return attrs; } - // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; - static final int STYLE_TYPE = 0; - static final int STYLE_DATA = 1; - static final int STYLE_ASSET_COOKIE = 2; - static final int STYLE_RESOURCE_ID = 3; - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - static final int STYLE_DENSITY = 5; - private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -87,7 +78,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * STYLE_NUM_ENTRIES; + final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -175,9 +166,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -212,9 +203,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -251,13 +242,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString(data[index + STYLE_DATA]).toString(); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]).toString(); } } return null; @@ -282,11 +274,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -328,14 +320,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA] != 0; + return data[index+AssetManager.STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -367,14 +359,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -404,16 +396,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index + STYLE_DATA]); + return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -454,15 +446,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -506,7 +498,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -541,7 +533,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -572,15 +564,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -620,14 +612,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimension( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -668,14 +661,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -717,14 +711,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -760,15 +755,16 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -799,14 +795,15 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index + AssetManager.STYLE_DATA], mMetrics); } return defValue; @@ -836,14 +833,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); + return TypedValue.complexToFraction( + data[index+AssetManager.STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -876,10 +874,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index + STYLE_RESOURCE_ID]; + if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -904,10 +902,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + STYLE_DATA]; + if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + AssetManager.STYLE_DATA]; } return defValue; } @@ -941,7 +939,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -977,7 +975,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1008,7 +1006,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1029,7 +1027,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index * STYLE_NUM_ENTRIES, outValue); + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -1045,8 +1043,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; - return mData[index + STYLE_TYPE]; + index *= AssetManager.STYLE_NUM_ENTRIES; + return mData[index + AssetManager.STYLE_TYPE]; } /** @@ -1065,9 +1063,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1086,11 +1084,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1111,7 +1109,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1183,16 +1181,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + STYLE_DATA]; + final int attr = data[index + AssetManager.STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1233,44 +1231,45 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - final int type = data[index + STYLE_TYPE]; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index + STYLE_DATA]; - outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index + STYLE_RESOURCE_ID]; + outValue.data = data[index+AssetManager.STYLE_DATA]; + outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index + STYLE_DENSITY]; + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index+AssetManager.STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString(data[index + STYLE_DATA]); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index d4ccffb83ca5..e6b957414ea8 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,7 +16,6 @@ package android.content.res; -import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -34,7 +33,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock implements AutoCloseable { +final class XmlBlock { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -49,7 +48,6 @@ final class XmlBlock implements AutoCloseable { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } - @Override public void close() { synchronized (this) { if (mOpen) { @@ -480,13 +478,13 @@ final class XmlBlock implements AutoCloseable { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(@Nullable AssetManager assets, long xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private @Nullable final AssetManager mAssets; + private final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 1de8882e057a..fdea5a2f9351 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1246,7 +1246,7 @@ public final class InputManager { int repeat; if (effect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; - pattern = new long[] { 0, oneShot.getTiming() }; + pattern = new long[] { 0, oneShot.getDuration() }; repeat = -1; } else if (effect instanceof VibrationEffect.Waveform) { VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7528bc3989c2..a817f33ce7ba 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -265,6 +265,10 @@ public class InputMethodService extends AbstractInputMethodService { */ public static final int IME_VISIBLE = 0x2; + // Min and max values for back disposition. + private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; + private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_WILL_DISMISS; + InputMethodManager mImm; int mTheme = 0; @@ -501,9 +505,8 @@ public class InputMethodService extends AbstractInputMethodService { } clearInsetOfPreviousIme(); // If user uses hard keyboard, IME button should always be shown. - boolean showing = isInputViewShown(); mImm.setImeWindowStatus(mToken, mStartInputToken, - IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); + mapToImeWindowStatus(isInputViewShown()), mBackDisposition); if (resultReceiver != null) { resultReceiver.send(wasVis != isInputViewShown() ? InputMethodManager.RESULT_SHOWN @@ -1014,7 +1017,16 @@ public class InputMethodService extends AbstractInputMethodService { } public void setBackDisposition(int disposition) { + if (disposition == mBackDisposition) { + return; + } + if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) { + Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified."); + return; + } mBackDisposition = disposition; + mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()), + mBackDisposition); } public int getBackDisposition() { @@ -1762,7 +1774,7 @@ public class InputMethodService extends AbstractInputMethodService { startExtractingText(false); } - final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0); + final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown()); if (previousImeWindowStatus != nextImeWindowStatus) { mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus, mBackDisposition); @@ -1889,6 +1901,7 @@ public class InputMethodService extends AbstractInputMethodService { mInputStarted = false; mStartedInputConnection = null; mCurCompletions = null; + mBackDisposition = BACK_DISPOSITION_DEFAULT; } void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { @@ -2104,7 +2117,11 @@ public class InputMethodService extends AbstractInputMethodService { * them to perform navigation in the underlying application. */ public boolean onKeyDown(int keyCode, KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (mBackDisposition == BACK_DISPOSITION_WILL_NOT_DISMISS) { + return false; + } final ExtractEditText eet = getExtractEditTextIfVisible(); if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) { return true; @@ -2738,6 +2755,10 @@ public class InputMethodService extends AbstractInputMethodService { mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo()); } + private static int mapToImeWindowStatus(boolean isInputViewShown) { + return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0); + } + /** * Performs a dump of the InputMethodService's internal state. Override * to add your own information to the dump. diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 843bdb50dcab..a734719afa5d 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -157,11 +157,11 @@ public class BatteryManager { // values of the "plugged" field in the ACTION_BATTERY_CHANGED intent. // These must be powers of 2. /** Power source is an AC charger. */ - public static final int BATTERY_PLUGGED_AC = 1; + public static final int BATTERY_PLUGGED_AC = OsProtoEnums.BATTERY_PLUGGED_AC; // = 1 /** Power source is a USB port. */ - public static final int BATTERY_PLUGGED_USB = 2; + public static final int BATTERY_PLUGGED_USB = OsProtoEnums.BATTERY_PLUGGED_USB; // = 2 /** Power source is wireless. */ - public static final int BATTERY_PLUGGED_WIRELESS = 4; + public static final int BATTERY_PLUGGED_WIRELESS = OsProtoEnums.BATTERY_PLUGGED_WIRELESS; // = 4 /** @hide */ public static final int BATTERY_PLUGGED_ANY = diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index fc7886191898..48f56847e88d 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -894,14 +894,6 @@ public class Build { /** * P. - * - * <p>Applications targeting this or a later release will get these - * new changes in behavior:</p> - * <ul> - * <li>{@link android.app.Service#startForeground Service.startForeground} requires - * that apps hold the permission - * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li> - * </ul> */ public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version. } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 158041dd91e7..03203d0502fd 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -41,6 +41,7 @@ public class Environment { private static final String ENV_OEM_ROOT = "OEM_ROOT"; private static final String ENV_ODM_ROOT = "ODM_ROOT"; private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT"; + private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT"; /** {@hide} */ public static final String DIR_ANDROID = "Android"; @@ -62,6 +63,7 @@ public class Environment { private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm"); private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); + private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product"); private static UserEnvironment sCurrentUser; private static boolean sUserRequired; @@ -180,6 +182,16 @@ public class Environment { } /** + * Return root directory of the "product" partition holding product-specific + * customizations if any. If present, the partition is mounted read-only. + * + * @hide + */ + public static File getProductDirectory() { + return DIR_PRODUCT_ROOT; + } + + /** * Return the system directory for a user. This is for use by system * services to store files relating to the user. This directory will be * automatically deleted when the user is removed. diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java index 335bf9d86fc7..91b796aba655 100644 --- a/core/java/android/os/HidlSupport.java +++ b/core/java/android/os/HidlSupport.java @@ -212,9 +212,10 @@ public class HidlSupport { } /** - * Return PID of process if sharable to clients. + * Return PID of process only if on a non-user build. For debugging purposes. * @hide */ + @SystemApi public static native int getPidIfSharable(); /** @hide */ diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index ecac002940cc..2a088a659b0f 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -53,14 +53,30 @@ public abstract class HwBinder implements IHwBinder { public native final void registerService(String serviceName) throws RemoteException; - /** @hide */ + /** + * Returns the specified service from the hwservicemanager. Does not retry. + * + * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz + * @param serviceName the instance name of the service for example default. + * @throws NoSuchElementException when the service is unavailable + * @hide + */ + @SystemApi public static final IHwBinder getService( String iface, String serviceName) throws RemoteException, NoSuchElementException { return getService(iface, serviceName, false /* retry */); } - /** @hide */ + /** + * Returns the specified service from the hwservicemanager. + * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz + * @param serviceName the instance name of the service for example default. + * @param retry whether to wait for the service to start if it's not already started + * @throws NoSuchElementException when the service is unavailable + * @hide + */ + @SystemApi public static native final IHwBinder getService( String iface, String serviceName, @@ -108,7 +124,19 @@ public abstract class HwBinder implements IHwBinder { private static native void native_report_sysprop_change(); /** + * Enable instrumentation if available. + * @hide + */ + @SystemApi + public static void enableInstrumentation() { + native_report_sysprop_change(); + } + + /** * Notifies listeners that a system property has changed + * + * TODO(b/72480743): remove this method + * * @hide */ public static void reportSyspropChanged() { diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java index ce9f6c1654c2..0c592e1f04b8 100644 --- a/core/java/android/os/IHwBinder.java +++ b/core/java/android/os/IHwBinder.java @@ -27,12 +27,22 @@ public interface IHwBinder { /** @hide */ public static final int FLAG_ONEWAY = 1; - /** @hide */ + /** + * Process a hwbinder transaction. + * + * @hide + */ + @SystemApi public void transact( int code, HwParcel request, HwParcel reply, int flags) throws RemoteException; - /** @hide */ + /** + * Return as IHwInterface instance only if this implements descriptor. + * @param descriptor for example foo.bar@1.0::IBaz + * @hide + */ + @SystemApi public IHwInterface queryLocalInterface(String descriptor); /** diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 3d17ffb7329f..811cc5ed472c 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -110,7 +110,7 @@ public final class PowerManager { /* NOTE: Wake lock levels were previously defined as a bit field, except that only a few * combinations were actually supported so the bit field was removed. This explains * why the numbering scheme is so odd. If adding a new wake lock level, any unused - * value can be used. + * value (in frameworks/base/core/proto/android/os/enums.proto) can be used. */ /** @@ -121,7 +121,7 @@ public final class PowerManager { * but the CPU will be kept on until all partial wake locks have been released. * </p> */ - public static final int PARTIAL_WAKE_LOCK = 0x00000001; + public static final int PARTIAL_WAKE_LOCK = OsProtoEnums.PARTIAL_WAKE_LOCK; // 0x00000001 /** * Wake lock level: Ensures that the screen is on (but may be dimmed); @@ -138,7 +138,7 @@ public final class PowerManager { * as the user moves between applications and doesn't require a special permission. */ @Deprecated - public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006; + public static final int SCREEN_DIM_WAKE_LOCK = OsProtoEnums.SCREEN_DIM_WAKE_LOCK; // 0x00000006 /** * Wake lock level: Ensures that the screen is on at full brightness; @@ -155,7 +155,8 @@ public final class PowerManager { * as the user moves between applications and doesn't require a special permission. */ @Deprecated - public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a; + public static final int SCREEN_BRIGHT_WAKE_LOCK = + OsProtoEnums.SCREEN_BRIGHT_WAKE_LOCK; // 0x0000000a /** * Wake lock level: Ensures that the screen and keyboard backlight are on at @@ -172,7 +173,7 @@ public final class PowerManager { * as the user moves between applications and doesn't require a special permission. */ @Deprecated - public static final int FULL_WAKE_LOCK = 0x0000001a; + public static final int FULL_WAKE_LOCK = OsProtoEnums.FULL_WAKE_LOCK; // 0x0000001a /** * Wake lock level: Turns the screen off when the proximity sensor activates. @@ -193,7 +194,8 @@ public final class PowerManager { * Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}. * </p> */ - public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020; + public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = + OsProtoEnums.PROXIMITY_SCREEN_OFF_WAKE_LOCK; // 0x00000020 /** * Wake lock level: Put the screen in a low power state and allow the CPU to suspend @@ -207,7 +209,7 @@ public final class PowerManager { * * {@hide} */ - public static final int DOZE_WAKE_LOCK = 0x00000040; + public static final int DOZE_WAKE_LOCK = OsProtoEnums.DOZE_WAKE_LOCK; // 0x00000040 /** * Wake lock level: Keep the device awake enough to allow drawing to occur. @@ -221,7 +223,7 @@ public final class PowerManager { * * {@hide} */ - public static final int DRAW_WAKE_LOCK = 0x00000080; + public static final int DRAW_WAKE_LOCK = OsProtoEnums.DRAW_WAKE_LOCK; // 0x00000080 /** * Mask for the wake lock level component of a combined wake lock level and flags integer. diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 13b5b5c90c2c..b2b43cfd6ddc 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2259,12 +2259,6 @@ public class UserManager { } } - /** @removed */ - @Deprecated - public boolean trySetQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) { - return requestQuietModeEnabled(enableQuietMode, userHandle); - } - /** * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a * managed profile don't run, generate notifications, or consume data or battery. diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index da0ed54e003e..b6f16a7b9ff8 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -16,7 +16,9 @@ package android.os; +import android.hardware.vibrator.V1_0.Constants.EffectStrength; import android.hardware.vibrator.V1_1.Constants.Effect_1_1; +import android.util.MathUtils; import java.util.Arrays; @@ -36,6 +38,12 @@ public abstract class VibrationEffect implements Parcelable { public static final int DEFAULT_AMPLITUDE = -1; /** + * The maximum amplitude value + * @hide + */ + public static final int MAX_AMPLITUDE = 255; + + /** * A click effect. * * @see #get(int) @@ -198,38 +206,75 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ public abstract void validate(); + /** + * Gets the estimated duration of the vibration in milliseconds. + * + * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this + * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where + * the length is device and potentially run-time dependent), this returns -1. + * + * @hide + */ + public abstract long getDuration(); + + /** + * Scale the amplitude with the given constraints. + * + * This assumes that the previous value was in the range [0, MAX_AMPLITUDE] + * @hide + */ + protected static int scale(int amplitude, float gamma, int maxAmplitude) { + float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma); + return (int) (val * maxAmplitude); + } + /** @hide */ public static class OneShot extends VibrationEffect implements Parcelable { - private long mTiming; - private int mAmplitude; + private final long mDuration; + private final int mAmplitude; public OneShot(Parcel in) { - this(in.readLong(), in.readInt()); + mDuration = in.readLong(); + mAmplitude = in.readInt(); } public OneShot(long milliseconds, int amplitude) { - mTiming = milliseconds; + mDuration = milliseconds; mAmplitude = amplitude; } - public long getTiming() { - return mTiming; + @Override + public long getDuration() { + return mDuration; } public int getAmplitude() { return mAmplitude; } + /** + * Scale the amplitude of this effect. + * + * @param gamma the gamma adjustment to apply + * @param maxAmplitude the new maximum amplitude of the effect + * + * @return A {@link OneShot} effect with the same timing but scaled amplitude. + */ + public VibrationEffect scale(float gamma, int maxAmplitude) { + int newAmplitude = scale(mAmplitude, gamma, maxAmplitude); + return new OneShot(mDuration, newAmplitude); + } + @Override public void validate() { if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) { throw new IllegalArgumentException( - "amplitude must either be DEFAULT_AMPLITUDE, " + - "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")"); + "amplitude must either be DEFAULT_AMPLITUDE, " + + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")"); } - if (mTiming <= 0) { + if (mDuration <= 0) { throw new IllegalArgumentException( - "timing must be positive (timing=" + mTiming + ")"); + "duration must be positive (duration=" + mDuration + ")"); } } @@ -239,26 +284,26 @@ public abstract class VibrationEffect implements Parcelable { return false; } VibrationEffect.OneShot other = (VibrationEffect.OneShot) o; - return other.mTiming == mTiming && other.mAmplitude == mAmplitude; + return other.mDuration == mDuration && other.mAmplitude == mAmplitude; } @Override public int hashCode() { int result = 17; - result = 37 * (int) mTiming; - result = 37 * mAmplitude; + result += 37 * (int) mDuration; + result += 37 * mAmplitude; return result; } @Override public String toString() { - return "OneShot{mTiming=" + mTiming +", mAmplitude=" + mAmplitude + "}"; + return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}"; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_ONE_SHOT); - out.writeLong(mTiming); + out.writeLong(mDuration); out.writeInt(mAmplitude); } @@ -279,9 +324,9 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ public static class Waveform extends VibrationEffect implements Parcelable { - private long[] mTimings; - private int[] mAmplitudes; - private int mRepeat; + private final long[] mTimings; + private final int[] mAmplitudes; + private final int mRepeat; public Waveform(Parcel in) { this(in.createLongArray(), in.createIntArray(), in.readInt()); @@ -308,34 +353,68 @@ public abstract class VibrationEffect implements Parcelable { } @Override + public long getDuration() { + if (mRepeat >= 0) { + return Long.MAX_VALUE; + } + long duration = 0; + for (long d : mTimings) { + duration += d; + } + return duration; + } + + /** + * Scale the Waveform with the given gamma and new max amplitude. + * + * @param gamma the gamma adjustment to apply + * @param maxAmplitude the new maximum amplitude of the effect + * + * @return A {@link Waveform} effect with the same timings and repeat index + * but scaled amplitude. + */ + public VibrationEffect scale(float gamma, int maxAmplitude) { + if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) { + // Just return a copy of the original if there's no scaling to be done. + return new Waveform(mTimings, mAmplitudes, mRepeat); + } + + int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length); + for (int i = 0; i < scaledAmplitudes.length; i++) { + scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude); + } + return new Waveform(mTimings, scaledAmplitudes, mRepeat); + } + + @Override public void validate() { if (mTimings.length != mAmplitudes.length) { throw new IllegalArgumentException( - "timing and amplitude arrays must be of equal length" + - " (timings.length=" + mTimings.length + - ", amplitudes.length=" + mAmplitudes.length + ")"); + "timing and amplitude arrays must be of equal length" + + " (timings.length=" + mTimings.length + + ", amplitudes.length=" + mAmplitudes.length + ")"); } if (!hasNonZeroEntry(mTimings)) { - throw new IllegalArgumentException("at least one timing must be non-zero" + - " (timings=" + Arrays.toString(mTimings) + ")"); + throw new IllegalArgumentException("at least one timing must be non-zero" + + " (timings=" + Arrays.toString(mTimings) + ")"); } for (long timing : mTimings) { if (timing < 0) { - throw new IllegalArgumentException("timings must all be >= 0" + - " (timings=" + Arrays.toString(mTimings) + ")"); + throw new IllegalArgumentException("timings must all be >= 0" + + " (timings=" + Arrays.toString(mTimings) + ")"); } } for (int amplitude : mAmplitudes) { if (amplitude < -1 || amplitude > 255) { throw new IllegalArgumentException( - "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" + - " (amplitudes=" + Arrays.toString(mAmplitudes) + ")"); + "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" + + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")"); } } if (mRepeat < -1 || mRepeat >= mTimings.length) { throw new IllegalArgumentException( - "repeat index must be within the bounds of the timings array" + - " (timings.length=" + mTimings.length + ", index=" + mRepeat +")"); + "repeat index must be within the bounds of the timings array" + + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")"); } } @@ -345,26 +424,26 @@ public abstract class VibrationEffect implements Parcelable { return false; } VibrationEffect.Waveform other = (VibrationEffect.Waveform) o; - return Arrays.equals(mTimings, other.mTimings) && - Arrays.equals(mAmplitudes, other.mAmplitudes) && - mRepeat == other.mRepeat; + return Arrays.equals(mTimings, other.mTimings) + && Arrays.equals(mAmplitudes, other.mAmplitudes) + && mRepeat == other.mRepeat; } @Override public int hashCode() { int result = 17; - result = 37 * Arrays.hashCode(mTimings); - result = 37 * Arrays.hashCode(mAmplitudes); - result = 37 * mRepeat; + result += 37 * Arrays.hashCode(mTimings); + result += 37 * Arrays.hashCode(mAmplitudes); + result += 37 * mRepeat; return result; } @Override public String toString() { - return "Waveform{mTimings=" + Arrays.toString(mTimings) + - ", mAmplitudes=" + Arrays.toString(mAmplitudes) + - ", mRepeat=" + mRepeat + - "}"; + return "Waveform{mTimings=" + Arrays.toString(mTimings) + + ", mAmplitudes=" + Arrays.toString(mAmplitudes) + + ", mRepeat=" + mRepeat + + "}"; } @Override @@ -402,16 +481,20 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ public static class Prebaked extends VibrationEffect implements Parcelable { - private int mEffectId; - private boolean mFallback; + private final int mEffectId; + private final boolean mFallback; + + private int mEffectStrength; public Prebaked(Parcel in) { this(in.readInt(), in.readByte() != 0); + mEffectStrength = in.readInt(); } public Prebaked(int effectId, boolean fallback) { mEffectId = effectId; mFallback = fallback; + mEffectStrength = EffectStrength.MEDIUM; } public int getId() { @@ -427,6 +510,39 @@ public abstract class VibrationEffect implements Parcelable { } @Override + public long getDuration() { + return -1; + } + + /** + * Set the effect strength of the prebaked effect. + */ + public void setEffectStrength(int strength) { + if (!isValidEffectStrength(strength)) { + throw new IllegalArgumentException("Invalid effect strength: " + strength); + } + mEffectStrength = strength; + } + + /** + * Set the effect strength. + */ + public int getEffectStrength() { + return mEffectStrength; + } + + private static boolean isValidEffectStrength(int strength) { + switch (strength) { + case EffectStrength.LIGHT: + case EffectStrength.MEDIUM: + case EffectStrength.STRONG: + return true; + default: + return false; + } + } + + @Override public void validate() { switch (mEffectId) { case EFFECT_CLICK: @@ -437,6 +553,10 @@ public abstract class VibrationEffect implements Parcelable { throw new IllegalArgumentException( "Unknown prebaked effect type (value=" + mEffectId + ")"); } + if (!isValidEffectStrength(mEffectStrength)) { + throw new IllegalArgumentException( + "Unknown prebaked effect strength (value=" + mEffectStrength + ")"); + } } @Override @@ -445,17 +565,25 @@ public abstract class VibrationEffect implements Parcelable { return false; } VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o; - return mEffectId == other.mEffectId && mFallback == other.mFallback; + return mEffectId == other.mEffectId + && mFallback == other.mFallback + && mEffectStrength == other.mEffectStrength; } @Override public int hashCode() { - return mEffectId; + int result = 17; + result += 37 * mEffectId; + result += 37 * mEffectStrength; + return result; } @Override public String toString() { - return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}"; + return "Prebaked{mEffectId=" + mEffectId + + ", mEffectStrength=" + mEffectStrength + + ", mFallback=" + mFallback + + "}"; } @@ -464,6 +592,7 @@ public abstract class VibrationEffect implements Parcelable { out.writeInt(PARCEL_TOKEN_EFFECT); out.writeInt(mEffectId); out.writeByte((byte) (mFallback ? 1 : 0)); + out.writeInt(mEffectStrength); } public static final Parcelable.Creator<Prebaked> CREATOR = diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 8078fb87eb27..f1f6f414eba8 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.app.ActivityThread; @@ -23,6 +24,9 @@ import android.content.Context; import android.media.AudioAttributes; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Class that operates the vibrator on the device. * <p> @@ -33,6 +37,40 @@ import android.util.Log; public abstract class Vibrator { private static final String TAG = "Vibrator"; + /** + * Vibration intensity: no vibrations. + * @hide + */ + public static final int VIBRATION_INTENSITY_OFF = 0; + + /** + * Vibration intensity: low. + * @hide + */ + public static final int VIBRATION_INTENSITY_LOW = 1; + + /** + * Vibration intensity: medium. + * @hide + */ + public static final int VIBRATION_INTENSITY_MEDIUM = 2; + + /** + * Vibration intensity: high. + * @hide + */ + public static final int VIBRATION_INTENSITY_HIGH = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = { + VIBRATION_INTENSITY_OFF, + VIBRATION_INTENSITY_LOW, + VIBRATION_INTENSITY_MEDIUM, + VIBRATION_INTENSITY_HIGH + }) + public @interface VibrationIntensity{} + private final String mPackageName; /** @@ -50,6 +88,22 @@ public abstract class Vibrator { } /** + * Get the default vibration intensity for haptic feedback. + * @hide + */ + public int getDefaultHapticFeedbackIntensity() { + return VIBRATION_INTENSITY_MEDIUM; + } + + /** + * Get the default vibration intensity for notifications and ringtones. + * @hide + */ + public int getDefaultNotificationVibrationIntensity() { + return VIBRATION_INTENSITY_HIGH; + } + + /** * Check whether the hardware has a vibrator. * * @return True if the hardware has a vibrator, else false. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e957842dc262..2440b489f416 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3175,6 +3175,43 @@ public final class Settings { private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR; /** + * The intensity of notification vibrations, if configurable. + * + * Not all devices are capable of changing their vibration intensity; on these devices + * there will likely be no difference between the various vibration intensities except for + * intensity 0 (off) and the rest. + * + * <b>Values:</b><br/> + * 0 - Vibration is disabled<br/> + * 1 - Weak vibrations<br/> + * 2 - Medium vibrations<br/> + * 3 - Strong vibrations + * @hide + */ + public static final String NOTIFICATION_VIBRATION_INTENSITY = + "notification_vibration_intensity"; + + /** + * The intensity of haptic feedback vibrations, if configurable. + * + * Not all devices are capable of changing their feedback intensity; on these devices + * there will likely be no difference between the various vibration intensities except for + * intensity 0 (off) and the rest. + * + * <b>Values:</b><br/> + * 0 - Vibration is disabled<br/> + * 1 - Weak vibrations<br/> + * 2 - Medium vibrations<br/> + * 3 - Strong vibrations + * @hide + */ + public static final String HAPTIC_FEEDBACK_INTENSITY = + "haptic_feedback_intensity"; + + private static final Validator VIBRATION_INTENSITY_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); + + /** * Ringer volume. This is used internally, changing this value will not * change the volume. See AudioManager. * @@ -3995,7 +4032,9 @@ public final class Settings { LOCK_TO_APP_ENABLED, NOTIFICATION_SOUND, ACCELEROMETER_ROTATION, - SHOW_BATTERY_PERCENT + SHOW_BATTERY_PERCENT, + NOTIFICATION_VIBRATION_INTENSITY, + HAPTIC_FEEDBACK_INTENSITY, }; /** @@ -4136,6 +4175,8 @@ public final class Settings { VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR); VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR); VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR); + VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); + VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR); VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR); VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR); @@ -10462,6 +10503,15 @@ public final class Settings { = "forced_app_standby_for_small_battery_enabled"; /** + * Whether or not to enable Time Only Mode for watch type devices. + * Type: int (0 for false, 1 for true) + * Default: 0 + * @hide + */ + public static final String TIME_ONLY_MODE_ENABLED + = "time_only_mode_enabled"; + + /** * Whether or not Network Watchlist feature is enabled. * Type: int (0 for false, 1 for true) * Default: 0 diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java new file mode 100644 index 000000000000..f0b9fa2de5d2 --- /dev/null +++ b/core/java/android/se/omapi/Channel.java @@ -0,0 +1,251 @@ +/* + * 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. + */ +/* + * Copyright (c) 2015-2017, The Linux Foundation. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * Instances of this class represent an ISO/IEC 7816-4 channel opened to a + * Secure Element. It can be either a logical channel or the basic channel. They + * can be used to send APDUs to the secure element. Channels are opened by + * calling the Session.openBasicChannel(byte[]) or + * Session.openLogicalChannel(byte[]) methods. + * + * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a> + */ +public class Channel { + + private static final String TAG = "OMAPI.Channel"; + private Session mSession; + private final ISecureElementChannel mChannel; + private final SEService mService; + private final Object mLock = new Object(); + + Channel(SEService service, Session session, ISecureElementChannel channel) { + if (service == null || session == null || channel == null) { + throw new IllegalArgumentException("Parameters cannot be null"); + } + mService = service; + mSession = session; + mChannel = channel; + } + + /** + * Closes this channel to the Secure Element. If the method is called when + * the channel is already closed, this method will be ignored. The close() + * method shall wait for completion of any pending transmit(byte[] command) + * before closing the channel. + */ + public void close() { + if (!isClosed()) { + synchronized (mLock) { + try { + mChannel.close(); + } catch (Exception e) { + Log.e(TAG, "Error closing channel", e); + } + } + } + } + + /** + * Tells if this channel is closed. + * + * @return <code>true</code> if the channel is closed or in case of an error. + * <code>false</code> otherwise. + */ + public boolean isClosed() { + if (!mService.isConnected()) { + Log.e(TAG, "service not connected to system"); + return true; + } + try { + return mChannel.isClosed(); + } catch (RemoteException e) { + Log.e(TAG, "Exception in isClosed()"); + return true; + } + } + + /** + * Returns a boolean telling if this channel is the basic channel. + * + * @return <code>true</code> if this channel is a basic channel. <code>false</code> if + * this channel is a logical channel. + */ + public boolean isBasicChannel() { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + try { + return mChannel.isBasicChannel(); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + /** + * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The + * underlying layers generate as many TPDUs as necessary to transport this APDU. The + * API shall ensure that all available data returned from Secure Element, including + * concatenated responses, are retrieved and made available to the calling application. If a + * warning status code is received the API wont check for further response data but will + * return all data received so far and the warning status code.<br> + * The transport part is invisible from the application. The generated response is the + * response of the APDU which means that all protocols related responses are handled + * inside the API or the underlying implementation.<br> + * The transmit method shall support extended length APDU commands independently of + * the coding within the ATR.<br> + * For status word '61 XX' the API or underlying implementation shall issue a GET + * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status + * word '6C XX', the API or underlying implementation shall reissue the input command + * with LE=XX. For other status words, the API (or underlying implementation) shall return + * the complete response including data and status word to the device application. The API + * (or underlying implementation) shall not handle internally the received status words. The + * channel shall not be closed even if the Secure Element answered with an error code. + * The system ensures the synchronization between all the concurrent calls to this method, + * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that + * might be required to transport it to the SE. The entire APDU communication to this SE is + * locked to the APDU.<br> + * The channel information in the class byte in the APDU will be ignored. The system will + * add any required information to ensure the APDU is transported on this channel. + * The only restrictions on the set of commands that can be sent is defined below, the API + * implementation shall be able to send all other commands: <br> + * <ul> + * <li>MANAGE_CHANNEL commands are not allowed.</li> + * <li>SELECT by DF Name (p1=04) are not allowed.</li> + * <li>CLA bytes with channel numbers are de-masked.</li> + * </ul> + * + * @param command the APDU command to be transmitted, as a byte array. + * + * @return the response received, as a byte array. The returned byte array contains the data + * bytes in the following order: + * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>] + * + * @throws IOException if there is a communication problem to the reader or the Secure Element. + * @throws IllegalStateException if the channel is used after being closed. + * @throws IllegalArgumentException if the command byte array is less than 4 bytes long. + * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array. + * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff). + * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x). + * @throws SecurityException if the command is filtered by the security policy. + * @throws NullPointerException if command is NULL. + */ + public @NonNull byte[] transmit(byte[] command) throws IOException { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + synchronized (mLock) { + try { + byte[] response = mChannel.transmit(command); + if (response == null) { + throw new IOException("Error in communicating with Secure Element"); + } + return response; + } catch (RemoteException e) { + throw new IOException(e.getMessage()); + } + } + } + + /** + * Get the session that has opened this channel. + * + * @return the session object this channel is bound to. + */ + public @NonNull Session getSession() { + return mSession; + } + + /** + * Returns the data as received from the application select command inclusively the status word + * received at applet selection. + * The returned byte array contains the data bytes in the following order: + * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>] + * @return The data as returned by the application select command inclusively the status word. + * Only the status word if the application select command has no returned data. + * Returns null if an application select command has not been performed or the selection + * response can not be retrieved by the reader implementation. + */ + public @Nullable byte[] getSelectResponse() { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + + byte[] response; + try { + response = mChannel.getSelectResponse(); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); + } + + if (response != null && response.length == 0) { + response = null; + } + return response; + } + + /** + * Performs a selection of the next Applet on this channel that matches to the partial AID + * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method. + * This mechanism can be used by a device application to iterate through all Applets + * matching to the same partial AID. + * If selectNext() returns true a new Applet was successfully selected on this channel. + * If no further Applet exists with matches to the partial AID this method returns false + * and the already selected Applet stays selected. <br> + * + * Since the API cannot distinguish between a partial and full AID the API shall rely on the + * response of the Secure Element for the return value of this method. <br> + * The implementation of the underlying SELECT command within this method shall use + * the same values as the corresponding openBasicChannel(byte[] aid) or + * openLogicalChannel(byte[] aid) command with the option: <br> + * P2='02' (Next occurrence) <br> + * The select response stored in the Channel object shall be updated with the APDU + * response of the SELECT command. + + * @return <code>true</code> if new Applet was selected on this channel. + <code>false</code> he already selected Applet stays selected on this channel. + * + * @throws IOException if there is a communication problem to the reader or the Secure Element. + * @throws IllegalStateException if the channel is used after being closed. + * @throws UnsupportedOperationException if this operation is not supported by the card. + */ + public boolean selectNext() throws IOException { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + try { + synchronized (mLock) { + return mChannel.selectNext(); + } + } catch (RemoteException e) { + throw new IOException(e.getMessage()); + } + } +} diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/core/java/android/se/omapi/ISecureElementChannel.aidl new file mode 100644 index 000000000000..4ae57ab829cb --- /dev/null +++ b/core/java/android/se/omapi/ISecureElementChannel.aidl @@ -0,0 +1,80 @@ +/* + * 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. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.se.omapi.ISecureElementSession; + +/** @hide */ +interface ISecureElementChannel { + + /** + * Closes the specified connection and frees internal resources. + * A logical channel will be closed. + */ + void close(); + + /** + * Tells if this channel is closed. + * + * @return <code>true</code> if the channel is closed, + * <code>false</code> otherwise. + */ + boolean isClosed(); + + /** + * Returns a boolean telling if this channel is the basic channel. + * + * @return <code>true</code> if this channel is a basic channel. + * <code>false</code> if this channel is a logical channel. + */ + boolean isBasicChannel(); + + /** + * Returns the data as received from the application select command + * inclusively the status word. The returned byte array contains the data + * bytes in the following order: + * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>] + */ + byte[] getSelectResponse(); + + /** + * Transmits the specified command APDU and returns the response APDU. + * MANAGE channel commands are not supported. + * Selection of applets is not supported in logical channels. + */ + byte[] transmit(in byte[] command); + + /** + * Performs a selection of the next Applet on this channel that matches to + * the partial AID specified in the openBasicChannel(byte[] aid) or + * openLogicalChannel(byte[] aid) method. This mechanism can be used by a + * device application to iterate through all Applets matching to the same + * partial AID. + * If selectNext() returns true a new Applet was successfully selected on + * this channel. + * If no further Applet exists with matches to the partial AID this method + * returns false and the already selected Applet stays selected. + * + * @return <code>true</code> if new Applet was successfully selected. + * <code>false</code> if no further Applet exists which matches the + * partial AID. + */ + boolean selectNext(); +} diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl new file mode 100644 index 000000000000..3a99d634e4d1 --- /dev/null +++ b/core/java/android/se/omapi/ISecureElementListener.aidl @@ -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. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +/** + * Interface to receive call-backs when the service is connected. + */ +interface ISecureElementListener { + /** + * Called by the framework when the service is connected. + */ + void serviceConnected(); +} diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl new file mode 100644 index 000000000000..a312c445395a --- /dev/null +++ b/core/java/android/se/omapi/ISecureElementReader.aidl @@ -0,0 +1,51 @@ +/* + * 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. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.se.omapi.ISecureElementSession; + +/** @hide */ +interface ISecureElementReader { + + /** + * Returns true if a card is present in the specified reader. + * Returns false if a card is not present in the specified reader. + */ + boolean isSecureElementPresent(); + + /** + * Connects to a secure element in this reader. <br> + * This method prepares (initialises) the Secure Element for communication + * before the Session object is returned (e.g. powers the Secure Element by + * ICC ON if its not already on). There might be multiple sessions opened at + * the same time on the same reader. The system ensures the interleaving of + * APDUs between the respective sessions. + * + * @return a Session object to be used to create Channels. + */ + ISecureElementSession openSession(); + + /** + * Close all the sessions opened on this reader. All the channels opened by + * all these sessions will be closed. + */ + void closeSessions(); + +} diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/core/java/android/se/omapi/ISecureElementService.aidl new file mode 100644 index 000000000000..4fa799e78757 --- /dev/null +++ b/core/java/android/se/omapi/ISecureElementService.aidl @@ -0,0 +1,50 @@ +/* + * 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. + */ +/* + * Copyright (c) 2015-2017, The Linux Foundation. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.se.omapi.ISecureElementReader; + +/** + * SecureElement service interface. + * @hide + */ +interface ISecureElementService { + + /** + * Returns the friendly names of available Secure Element readers. + */ + String[] getReaders(); + + /** + * Returns SecureElement Service reader object to the given name. + */ + ISecureElementReader getReader(String reader); + + /** + * Checks if the application defined by the package name is allowed to + * receive NFC transaction events for the defined AID. + */ + boolean[] isNFCEventAllowed(String reader, in byte[] aid, + in String[] packageNames); + +} diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/core/java/android/se/omapi/ISecureElementSession.aidl new file mode 100644 index 000000000000..8ea599f2e866 --- /dev/null +++ b/core/java/android/se/omapi/ISecureElementSession.aidl @@ -0,0 +1,73 @@ +/* + * 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. + */ +/* + * Copyright (c) 2015-2017, The Linux Foundation. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.se.omapi.ISecureElementChannel; +import android.se.omapi.ISecureElementReader; +import android.se.omapi.ISecureElementListener; + +/** @hide */ +interface ISecureElementSession { + + /** + * Returns the ATR of the connected card or null if the ATR is not available + */ + byte[] getAtr(); + + /** + * Close the connection with the Secure Element. This will close any + * channels opened by this application with this Secure Element. + */ + void close(); + + /** + * Close any channel opened on this session. + */ + void closeChannels(); + + + /** + * Tells if this session is closed. + * + * @return <code>true</code> if the session is closed, false otherwise. + */ + boolean isClosed(); + + /** + * Opens a connection using the basic channel of the card in the + * specified reader and returns a channel handle. Selects the specified + * applet if aid != null. + * Logical channels cannot be opened with this connection. + * Use interface method openLogicalChannel() to open a logical channel. + */ + ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, + ISecureElementListener listener); + + /** + * Opens a connection using the next free logical channel of the card in the + * specified reader. Selects the specified applet. + * Selection of other applets with this connection is not supported. + */ + ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, + ISecureElementListener listener); +} diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java new file mode 100644 index 000000000000..9f1573973be4 --- /dev/null +++ b/core/java/android/se/omapi/Reader.java @@ -0,0 +1,151 @@ +/* + * 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. + */ +/* + * Copyright (c) 2015-2017, The Linux Foundation. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.annotation.NonNull; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * Instances of this class represent Secure Element Readers supported to this + * device. These Readers can be physical devices or virtual devices. They can be + * removable or not. They can contain Secure Element that can or cannot be + * removed. + * + * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a> + */ +public class Reader { + + private static final String TAG = "OMAPI.Reader"; + private final String mName; + private final SEService mService; + private ISecureElementReader mReader; + private final Object mLock = new Object(); + + + Reader(SEService service, String name, ISecureElementReader reader) throws + IOException { + if (reader == null || service == null || name == null) { + throw new IllegalArgumentException("Parameters cannot be null"); + } + mName = name; + mService = service; + mReader = reader; + } + + /** + * Return the name of this reader. + * <ul> + * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li> + * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li> + * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li> + * </ul> + * Slot is a decimal number without leading zeros. The Numbering must start with 1 + * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...). + * The slot number “1” for a reader is optional + * (SIM and SIM1 are both valid for the first SIM-reader, + * but if there are two readers then the second reader must be named SIM2). + * This applies also for other SD or SE readers. + * + * @return the reader name, as a String. + */ + public @NonNull String getName() { + return mName; + } + + /** + * Connects to a Secure Element in this reader. <br> + * This method prepares (initialises) the Secure Element for communication + * before the Session object is returned (e.g. powers the Secure Element by + * ICC ON if its not already on). There might be multiple sessions opened at + * the same time on the same reader. The system ensures the interleaving of + * APDUs between the respective sessions. + * + * @throws IOException if something went wrong with the communicating to the + * Secure Element or the reader. + * @return a Session object to be used to create Channels. + */ + public @NonNull Session openSession() throws IOException { + if (!mService.isConnected()) { + throw new IllegalStateException("service is not connected"); + } + + synchronized (mLock) { + ISecureElementSession session; + try { + session = mReader.openSession(); + } catch (RemoteException e) { + throw new IOException(e.getMessage()); + } + if (session == null) { + throw new IOException("service session is null."); + } + return new Session(mService, session, this); + } + } + + /** + * Check if a Secure Element is present in this reader. + * + * @throws IllegalStateException if the service is not connected + * @return <code>true</code> if the SE is present, <code>false</code> otherwise. + */ + public boolean isSecureElementPresent() { + if (!mService.isConnected()) { + throw new IllegalStateException("service is not connected"); + } + + try { + return mReader.isSecureElementPresent(); + } catch (RemoteException e) { + throw new IllegalStateException("Error in isSecureElementPresent()"); + } + } + + /** + * Return the Secure Element service this reader is bound to. + * + * @return the SEService object. + */ + public @NonNull SEService getSEService() { + return mService; + } + + /** + * Close all the sessions opened on this reader. + * All the channels opened by all these sessions will be closed. + */ + public void closeSessions() { + if (!mService.isConnected()) { + Log.e(TAG, "service is not connected"); + return; + } + synchronized (mLock) { + try { + mReader.closeSessions(); + } catch (RemoteException ignore) { } + } + } +} diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java new file mode 100644 index 000000000000..1e37277dcd6d --- /dev/null +++ b/core/java/android/se/omapi/SEService.java @@ -0,0 +1,222 @@ +/* + * 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. + */ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; + +/** + * The SEService realises the communication to available Secure Elements on the + * device. This is the entry point of this API. It is used to connect to the + * infrastructure and get access to a list of Secure Element Readers. + * + * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a> + */ +public class SEService { + + private static final String TAG = "OMAPI.SEService"; + + private final Object mLock = new Object(); + + /** The client context (e.g. activity). */ + private final Context mContext; + + /** The backend system. */ + private volatile ISecureElementService mSecureElementService; + + /** + * Class for interacting with the main interface of the backend. + */ + private ServiceConnection mConnection; + + /** + * Collection of available readers + */ + private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>(); + + /** + * Listener object that allows the notification of the caller if this + * SEService could be bound to the backend. + */ + private ISecureElementListener mSEListener; + + /** + * Establishes a new connection that can be used to connect to all the + * Secure Elements available in the system. The connection process can be + * quite long, so it happens in an asynchronous way. It is usable only if + * the specified listener is called or if isConnected() returns + * <code>true</code>. <br> + * The call-back object passed as a parameter will have its + * serviceConnected() method called when the connection actually happen. + * + * @param context + * the context of the calling application. Cannot be + * <code>null</code>. + * @param listener + * a ISecureElementListener object. Can be <code>null</code>. + */ + public SEService(Context context, ISecureElementListener listener) { + + if (context == null) { + throw new NullPointerException("context must not be null"); + } + + mContext = context; + mSEListener = listener; + + mConnection = new ServiceConnection() { + + public synchronized void onServiceConnected( + ComponentName className, IBinder service) { + + mSecureElementService = ISecureElementService.Stub.asInterface(service); + if (mSEListener != null) { + try { + mSEListener.serviceConnected(); + } catch (RemoteException ignore) { } + } + Log.i(TAG, "Service onServiceConnected"); + } + + public void onServiceDisconnected(ComponentName className) { + mSecureElementService = null; + Log.i(TAG, "Service onServiceDisconnected"); + } + }; + + Intent intent = new Intent(ISecureElementService.class.getName()); + intent.setClassName("com.android.se", + "com.android.se.SecureElementService"); + boolean bindingSuccessful = + mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + if (bindingSuccessful) { + Log.i(TAG, "bindService successful"); + } + } + + /** + * Tells whether or not the service is connected. + * + * @return <code>true</code> if the service is connected. + */ + public boolean isConnected() { + return mSecureElementService != null; + } + + /** + * Returns the list of available Secure Element readers. + * There must be no duplicated objects in the returned list. + * All available readers shall be listed even if no card is inserted. + * + * @return The readers list, as an array of Readers. If there are no + * readers the returned array is of length 0. + */ + public @NonNull Reader[] getReaders() { + if (mSecureElementService == null) { + throw new IllegalStateException("service not connected to system"); + } + String[] readerNames; + try { + readerNames = mSecureElementService.getReaders(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + Reader[] readers = new Reader[readerNames.length]; + int i = 0; + for (String readerName : readerNames) { + if (mReaders.get(readerName) == null) { + try { + mReaders.put(readerName, new Reader(this, readerName, + getReader(readerName))); + readers[i++] = mReaders.get(readerName); + } catch (Exception e) { + Log.e(TAG, "Error adding Reader: " + readerName, e); + } + } else { + readers[i++] = mReaders.get(readerName); + } + } + return readers; + } + + /** + * Releases all Secure Elements resources allocated by this SEService + * (including any binding to an underlying service). + * As a result isConnected() will return false after shutdown() was called. + * After this method call, the SEService object is not connected. + * It is recommended to call this method in the termination method of the calling application + * (or part of this application) which is bound to this SEService. + */ + public void shutdown() { + synchronized (mLock) { + if (mSecureElementService != null) { + for (Reader reader : mReaders.values()) { + try { + reader.closeSessions(); + } catch (Exception ignore) { } + } + } + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + // Do nothing and fail silently since an error here indicates + // that binding never succeeded in the first place. + } + mSecureElementService = null; + } + } + + /** + * Returns the version of the OpenMobile API specification this + * implementation is based on. + * + * @return String containing the OpenMobile API version (e.g. "3.0"). + */ + public String getVersion() { + return "3.2"; + } + + @NonNull ISecureElementListener getListener() { + return mSEListener; + } + + /** + * Obtain a Reader instance from the SecureElementService + */ + private @NonNull ISecureElementReader getReader(String name) { + try { + return mSecureElementService.getReader(name); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); + } + } +} diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java new file mode 100644 index 000000000000..bb2a0327ee0d --- /dev/null +++ b/core/java/android/se/omapi/Session.java @@ -0,0 +1,347 @@ +/* + * 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. + */ +/* + * Copyright (c) 2017, The Linux Foundation. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package android.se.omapi; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; +import java.util.NoSuchElementException; + +/** + * Instances of this class represent a connection session to one of the Secure + * Elements available on the device. These objects can be used to get a + * communication channel with an Applet in the Secure Element. + * This channel can be the basic channel or a logical channel. + * + * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a> + */ +public class Session { + + private final Object mLock = new Object(); + private final SEService mService; + private final Reader mReader; + private final ISecureElementSession mSession; + private static final String TAG = "OMAPI.Session"; + + Session(SEService service, ISecureElementSession session, Reader reader) { + if (service == null || reader == null || session == null) { + throw new IllegalArgumentException("Parameters cannot be null"); + } + mService = service; + mReader = reader; + mSession = session; + } + + /** + * Get the reader that provides this session. + * + * @return The Reader object. + */ + public @NonNull Reader getReader() { + return mReader; + } + + /** + * Get the Answer to Reset of this Secure Element. <br> + * The returned byte array can be null if the ATR for this Secure Element is + * not available. + * + * @throws IllegalStateException if there was an error connecting to SE or + * if the service was not connected. + * @return the ATR as a byte array or null. + */ + public @Nullable byte[] getATR() { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + try { + return mSession.getAtr(); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); + } + } + + /** + * Close the connection with the Secure Element. This will close any + * channels opened by this application with this Secure Element. + */ + public void close() { + if (!mService.isConnected()) { + Log.e(TAG, "service not connected to system"); + return; + } + synchronized (mLock) { + try { + mSession.close(); + } catch (RemoteException e) { + Log.e(TAG, "Error closing session", e); + } + } + } + + /** + * Tells if this session is closed. + * + * @return <code>true</code> if the session is closed, false otherwise. + */ + public boolean isClosed() { + try { + return mSession.isClosed(); + } catch (RemoteException e) { + // If there was an error here, then the session is considered close + return true; + } + } + + /** + * Close any channel opened on this session. + */ + public void closeChannels() { + if (!mService.isConnected()) { + Log.e(TAG, "service not connected to system"); + return; + } + + synchronized (mLock) { + try { + mSession.closeChannels(); + } catch (RemoteException e) { + Log.e(TAG, "Error closing channels", e); + } + } + } + + /** + * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the + * one that has number 0). The obtained object is an instance of the Channel class. + * If the AID is null, it means no Applet is to be selected on this channel and the default + * Applet is used. If the AID is defined then the corresponding Applet is selected. + * Once this channel has been opened by a device application, it is considered as "locked" + * by this device application, and other calls to this method will return null, until the + * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel + * locked (i.e. return null to applications), to prevent access to the basic channel, while + * some other might return a channel object implementing some kind of filtering on the + * commands, restricting the set of accepted command to a smaller set. + * It is recommended for the UICC to reject the opening of the basic channel to a specific + * applet, by always answering null to such a request. + * For other Secure Elements, the recommendation is to accept opening the basic channel + * on the default applet until another applet is selected on the basic channel. As there is no + * other way than a reset to select again the default applet, the implementation of the + * transport API should guarantee that the openBasicChannel(null) command will return + * null until a reset occurs. + * With previous release (V2.05) it was not possible to set P2 value, this value was always + * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is + * recommended that the device allows all values for P2, however only the following values + * are mandatory: '00', '04', '08', '0C'(as defined in [2]) + * The implementation of the underlying SELECT command within this method shall be + * based on ISO 7816-4 with following options: + * <ul> + * <li>CLA = '00'</li> + * <li>INS = 'A4'</li> + * <li>P1 = '04' (Select by DF name/application identifier)</li> + * </ul> + * + * The select response data can be retrieved with byte[] getSelectResponse(). + * The API shall handle received status word as follow. If the status word indicates that the + * Secure Element was able to open a channel (e.g. status word '90 00' or status words + * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the + * channel opened and the next getSelectResponse() shall return the received status + * word. + * Other received status codes indicating that the Secure Element was able not to open a + * channel shall be considered as an error and the corresponding channel shall not be + * opened. + * The function without P2 as parameter is provided for backwards compatibility and will + * fall back to a select command with P2='00'. + * + * @param aid the AID of the Applet to be selected on this channel, as a + * byte array, or null if no Applet is to be selected. + * @param p2 the P2 parameter of the SELECT APDU executed on this channel. + * @throws IOException if there is a communication problem to the reader or + * the Secure Element. + * @throws IllegalStateException if the Secure Element session is used after + * being closed. + * @throws IllegalArgumentException if the aid's length is not within 5 to + * 16 (inclusive). + * @throws SecurityException if the calling application cannot be granted + * access to this AID or the default Applet on this + * session. + * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be + * selected. + * @throws UnsupportedOperationException if the given P2 parameter is not + * supported by the device + * @return an instance of Channel if available or null. + */ + public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException { + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + + synchronized (mLock) { + try { + ISecureElementChannel channel = mSession.openBasicChannel(aid, p2, + mReader.getSEService().getListener()); + if (channel == null) { + return null; + } + return new Channel(mService, this, channel); + } catch (RemoteException e) { + throw new IOException(e.getMessage()); + } + } + } + + /** + * This method is provided to ease the development of mobile application and for compliancy + * with existing applications. + * This method is equivalent to openBasicChannel(aid, P2=0x00) + * + * @param aid the AID of the Applet to be selected on this channel, as a + * byte array, or null if no Applet is to be selected. + * @throws IOException if there is a communication problem to the reader or + * the Secure Element. + * @throws IllegalStateException if the Secure Element session is used after + * being closed. + * @throws IllegalArgumentException if the aid's length is not within 5 to + * 16 (inclusive). + * @throws SecurityException if the calling application cannot be granted + * access to this AID or the default Applet on this + * session. + * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be + * selected. + * @throws UnsupportedOperationException if the given P2 parameter is not + * supported by the device + * @return an instance of Channel if available or null. + */ + public @Nullable Channel openBasicChannel(byte[] aid) throws IOException { + return openBasicChannel(aid, (byte) 0x00); + } + + /** + * Open a logical channel with the Secure Element, selecting the Applet represented by + * the given AID. If the AID is null, which means no Applet is to be selected on this + * channel, the default Applet is used. It's up to the Secure Element to choose which + * logical channel will be used. + * With previous release (V2.05) it was not possible to set P2 value, this value was always + * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is + * recommended that the device allows all values for P2, however only the following values + * are mandatory: '00', '04', '08', '0C'(as defined in [2]) + * The implementation of the underlying SELECT command within this method shall be + * based on ISO 7816-4 with following options: + * + * <ul> + * <li>CLA = '01' to '03', '40 to 4F'</li> + * <li>INS = 'A4'</li> + * <li>P1 = '04' (Select by DF name/application identifier)</li> + * </ul> + * + * The select response data can be retrieved with byte[] getSelectResponse(). + * The API shall handle received status word as follow. If the status word indicates that the + * Secure Element was able to open a channel (e.g. status word '90 00' or status words + * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the + * channel opened and the next getSelectResponse() shall return the received status + * word. + * Other received status codes indicating that the Secure Element was able not to open a + * channel shall be considered as an error and the corresponding channel shall not be + * opened. + * In case of UICC it is recommended for the API to reject the opening of the logical + * channel without a specific AID, by always answering null to such a request. + * The function without P2 as parameter is provided for backwards compatibility and will + * fall back to a select command with P2=00. + * + * @param aid the AID of the Applet to be selected on this channel, as a + * byte array. + * @param p2 the P2 parameter of the SELECT APDU executed on this channel. + * @throws IOException if there is a communication problem to the reader or + * the Secure Element. + * @throws IllegalStateException if the Secure Element is used after being + * closed. + * @throws IllegalArgumentException if the aid's length is not within 5 to + * 16 (inclusive). + * @throws SecurityException if the calling application cannot be granted + * access to this AID or the default Applet on this + * session. + * @throws NoSuchElementException if the AID on the Secure Element is not + * available or cannot be selected or a logical channel is already + * open to a non-multiselectable Applet. + * @throws UnsupportedOperationException if the given P2 parameter is not + * supported by the device. + * @return an instance of Channel. Null if the Secure Element is unable to + * provide a new logical channel. + */ + public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException { + + if ((mReader.getName().startsWith("SIM")) && (aid == null)) { + Log.e(TAG, "NULL AID not supported on " + mReader.getName()); + return null; + } + + if (!mService.isConnected()) { + throw new IllegalStateException("service not connected to system"); + } + synchronized (mLock) { + try { + ISecureElementChannel channel = mSession.openLogicalChannel( + aid, + p2, + mReader.getSEService().getListener()); + if (channel == null) { + return null; + } + return new Channel(mService, this, channel); + } catch (RemoteException e) { + throw new IOException(e.getMessage()); + } + } + } + + /** + * This method is provided to ease the development of mobile application and for compliancy + * with existing applications. + * This method is equivalent to openLogicalChannel(aid, P2=0x00) + * + * @param aid the AID of the Applet to be selected on this channel, as a + * byte array. + * @throws IOException if there is a communication problem to the reader or + * the Secure Element. + * @throws IllegalStateException if the Secure Element is used after being + * closed. + * @throws IllegalArgumentException if the aid's length is not within 5 to + * 16 (inclusive). + * @throws SecurityException if the calling application cannot be granted + * access to this AID or the default Applet on this + * session. + * @throws NoSuchElementException if the AID on the Secure Element is not + * available or cannot be selected or a logical channel is already + * open to a non-multiselectable Applet. + * @throws UnsupportedOperationException if the given P2 parameter is not + * supported by the device. + * @return an instance of Channel. Null if the Secure Element is unable to + * provide a new logical channel. + */ + public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException { + return openLogicalChannel(aid, (byte) 0x00); + } +} diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index 2a352adcee18..b6c6bdc00bbe 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -18,11 +18,11 @@ package android.service.notification; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.app.AutomaticZenRule; import android.content.Context; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -161,6 +161,22 @@ public final class Condition implements Parcelable { .append(']').toString(); } + /** @hide */ + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + + // id is guarantreed not to be null. + proto.write(ConditionProto.ID, id.toString()); + proto.write(ConditionProto.SUMMARY, summary); + proto.write(ConditionProto.LINE_1, line1); + proto.write(ConditionProto.LINE_2, line2); + proto.write(ConditionProto.ICON, icon); + proto.write(ConditionProto.STATE, state); + proto.write(ConditionProto.FLAGS, flags); + + proto.end(token); + } + @SystemApi public static String stateToString(int state) { if (state == STATE_FALSE) return "STATE_FALSE"; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index f658ae03c927..bb88e1a20f73 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -33,6 +33,7 @@ import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -1262,6 +1263,30 @@ public class ZenModeConfig implements Parcelable { .append(']').toString(); } + /** @hide */ + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + + proto.write(ZenRuleProto.ID, id); + proto.write(ZenRuleProto.NAME, name); + proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime); + proto.write(ZenRuleProto.ENABLED, enabled); + proto.write(ZenRuleProto.ENABLER, enabler); + proto.write(ZenRuleProto.IS_SNOOZING, snoozing); + proto.write(ZenRuleProto.ZEN_MODE, zenMode); + if (conditionId != null) { + proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString()); + } + if (condition != null) { + condition.writeToProto(proto, ZenRuleProto.CONDITION); + } + if (component != null) { + component.writeToProto(proto, ZenRuleProto.COMPONENT); + } + + proto.end(token); + } + private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) { if (d == null) return; if (from == null) { diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java index 615830949ed3..18c3e16b910a 100644 --- a/core/java/android/text/style/AlignmentSpan.java +++ b/core/java/android/text/style/AlignmentSpan.java @@ -16,49 +16,90 @@ package android.text.style; +import android.annotation.NonNull; import android.os.Parcel; import android.text.Layout; import android.text.ParcelableSpan; import android.text.TextUtils; +/** + * Span that allows defining the alignment of text at the paragraph level. + */ public interface AlignmentSpan extends ParagraphStyle { + + /** + * Returns the alignment of the text. + * + * @return the text alignment + */ Layout.Alignment getAlignment(); + /** + * Default implementation of the {@link AlignmentSpan}. + * <p> + * For example, a text written in a left to right language, like English, which is by default + * aligned to the left, can be aligned opposite to the layout direction like this: + * <pre>{@code SpannableString string = new SpannableString("Text with opposite alignment"); + *string.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), 0, + *string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/ltralignmentspan.png" /> + * <figcaption>Align left to right text opposite to the layout direction.</figcaption> + * <p> + * A text written in a right to left language, like Hebrew, which is by default aligned to the + * right, can be aligned opposite to the layout direction like this: + * <pre>{@code SpannableString string = new SpannableString("טקסט עם יישור הפוך"); + *string.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), 0, + *string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/rtlalignmentspan.png" /> + * <figcaption>Align right to left text opposite to the layout direction.</figcaption> + */ class Standard implements AlignmentSpan, ParcelableSpan { - public Standard(Layout.Alignment align) { + private final Layout.Alignment mAlignment; + + /** + * Constructs a {@link Standard} from an alignment. + */ + public Standard(@NonNull Layout.Alignment align) { mAlignment = align; } - public Standard(Parcel src) { + /** + * Constructs a {@link Standard} from a parcel. + */ + public Standard(@NonNull Parcel src) { mAlignment = Layout.Alignment.valueOf(src.readString()); } - + + @Override public int getSpanTypeId() { - return getSpanTypeIdInternal(); - } + return getSpanTypeIdInternal(); + } - /** @hide */ - public int getSpanTypeIdInternal() { + /** @hide */ + @Override + public int getSpanTypeIdInternal() { return TextUtils.ALIGNMENT_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeString(mAlignment.name()); } + @Override public Layout.Alignment getAlignment() { return mAlignment; } - - private final Layout.Alignment mAlignment; } } diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java index b098f16da1ed..60aed2a782ab 100644 --- a/core/java/android/text/style/ClickableSpan.java +++ b/core/java/android/text/style/ClickableSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.text.TextPaint; import android.view.View; @@ -24,6 +25,16 @@ import android.view.View; * with a movement method of LinkMovementMethod, the affected spans of * text can be selected. If selected and clicked, the {@link #onClick} method will * be called. + * <p> + * The text with a <code>ClickableSpan</code> attached will be underlined and the link color will be + * used as a text color. The default link color is the theme's accent color or + * <code>android:textColorLink</code> if this attribute is defined in the theme. + * For example, considering that we have a <code>CustomClickableSpan</code> that extends + * <code>ClickableSpan</code>, it can be used like this: + * <pre>{@code SpannableString string = new SpannableString("Text with clickable text"); + *string.setSpan(new CustomClickableSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/clickablespan.png" /> + * <figcaption>Text with <code>ClickableSpan</code>.</figcaption> */ public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance { private static int sIdCounter = 0; @@ -33,13 +44,13 @@ public abstract class ClickableSpan extends CharacterStyle implements UpdateAppe /** * Performs the click action associated with this span. */ - public abstract void onClick(View widget); + public abstract void onClick(@NonNull View widget); /** * Makes the text underlined and in the link color. */ @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { ds.setColor(ds.linkColor); ds.setUnderlineText(true); } diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java index 7af1c2c89600..9ee0b074459e 100644 --- a/core/java/android/text/style/EasyEditSpan.java +++ b/core/java/android/text/style/EasyEditSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.app.PendingIntent; import android.os.Parcel; import android.text.ParcelableSpan; @@ -79,7 +80,7 @@ public class EasyEditSpan implements ParcelableSpan { /** * Constructor called from {@link TextUtils} to restore the span. */ - public EasyEditSpan(Parcel source) { + public EasyEditSpan(@NonNull Parcel source) { mPendingIntent = source.readParcelable(null); mDeleteEnabled = (source.readByte() == 1); } @@ -90,12 +91,12 @@ public class EasyEditSpan implements ParcelableSpan { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeParcelable(mPendingIntent, 0); dest.writeByte((byte) (mDeleteEnabled ? 1 : 0)); } diff --git a/core/java/android/text/style/MetricAffectingSpan.java b/core/java/android/text/style/MetricAffectingSpan.java index 853ecc6e8f49..61b7947af638 100644 --- a/core/java/android/text/style/MetricAffectingSpan.java +++ b/core/java/android/text/style/MetricAffectingSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.text.TextPaint; /** @@ -23,13 +24,19 @@ import android.text.TextPaint; * changes the width or height of characters extend this class. */ public abstract class MetricAffectingSpan -extends CharacterStyle -implements UpdateLayout { + extends CharacterStyle + implements UpdateLayout { - public abstract void updateMeasureState(TextPaint p); + /** + * Classes that extend MetricAffectingSpan implement this method to update the text formatting + * in a way that can change the width or height of characters. + * + * @param textPaint the paint used for drawing the text + */ + public abstract void updateMeasureState(@NonNull TextPaint textPaint); /** - * Returns "this" for most MetricAffectingSpans, but for + * Returns "this" for most MetricAffectingSpans, but for * MetricAffectingSpans that were generated by {@link #wrap}, * returns the underlying MetricAffectingSpan. */ @@ -41,18 +48,18 @@ implements UpdateLayout { /** * A Passthrough MetricAffectingSpan is one that * passes {@link #updateDrawState} and {@link #updateMeasureState} - * calls through to the specified MetricAffectingSpan + * calls through to the specified MetricAffectingSpan * while still being a distinct object, * and is therefore able to be attached to the same Spannable * to which the specified MetricAffectingSpan is already attached. */ /* package */ static class Passthrough extends MetricAffectingSpan { private MetricAffectingSpan mStyle; - + /** * Creates a new Passthrough of the specfied MetricAffectingSpan. */ - public Passthrough(MetricAffectingSpan cs) { + Passthrough(@NonNull MetricAffectingSpan cs) { mStyle = cs; } @@ -60,7 +67,7 @@ implements UpdateLayout { * Passes updateDrawState through to the underlying MetricAffectingSpan. */ @Override - public void updateDrawState(TextPaint tp) { + public void updateDrawState(@NonNull TextPaint tp) { mStyle.updateDrawState(tp); } @@ -68,10 +75,10 @@ implements UpdateLayout { * Passes updateMeasureState through to the underlying MetricAffectingSpan. */ @Override - public void updateMeasureState(TextPaint tp) { + public void updateMeasureState(@NonNull TextPaint tp) { mStyle.updateMeasureState(tp); } - + /** * Returns the MetricAffectingSpan underlying this one, or the one * underlying it if it too is a Passthrough. diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java index 7217e1e5efa8..a1c12c256fa1 100644 --- a/core/java/android/text/style/QuoteSpan.java +++ b/core/java/android/text/style/QuoteSpan.java @@ -17,6 +17,9 @@ package android.text.style; import android.annotation.ColorInt; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Px; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Parcel; @@ -24,68 +27,178 @@ import android.text.Layout; import android.text.ParcelableSpan; import android.text.TextUtils; +/** + * A span which styles paragraphs by adding a vertical stripe at the beginning of the text + * (respecting layout direction). + * <p> + * A <code>QuoteSpan</code> must be attached from the first character to the last character of a + * single paragraph, otherwise the span will not be displayed. + * <p> + * <code>QuoteSpans</code> allow configuring the following elements: + * <ul> + * <li><b>color</b> - the vertical stripe color. By default, the stripe color is 0xff0000ff</li> + * <li><b>gap width</b> - the distance, in pixels, between the stripe and the paragraph. + * Default value is 2px.</li> + * <li><b>stripe width</b> - the width, in pixels, of the stripe. Default value is + * 2px.</li> + * </ul> + * For example, a <code>QuoteSpan</code> using the default values can be constructed like this: + * <pre>{@code SpannableString string = new SpannableString("Text with quote span on a long line"); + *string.setSpan(new QuoteSpan(), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/defaultquotespan.png" /> + * <figcaption><code>QuoteSpan</code> constructed with default values.</figcaption> + * <p> + * <p> + * To construct a <code>QuoteSpan</code> with a green stripe, of 20px in width and a gap width of + * 40px: + * <pre>{@code SpannableString string = new SpannableString("Text with quote span on a long line"); + *string.setSpan(new QuoteSpan(Color.GREEN, 20, 40), 0, string.length(), + *Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/customquotespan.png" /> + * <figcaption>Customized <code>QuoteSpan</code>.</figcaption> + */ public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan { - private static final int STRIPE_WIDTH = 2; - private static final int GAP_WIDTH = 2; + /** + * Default stripe width in pixels. + */ + public static final int STANDARD_STRIPE_WIDTH_PX = 2; + + /** + * Default gap width in pixels. + */ + public static final int STANDARD_GAP_WIDTH_PX = 2; + + /** + * Default color for the quote stripe. + */ + @ColorInt + public static final int STANDARD_COLOR = 0xff0000ff; + @ColorInt private final int mColor; - + @Px + private final int mStripeWidth; + @Px + private final int mGapWidth; + + /** + * Creates a {@link QuoteSpan} with the default values. + */ public QuoteSpan() { - super(); - mColor = 0xff0000ff; + this(STANDARD_COLOR, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX); } + /** + * Creates a {@link QuoteSpan} based on a color. + * + * @param color the color of the quote stripe. + */ public QuoteSpan(@ColorInt int color) { - super(); + this(color, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX); + } + + /** + * Creates a {@link QuoteSpan} based on a color, a stripe width and the width of the gap + * between the stripe and the text. + * + * @param color the color of the quote stripe. + * @param stripeWidth the width of the stripe. + * @param gapWidth the width of the gap between the stripe and the text. + */ + public QuoteSpan(@ColorInt int color, @IntRange(from = 0) int stripeWidth, + @IntRange(from = 0) int gapWidth) { mColor = color; + mStripeWidth = stripeWidth; + mGapWidth = gapWidth; } - public QuoteSpan(Parcel src) { + /** + * Create a {@link QuoteSpan} from a parcel. + */ + public QuoteSpan(@NonNull Parcel src) { mColor = src.readInt(); + mStripeWidth = src.readInt(); + mGapWidth = src.readInt(); } + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } - /** @hide */ + /** + * @hide + */ + @Override public int getSpanTypeIdInternal() { return TextUtils.QUOTE_SPAN; } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { writeToParcelInternal(dest, flags); } - /** @hide */ + /** + * @hide + */ + @Override public void writeToParcelInternal(Parcel dest, int flags) { dest.writeInt(mColor); + dest.writeInt(mStripeWidth); + dest.writeInt(mGapWidth); } + /** + * Get the color of the quote stripe. + * + * @return the color of the quote stripe. + */ @ColorInt public int getColor() { return mColor; } + /** + * Get the width of the quote stripe. + * + * @return the width of the quote stripe. + */ + public int getStripeWidth() { + return mStripeWidth; + } + + /** + * Get the width of the gap between the stripe and the text. + * + * @return the width of the gap between the stripe and the text. + */ + public int getGapWidth() { + return mGapWidth; + } + + @Override public int getLeadingMargin(boolean first) { - return STRIPE_WIDTH + GAP_WIDTH; + return mStripeWidth + mGapWidth; } - public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, - int top, int baseline, int bottom, - CharSequence text, int start, int end, - boolean first, Layout layout) { + @Override + public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir, + int top, int baseline, int bottom, + @NonNull CharSequence text, int start, int end, + boolean first, @NonNull Layout layout) { Paint.Style style = p.getStyle(); int color = p.getColor(); p.setStyle(Paint.Style.FILL); p.setColor(mColor); - c.drawRect(x, top, x + dir * STRIPE_WIDTH, bottom, p); + c.drawRect(x, top, x + dir * mStripeWidth, bottom, p); p.setStyle(style); p.setColor(color); diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java index bf335196edef..597089235e6b 100644 --- a/core/java/android/util/MemoryIntArray.java +++ b/core/java/android/util/MemoryIntArray.java @@ -16,13 +16,19 @@ package android.util; +import static android.os.Process.FIRST_APPLICATION_UID; + import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.Process; + +import com.android.internal.annotations.GuardedBy; -import libcore.io.IoUtils; import dalvik.system.CloseGuard; +import libcore.io.IoUtils; + import java.io.Closeable; import java.io.IOException; import java.util.UUID; @@ -49,13 +55,18 @@ import java.util.UUID; */ public final class MemoryIntArray implements Parcelable, Closeable { private static final String TAG = "MemoryIntArray"; + private static final boolean DEBUG = Process.myUid() < FIRST_APPLICATION_UID; private static final int MAX_SIZE = 1024; + private final Object mLock = new Object(); private final CloseGuard mCloseGuard = CloseGuard.get(); private final boolean mIsOwner; private final long mMemoryAddr; + + /** Fd for the shared memory object, -1 when closed */ + @GuardedBy("mLock") private int mFd = -1; /** @@ -74,6 +85,7 @@ public final class MemoryIntArray implements Parcelable, Closeable { mFd = nativeCreate(name, size); mMemoryAddr = nativeOpen(mFd, mIsOwner); mCloseGuard.open("close"); + if (DEBUG) Log.i(TAG, "created " + getString()); } private MemoryIntArray(Parcel parcel) throws IOException { @@ -85,6 +97,8 @@ public final class MemoryIntArray implements Parcelable, Closeable { mFd = pfd.detachFd(); mMemoryAddr = nativeOpen(mFd, mIsOwner); mCloseGuard.open("close"); + + if (DEBUG) Log.i(TAG, "created from parcel " + getString()); } /** @@ -141,13 +155,33 @@ public final class MemoryIntArray implements Parcelable, Closeable { */ @Override public void close() throws IOException { - if (!isClosed()) { - nativeClose(mFd, mMemoryAddr, mIsOwner); - mFd = -1; - mCloseGuard.close(); + synchronized (mLock) { + if (!isClosed()) { + if (DEBUG) { + try { + throw new Exception(); + } catch (Exception here) { + Log.i(TAG, "closing " + getString(), here); + } + } + nativeClose(mFd, mMemoryAddr, mIsOwner); + mFd = -1; + mCloseGuard.close(); + } else { + try { + throw new Exception(); + } catch (Exception here) { + if (DEBUG) Log.i(TAG, getString() + " already closed", here); + } + } } } + private String getString() { + return this.getClass().getSimpleName() + "@" + System.identityHashCode(this) + + " mMemoryAddr=" + mMemoryAddr + " mFd=" + mFd; + } + /** * @return Whether this array is closed and shouldn't be used. */ @@ -162,7 +196,9 @@ public final class MemoryIntArray implements Parcelable, Closeable { mCloseGuard.warnIfOpen(); } - IoUtils.closeQuietly(this); + if (!isClosed()) { + IoUtils.closeQuietly(this); + } } finally { super.finalize(); } @@ -206,7 +242,8 @@ public final class MemoryIntArray implements Parcelable, Closeable { private void enforceNotClosed() { if (isClosed()) { - throw new IllegalStateException("cannot interact with a closed instance"); + throw new IllegalStateException("cannot interact with a closed instance " + + getString()); } } diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java new file mode 100644 index 000000000000..d100f12ed026 --- /dev/null +++ b/core/java/android/util/SparseSetArray.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.util; + +/** + * A sparse array of ArraySets, which is suitable to hold userid->packages association. + * + * @hide + */ +public class SparseSetArray<T> { + private final SparseArray<ArraySet<T>> mData = new SparseArray<>(); + + public SparseSetArray() { + } + + /** + * Add a value at index n. + * @return FALSE when the value already existed at the given index, TRUE otherwise. + */ + public boolean add(int n, T value) { + ArraySet<T> set = mData.get(n); + if (set == null) { + set = new ArraySet<>(); + mData.put(n, set); + } + if (set.contains(value)) { + return true; + } + set.add(value); + return false; + } + + /** + * @return whether a value exists at index n. + */ + public boolean contains(int n, T value) { + final ArraySet<T> set = mData.get(n); + if (set == null) { + return false; + } + return set.contains(value); + } + + /** + * Remove a value from index n. + * @return TRUE when the value existed at the given index and removed, FALSE otherwise. + */ + public boolean remove(int n, T value) { + final ArraySet<T> set = mData.get(n); + if (set == null) { + return false; + } + final boolean ret = set.remove(value); + if (set.size() == 0) { + mData.remove(n); + } + return ret; + } + + /** + * Remove all values from index n. + */ + public void remove(int n) { + mData.remove(n); + } + public int size() { + return mData.size(); + } + + public int keyAt(int index) { + return mData.keyAt(index); + } + + public int sizeAt(int index) { + final ArraySet<T> set = mData.valueAt(index); + if (set == null) { + return 0; + } + return set.size(); + } + + public T valueAt(int intIndex, int valueIndex) { + return mData.valueAt(intIndex).valueAt(valueIndex); + } +} diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index ee6fc072765f..a4c590f1b459 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -477,6 +477,7 @@ public class ApkSignatureSchemeV3Verifier { } } + signedData.rewind(); byte[] encodedCert = readLengthPrefixedByteArray(signedData); int signedSigAlgorithm = signedData.getInt(); if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) { diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index ba21ccb808fc..4c6e511ede46 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -308,14 +308,6 @@ abstract class ApkVerityBuilder { return rootHash; } - private static void bufferPut(ByteBuffer buffer, byte value) { - // FIXME(b/72459251): buffer.put(value) does NOT work surprisingly. The position() after put - // does NOT even change. This hack workaround the problem, but the root cause remains - // unkonwn yet. This seems only happen when it goes through the apk install flow on my - // setup. - buffer.put(new byte[] { value }); - } - private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth, byte[] salt) { if (salt.length != 8) { @@ -325,10 +317,10 @@ abstract class ApkVerityBuilder { // TODO(b/30972906): update the reference when there is a better one in public. buffer.put("TrueBrew".getBytes()); // magic - bufferPut(buffer, (byte) 1); // major version - bufferPut(buffer, (byte) 0); // minor version - bufferPut(buffer, (byte) 12); // log2(block-size): log2(4096) - bufferPut(buffer, (byte) 7); // log2(leaves-per-node): log2(4096 / 32) + buffer.put((byte) 1); // major version + buffer.put((byte) 0); // minor version + buffer.put((byte) 12); // log2(block-size): log2(4096) + buffer.put((byte) 7); // log2(leaves-per-node): log2(4096 / 32) buffer.putShort((short) 1); // meta algorithm, SHA256_MODE == 1 buffer.putShort((short) 1); // data algorithm, SHA256_MODE == 1 @@ -338,8 +330,8 @@ abstract class ApkVerityBuilder { buffer.putLong(fileSize); // original file size - bufferPut(buffer, (byte) 0); // auth block offset, disabled here - bufferPut(buffer, (byte) 2); // extension count + buffer.put((byte) 0); // auth block offset, disabled here + buffer.put((byte) 2); // extension count buffer.put(salt); // salt (8 bytes) // skip(buffer, 22); // reserved diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 5bd7446d08b6..31cfebcc3ae3 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -267,21 +267,21 @@ public final class Display { * * @see #getState */ - public static final int STATE_UNKNOWN = 0; + public static final int STATE_UNKNOWN = ViewProtoEnums.DISPLAY_STATE_UNKNOWN; // 0 /** * Display state: The display is off. * * @see #getState */ - public static final int STATE_OFF = 1; + public static final int STATE_OFF = ViewProtoEnums.DISPLAY_STATE_OFF; // 1 /** * Display state: The display is on. * * @see #getState */ - public static final int STATE_ON = 2; + public static final int STATE_ON = ViewProtoEnums.DISPLAY_STATE_ON; // 2 /** * Display state: The display is dozing in a low power state; it is still @@ -291,7 +291,7 @@ public final class Display { * @see #getState * @see android.os.PowerManager#isInteractive */ - public static final int STATE_DOZE = 3; + public static final int STATE_DOZE = ViewProtoEnums.DISPLAY_STATE_DOZE; // 3 /** * Display state: The display is dozing in a suspended low power state; it is still @@ -303,7 +303,7 @@ public final class Display { * @see #getState * @see android.os.PowerManager#isInteractive */ - public static final int STATE_DOZE_SUSPEND = 4; + public static final int STATE_DOZE_SUSPEND = ViewProtoEnums.DISPLAY_STATE_DOZE_SUSPEND; // 4 /** * Display state: The display is on and optimized for VR mode. @@ -311,7 +311,7 @@ public final class Display { * @see #getState * @see android.os.PowerManager#isInteractive */ - public static final int STATE_VR = 5; + public static final int STATE_VR = ViewProtoEnums.DISPLAY_STATE_VR; // 5 /** * Display state: The display is in a suspended full power state; it is still @@ -323,7 +323,7 @@ public final class Display { * @see #getState * @see android.os.PowerManager#isInteractive */ - public static final int STATE_ON_SUSPEND = 6; + public static final int STATE_ON_SUSPEND = ViewProtoEnums.DISPLAY_STATE_ON_SUSPEND; // 6 /* The color mode constants defined below must be kept in sync with the ones in * system/core/include/system/graphics-base.h */ diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl new file mode 100644 index 000000000000..5607b1134e5b --- /dev/null +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.app.ActivityManager; +import android.view.IRemoteAnimationFinishedCallback; +import android.graphics.GraphicBuffer; + +/** + * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the + * runner control certain aspects of the recents animation, and to notify window manager when the + * animation has completed. + * + * {@hide} + */ +interface IRecentsAnimationController { + + /** + * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the + * current set of task ids provided to the handler. + */ + ActivityManager.TaskSnapshot screenshotTask(int taskId); + + /** + * Notifies to the system that the animation into Recents should end, and all leashes associated + * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then + * the home activity should be moved to the top. Otherwise, the home activity is hidden and the + * user is returned to the app. + */ + void finish(boolean moveHomeToTop); + + /** + * Called by the handler to indicate that the recents animation input consumer should be + * enabled. This is currently used to work around an issue where registering an input consumer + * mid-animation causes the existing motion event chain to be canceled. Instead, the caller + * may register the recents animation input consumer prior to starting the recents animation + * and then enable it mid-animation to start receiving touch events. + */ + void setInputConsumerEnabled(boolean enabled); +} diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl new file mode 100644 index 000000000000..ea6226b3ea69 --- /dev/null +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.view.RemoteAnimationTarget; +import android.view.IRecentsAnimationController; + +/** + * Interface that is used to callback from window manager to the process that runs a recents + * animation to start or cancel it. + * + * {@hide} + */ +oneway interface IRecentsAnimationRunner { + + /** + * Called when the system is ready for the handler to start animating all the visible tasks. + */ + void onAnimationStart(in IRecentsAnimationController controller, + in RemoteAnimationTarget[] apps); + + /** + * Called when the system needs to cancel the current animation. This can be due to the + * wallpaper not drawing in time, or the handler not finishing the animation within a predefined + * amount of time. + */ + void onAnimationCanceled(); +} diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index f39e618e169d..c28c3894482d 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.IntDef; +import android.app.WindowConfiguration; import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; @@ -98,8 +99,14 @@ public class RemoteAnimationTarget implements Parcelable { */ public final Rect sourceContainerBounds; + /** + * The window configuration for the target. + */ + public final WindowConfiguration windowConfiguration; + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, - Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) { + Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds, + WindowConfiguration windowConfig) { this.mode = mode; this.taskId = taskId; this.leash = leash; @@ -108,6 +115,7 @@ public class RemoteAnimationTarget implements Parcelable { this.prefixOrderIndex = prefixOrderIndex; this.position = new Point(position); this.sourceContainerBounds = new Rect(sourceContainerBounds); + this.windowConfiguration = windowConfig; } public RemoteAnimationTarget(Parcel in) { @@ -119,6 +127,7 @@ public class RemoteAnimationTarget implements Parcelable { prefixOrderIndex = in.readInt(); position = in.readParcelable(null); sourceContainerBounds = in.readParcelable(null); + windowConfiguration = in.readParcelable(null); } @Override @@ -136,6 +145,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeInt(prefixOrderIndex); dest.writeParcelable(position, 0 /* flags */); dest.writeParcelable(sourceContainerBounds, 0 /* flags */); + dest.writeParcelable(windowConfiguration, 0 /* flags */); } public static final Creator<RemoteAnimationTarget> CREATOR diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 8b730f282b1b..370c97e37262 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -166,18 +166,6 @@ public final class ThreadedRenderer { public static final String OVERDRAW_PROPERTY_SHOW = "show"; /** - * Defines the rendering pipeline to be used by the ThreadedRenderer. - * - * Possible values: - * "opengl", will use the existing OpenGL renderer - * "skiagl", will use Skia's OpenGL renderer - * "skiavk", will use Skia's Vulkan renderer - * - * @hide - */ - public static final String DEBUG_RENDERER_PROPERTY = "debug.hwui.renderer"; - - /** * Turn on to debug non-rectangular clip operations. * * Possible values: diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 3bb3a4c17b8f..1c5e87197750 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -98,11 +98,13 @@ public interface WindowManager extends ViewManager { int DOCKED_BOTTOM = 4; /** @hide */ - final static String INPUT_CONSUMER_PIP = "pip_input_consumer"; + String INPUT_CONSUMER_PIP = "pip_input_consumer"; /** @hide */ - final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer"; + String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer"; /** @hide */ - final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer"; + String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer"; + /** @hide */ + String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer"; /** * Not set up for a transition. diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java index f1d633a23131..84d18509f14a 100644 --- a/core/java/android/widget/MediaControlView2.java +++ b/core/java/android/widget/MediaControlView2.java @@ -25,7 +25,6 @@ import android.media.update.ApiLoader; import android.media.update.MediaControlView2Provider; import android.media.update.ViewProvider; import android.util.AttributeSet; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -211,21 +210,11 @@ public class MediaControlView2 extends FrameLayout { } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return mProvider.onKeyDown_impl(keyCode, event); - } - - @Override public void onFinishInflate() { mProvider.onFinishInflate_impl(); } @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return mProvider.dispatchKeyEvent_impl(event); - } - - @Override public void setEnabled(boolean enabled) { mProvider.setEnabled_impl(enabled); } @@ -257,21 +246,11 @@ public class MediaControlView2 extends FrameLayout { } @Override - public boolean onKeyDown_impl(int keyCode, KeyEvent event) { - return MediaControlView2.super.onKeyDown(keyCode, event); - } - - @Override public void onFinishInflate_impl() { MediaControlView2.super.onFinishInflate(); } @Override - public boolean dispatchKeyEvent_impl(KeyEvent event) { - return MediaControlView2.super.dispatchKeyEvent(event); - } - - @Override public void setEnabled_impl(boolean enabled) { MediaControlView2.super.setEnabled(enabled); } diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java index 7f0c086ff8e9..3f941309ec2e 100644 --- a/core/java/android/widget/VideoView2.java +++ b/core/java/android/widget/VideoView2.java @@ -22,15 +22,17 @@ import android.annotation.Nullable; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; -import android.media.MediaPlayerBase; +import android.media.MediaPlayerInterface; import android.media.session.MediaController; +import android.media.session.PlaybackState; import android.media.update.ApiLoader; import android.media.update.VideoView2Provider; import android.media.update.ViewProvider; import android.net.Uri; +import android.os.Bundle; import android.util.AttributeSet; -import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -46,7 +48,7 @@ import java.util.Map; * * <p> * <em> Data sources that VideoView2 supports : </em> - * VideoView2 can play video files and audio-only fiels as + * VideoView2 can play video files and audio-only files as * well. It can load from various sources such as resources or content providers. The supported * media file formats are the same as MediaPlayer2. * @@ -109,7 +111,18 @@ public class VideoView2 extends FrameLayout { @Retention(RetentionPolicy.SOURCE) public @interface ViewType {} + /** + * Indicates video is rendering on SurfaceView + * + * @see #setViewType + */ public static final int VIEW_TYPE_SURFACEVIEW = 1; + + /** + * Indicates video is rendering on TextureView + * + * @see #setViewType + */ public static final int VIEW_TYPE_TEXTUREVIEW = 2; private final VideoView2Provider mProvider; @@ -175,13 +188,6 @@ public class VideoView2 extends FrameLayout { } /** - * Returns the audio session ID. - */ - public int getAudioSessionId() { - return mProvider.getAudioSessionId_impl(); - } - - /** * Starts rendering closed caption or subtitles if there is any. The first subtitle track will * be chosen by default if there multiple subtitle tracks exist. */ @@ -256,7 +262,7 @@ public class VideoView2 extends FrameLayout { * @throws IllegalStateException if MediaControlView2 is not set. */ public void setRouteAttributes(@NonNull List<String> routeCategories, - @Nullable MediaPlayerBase player) { + @Nullable MediaPlayerInterface player) { mProvider.setRouteAttributes_impl(routeCategories, player); } @@ -274,8 +280,8 @@ public class VideoView2 extends FrameLayout { * * @param uri the URI of the video. */ - public void setVideoURI(Uri uri) { - mProvider.setVideoURI_impl(uri); + public void setVideoUri(Uri uri) { + mProvider.setVideoUri_impl(uri); } /** @@ -288,8 +294,8 @@ public class VideoView2 extends FrameLayout { * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value * to disallow or allow cross domain redirection. */ - public void setVideoURI(Uri uri, Map<String, String> headers) { - mProvider.setVideoURI_impl(uri, headers); + public void setVideoUri(Uri uri, Map<String, String> headers) { + mProvider.setVideoUri_impl(uri, headers); } /** @@ -316,6 +322,19 @@ public class VideoView2 extends FrameLayout { } /** + * Sets custom actions which will be shown as custom buttons in {@link MediaControlView2}. + * + * @param actionList A list of {@link PlaybackState.CustomAction}. The return value of + * {@link PlaybackState.CustomAction#getIcon()} will be used to draw buttons + * in {@link MediaControlView2}. + * @param listener A listener to be called when a custom button is clicked. + */ + public void setCustomActions(List<PlaybackState.CustomAction> actionList, + OnCustomActionListener listener) { + mProvider.setCustomActions_impl(actionList, listener); + } + + /** * Registers a callback to be invoked when the media file is loaded and ready to go. * * @param l the callback that will be run. @@ -372,19 +391,20 @@ public class VideoView2 extends FrameLayout { } /** - * Interface definition of a callback to be invoked when the viw type has been changed. + * Interface definition of a callback to be invoked when the view type has been changed. */ public interface OnViewTypeChangedListener { /** * Called when the view type has been changed. * @see #setViewType(int) + * @param view the View whose view type is changed * @param viewType * <ul> * <li>{@link #VIEW_TYPE_SURFACEVIEW} * <li>{@link #VIEW_TYPE_TEXTUREVIEW} * </ul> */ - void onViewTypeChanged(@ViewType int viewType); + void onViewTypeChanged(View view, @ViewType int viewType); } /** @@ -394,7 +414,7 @@ public class VideoView2 extends FrameLayout { /** * Called when the media file is ready for playback. */ - void onPrepared(); + void onPrepared(View view); } /** @@ -405,7 +425,7 @@ public class VideoView2 extends FrameLayout { /** * Called when the end of a media source is reached during playback. */ - void onCompletion(); + void onCompletion(View view); } /** @@ -421,7 +441,7 @@ public class VideoView2 extends FrameLayout { * @return true if the method handled the error, false if it didn't. * @see MediaPlayer#OnErrorListener */ - boolean onError(int what, int extra); + boolean onError(View view, int what, int extra); } /** @@ -436,7 +456,7 @@ public class VideoView2 extends FrameLayout { * * @see MediaPlayer#OnInfoListener */ - void onInfo(int what, int extra); + void onInfo(View view, int what, int extra); } /** @@ -446,7 +466,23 @@ public class VideoView2 extends FrameLayout { /** * Called to indicate a fullscreen mode change. */ - void onFullScreenChanged(boolean fullScreen); + void onFullScreenChanged(View view, boolean fullScreen); + } + + /** + * Interface definition of a callback to be invoked to inform that a custom action is performed. + * + * TODO: When MediaSession2 is ready, modify the method to match the signature. + */ + public interface OnCustomActionListener { + /** + * Called to indicate that a custom action is performed. + * + * @param action The action that was originally sent in the + * {@link PlaybackState.CustomAction}. + * @param extras Optional extras. + */ + void onCustomAction(String action, Bundle extras); } @Override @@ -475,21 +511,11 @@ public class VideoView2 extends FrameLayout { } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return mProvider.onKeyDown_impl(keyCode, event); - } - - @Override public void onFinishInflate() { mProvider.onFinishInflate_impl(); } @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return mProvider.dispatchKeyEvent_impl(event); - } - - @Override public void setEnabled(boolean enabled) { mProvider.setEnabled_impl(enabled); } @@ -521,21 +547,11 @@ public class VideoView2 extends FrameLayout { } @Override - public boolean onKeyDown_impl(int keyCode, KeyEvent event) { - return VideoView2.super.onKeyDown(keyCode, event); - } - - @Override public void onFinishInflate_impl() { VideoView2.super.onFinishInflate(); } @Override - public boolean dispatchKeyEvent_impl(KeyEvent event) { - return VideoView2.super.dispatchKeyEvent(event); - } - - @Override public void setEnabled_impl(boolean enabled) { VideoView2.super.setEnabled(enabled); } diff --git a/core/java/com/android/internal/app/EventLogTags.logtags b/core/java/com/android/internal/app/EventLogTags.logtags new file mode 100644 index 000000000000..d681a8d26e8e --- /dev/null +++ b/core/java/com/android/internal/app/EventLogTags.logtags @@ -0,0 +1,6 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.internal.app; + +53000 harmful_app_warning_uninstall (package_name|3) +53001 harmful_app_warning_launch_anyway (package_name|3)
\ No newline at end of file diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java index 042da36c98f1..99666264cb22 100644 --- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java +++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java @@ -20,8 +20,12 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; +import android.view.View; +import android.widget.TextView; import com.android.internal.R; /** @@ -31,7 +35,7 @@ import com.android.internal.R; */ public class HarmfulAppWarningActivity extends AlertActivity implements DialogInterface.OnClickListener { - private static final String TAG = "HarmfulAppWarningActivity"; + private static final String TAG = HarmfulAppWarningActivity.class.getSimpleName(); private static final String EXTRA_HARMFUL_APP_WARNING = "harmful_app_warning"; @@ -39,13 +43,11 @@ public class HarmfulAppWarningActivity extends AlertActivity implements private String mHarmfulAppWarning; private IntentSender mTarget; - // [b/63909431] STOPSHIP replace placeholder UI with final Harmful App Warning UI - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Intent intent = getIntent(); + final Intent intent = getIntent(); mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT); mHarmfulAppWarning = intent.getStringExtra(EXTRA_HARMFUL_APP_WARNING); @@ -55,33 +57,56 @@ public class HarmfulAppWarningActivity extends AlertActivity implements finish(); } - AlertController.AlertParams p = mAlertParams; + final ApplicationInfo applicationInfo; + try { + applicationInfo = getPackageManager().getApplicationInfo(mPackageName, 0 /*flags*/); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Could not show warning because package does not exist ", e); + finish(); + return; + } + + final AlertController.AlertParams p = mAlertParams; p.mTitle = getString(R.string.harmful_app_warning_title); - p.mMessage = mHarmfulAppWarning; - p.mPositiveButtonText = getString(R.string.harmful_app_warning_launch_anyway); + p.mView = createView(applicationInfo); + + p.mPositiveButtonText = getString(R.string.harmful_app_warning_uninstall); p.mPositiveButtonListener = this; - p.mNegativeButtonText = getString(R.string.harmful_app_warning_uninstall); + p.mNegativeButtonText = getString(R.string.harmful_app_warning_open_anyway); p.mNegativeButtonListener = this; mAlert.installContent(mAlertParams); } + private View createView(ApplicationInfo applicationInfo) { + final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog, + null /*root*/); + ((TextView) view.findViewById(R.id.app_name_text)) + .setText(applicationInfo.loadSafeLabel(getPackageManager())); + ((TextView) view.findViewById(R.id.message)) + .setText(mHarmfulAppWarning); + return view; + } + @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - getPackageManager().setHarmfulAppWarning(mPackageName, null); + getPackageManager().deletePackage(mPackageName, null /*observer*/, 0 /*flags*/); + EventLogTags.writeHarmfulAppWarningUninstall(mPackageName); + finish(); + break; + case DialogInterface.BUTTON_NEGATIVE: + getPackageManager().setHarmfulAppWarning(mPackageName, null /*warning*/); - IntentSender target = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + final IntentSender target = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); try { - startIntentSenderForResult(target, -1, null, 0, 0, 0); + startIntentSenderForResult(target, -1 /*requestCode*/, null /*fillInIntent*/, + 0 /*flagsMask*/, 0 /*flagsValue*/, 0 /*extraFlags*/); } catch (IntentSender.SendIntentException e) { - // ignore.. + Log.e(TAG, "Error while starting intent sender", e); } - finish(); - break; - case DialogInterface.BUTTON_NEGATIVE: - getPackageManager().deletePackage(mPackageName, null, 0); + EventLogTags.writeHarmfulAppWarningLaunchAnyway(mPackageName); finish(); break; } @@ -89,7 +114,7 @@ public class HarmfulAppWarningActivity extends AlertActivity implements public static Intent createHarmfulAppWarningIntent(Context context, String targetPackageName, IntentSender target, CharSequence harmfulAppWarning) { - Intent intent = new Intent(); + final Intent intent = new Intent(); intent.setClass(context, HarmfulAppWarningActivity.class); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName); intent.putExtra(Intent.EXTRA_INTENT, target); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index ee3bec803b51..fd5fe100444a 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -41,9 +41,11 @@ import android.os.Handler; import android.os.IBatteryPropertiesRegistrar; import android.os.Looper; import android.os.Message; +import android.os.OsProtoEnums; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; +import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -4252,11 +4254,11 @@ public class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime); if (wc != null) { - StatsLog.write( - StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1); + StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), + getPowerManagerWakeLockLevel(type), name, 1); } else { - StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name, - 1); + StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, + getPowerManagerWakeLockLevel(type), name, 1); } } } @@ -4295,15 +4297,45 @@ public class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime); if (wc != null) { - StatsLog.write( - StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0); + StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), + getPowerManagerWakeLockLevel(type), name, 0); } else { - StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name, - 0); + StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, + getPowerManagerWakeLockLevel(type), name, 0); } } } + /** + * Converts BatteryStats wakelock types back into PowerManager wakelock levels. + * This is the inverse map of Notifier.getBatteryStatsWakeLockMonitorType(). + * These are estimations, since batterystats loses some of the original data. + * TODO: Delete this. Instead, StatsLog.write should be called from PowerManager's Notifier. + */ + private int getPowerManagerWakeLockLevel(int battertStatsWakelockType) { + switch (battertStatsWakelockType) { + // PowerManager.PARTIAL_WAKE_LOCK or PROXIMITY_SCREEN_OFF_WAKE_LOCK + case BatteryStats.WAKE_TYPE_PARTIAL: + return PowerManager.PARTIAL_WAKE_LOCK; + + // PowerManager.SCREEN_DIM_WAKE_LOCK or SCREEN_BRIGHT_WAKE_LOCK + case BatteryStats.WAKE_TYPE_FULL: + return PowerManager.FULL_WAKE_LOCK; + + case BatteryStats.WAKE_TYPE_DRAW: + return PowerManager.DRAW_WAKE_LOCK; + + // It appears that nothing can ever make a Window and PowerManager lacks an equivalent. + case BatteryStats.WAKE_TYPE_WINDOW: + Slog.e(TAG, "Illegal window wakelock type observed in batterystats."); + return -1; + + default: + Slog.e(TAG, "Illegal wakelock type in batterystats: " + battertStatsWakelockType); + return -1; + } + } + public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, String historyName, int type, boolean unimportantForLogging) { final long elapsedRealtime = mClocks.elapsedRealtime(); @@ -12215,7 +12247,7 @@ public class BatteryStatsImpl extends BatteryStats { } // This should probably be exposed in the API, though it's not critical - public static final int BATTERY_PLUGGED_NONE = 0; + public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0 public void setBatteryStateLocked(final int status, final int health, final int plugType, final int level, /* not final */ int temp, final int volt, final int chargeUAh, diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index c5af89727575..111934f9d005 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -149,6 +149,9 @@ public class SystemConfig { final ArrayMap<String, ArraySet<String>> mVendorPrivAppPermissions = new ArrayMap<>(); final ArrayMap<String, ArraySet<String>> mVendorPrivAppDenyPermissions = new ArrayMap<>(); + final ArrayMap<String, ArraySet<String>> mProductPrivAppPermissions = new ArrayMap<>(); + final ArrayMap<String, ArraySet<String>> mProductPrivAppDenyPermissions = new ArrayMap<>(); + final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>(); public static SystemConfig getInstance() { @@ -240,6 +243,14 @@ public class SystemConfig { return mVendorPrivAppDenyPermissions.get(packageName); } + public ArraySet<String> getProductPrivAppPermissions(String packageName) { + return mProductPrivAppPermissions.get(packageName); + } + + public ArraySet<String> getProductPrivAppDenyPermissions(String packageName) { + return mProductPrivAppDenyPermissions.get(packageName); + } + public Map<String, Boolean> getOemPermissions(String packageName) { final Map<String, Boolean> oemPermissions = mOemPermissions.get(packageName); if (oemPermissions != null) { @@ -278,6 +289,14 @@ public class SystemConfig { Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag); readPermissions(Environment.buildPath( Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag); + + // Allow Product to customize system configs around libs, features, permissions and apps + int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS | + ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS; + readPermissions(Environment.buildPath( + Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag); + readPermissions(Environment.buildPath( + Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag); } void readPermissions(File libraryDir, int permissionFlag) { @@ -598,15 +617,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) { - // privapp permissions from system and vendor partitions are stored + // privapp permissions from system, vendor and product partitions are stored // separately. This is to prevent xml files in the vendor partition from // granting permissions to priv apps in the system partition and vice // versa. boolean vendor = permFile.toPath().startsWith( Environment.getVendorDirectory().toPath()); + boolean product = permFile.toPath().startsWith( + Environment.getProductDirectory().toPath()); if (vendor) { readPrivAppPermissions(parser, mVendorPrivAppPermissions, mVendorPrivAppDenyPermissions); + } else if (product) { + readPrivAppPermissions(parser, mProductPrivAppPermissions, + mProductPrivAppDenyPermissions); } else { readPrivAppPermissions(parser, mPrivAppPermissions, mPrivAppDenyPermissions); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 5751fc99a484..93f95c634ae3 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_Log.cpp", "android_util_MemoryIntArray.cpp", + "android_util_Log.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -190,7 +190,6 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", - "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e3b5c8f3136a..bf7a7794fcc6 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,7 +122,6 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); -extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -974,6 +973,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) addOption("--generate-debug-info"); } + // The mini-debug-info makes it possible to backtrace through JIT code. + if (property_get_bool("dalvik.vm.minidebuginfo", 0)) { + addOption("-Xcompiler-option"); + addOption("--generate-mini-debug-info"); + } + /* * Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will * contain the fingerprint and can be parsed. @@ -1341,7 +1346,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), - REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp index 262b55323cca..0e562c03b762 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -17,6 +17,7 @@ #include "GraphicsJNI.h" #include "ImageDecoder.h" #include "core_jni_helpers.h" +#include "Utils.h" #include <hwui/AnimatedImageDrawable.h> #include <hwui/Canvas.h> @@ -28,6 +29,7 @@ using namespace android; +static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID; // Note: jpostProcess holds a handle to the ImageDecoder. static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, @@ -113,6 +115,9 @@ static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, return drawable->isRunning(); } +// Java's NOT_RUNNING relies on this being -2.0. +static_assert(SkAnimatedImage::kNotRunning == -2.0); + static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); return drawable->start(); @@ -123,6 +128,44 @@ static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong na drawable->stop(); } +// Java's LOOP_INFINITE relies on this being the same. +static_assert(SkCodec::kRepetitionCountInfinite == -1); + +static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint loopCount) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->setRepetitionCount(loopCount); +} + +class JniAnimationEndListener : public OnAnimationEndListener { +public: + JniAnimationEndListener(JNIEnv* env, jobject javaObject) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); + mJavaObject = env->NewGlobalRef(javaObject); + } + + ~JniAnimationEndListener() override { + auto* env = get_env_or_die(mJvm); + env->DeleteGlobalRef(mJavaObject); + } + + void onAnimationEnd() override { + auto* env = get_env_or_die(mJvm); + env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID); + } + +private: + JavaVM* mJvm; + jobject mJavaObject; +}; + +static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, + jlong nativePtr, jobject jdrawable) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>( + new JniAnimationEndListener(env, jdrawable))); +} + static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); // FIXME: Report the size of the internal SkBitmap etc. @@ -139,10 +182,15 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, + { "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount }, + { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, }; int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { + jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); + gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V"); + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); } diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp index 85c9ef36b82e..173818b13837 100644 --- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp +++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp @@ -1,5 +1,6 @@ #include "ByteBufferStreamAdaptor.h" #include "core_jni_helpers.h" +#include "Utils.h" #include <SkStream.h> @@ -8,14 +9,6 @@ using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; -static JNIEnv* get_env_or_die(JavaVM* jvm) { - JNIEnv* env; - if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); - } - return env; -} - class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 4257c981be18..7a9fea72a78e 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -12,15 +12,6 @@ static jmethodID gInputStream_readMethodID; static jmethodID gInputStream_skipMethodID; -// FIXME: Share with ByteBufferStreamAdaptor.cpp? -static JNIEnv* get_env_or_die(JavaVM* jvm) { - JNIEnv* env; - if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); - } - return env; -} - /** * Wrapper for a Java InputStream. */ @@ -57,13 +48,13 @@ public: } ~JavaInputStreamAdaptor() override { - auto* env = get_env_or_die(fJvm); + auto* env = android::get_env_or_die(fJvm); env->DeleteGlobalRef(fJavaInputStream); env->DeleteGlobalRef(fJavaByteArray); } size_t read(void* buffer, size_t size) override { - auto* env = get_env_or_die(fJvm); + auto* env = android::get_env_or_die(fJvm); if (!fSwallowExceptions && checkException(env)) { // Just in case the caller did not clear from a previous exception. return 0; diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index c50026ea570e..dd3e6f02e9fe 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include <nativehelper/ScopedUtfChars.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_util_AssetManager.h> -#include <androidfw/AssetManager2.h> +#include <androidfw/AssetManager.h> #include "Utils.h" #include "FontUtils.h" @@ -224,8 +224,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr); + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -237,33 +236,27 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - std::unique_ptr<Asset> asset; - { - ScopedLock<AssetManager2> locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } + Asset* asset; + if (isAsset) { + asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + } else { + asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(), + Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); } - if (nullptr == asset) { + if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { + delete asset; builder->axes.clear(); return false; } - sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, - asset.release())); + sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp index 630220a4c9ed..dd9bafe3b411 100644 --- a/core/jni/android/graphics/Utils.cpp +++ b/core/jni/android/graphics/Utils.cpp @@ -117,3 +117,11 @@ jobject android::nullObjectReturn(const char msg[]) { bool android::isSeekable(int descriptor) { return ::lseek64(descriptor, 0, SEEK_CUR) != -1; } + +JNIEnv* android::get_env_or_die(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); + } + return env; +} diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h index 69930a581468..2f2ee9654489 100644 --- a/core/jni/android/graphics/Utils.h +++ b/core/jni/android/graphics/Utils.h @@ -74,6 +74,8 @@ jobject nullObjectReturn(const char msg[]); */ bool isSeekable(int descriptor); +JNIEnv* get_env_or_die(JavaVM* jvm); + }; // namespace android #endif // _ANDROID_GRAPHICS_UTILS_H_ diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..09e37e1a3de6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); + code->assetManager = assetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp deleted file mode 100644 index c0f151b71c93..000000000000 --- a/core/jni/android_content_res_ApkAssets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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. - */ - -#include "android-base/macros.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" -#include "androidfw/ApkAssets.h" -#include "utils/misc.h" - -#include "core_jni_helpers.h" -#include "jni.h" -#include "nativehelper/ScopedUtfChars.h" - -using ::android::base::unique_fd; - -namespace android { - -static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, - jboolean force_shared_lib, jboolean overlay) { - ScopedUtfChars path(env, java_path); - if (path.c_str() == nullptr) { - return 0; - } - - std::unique_ptr<const ApkAssets> apk_assets; - if (overlay) { - apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); - } else if (force_shared_lib) { - apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); - } else { - apk_assets = ApkAssets::Load(path.c_str(), system); - } - - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast<jlong>(apk_assets.release()); -} - -static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, - jstring friendly_name, jboolean system, jboolean force_shared_lib) { - ScopedUtfChars friendly_name_utf8(env, friendly_name); - if (friendly_name_utf8.c_str() == nullptr) { - return 0; - } - - int fd = jniGetFDFromFileDescriptor(env, file_descriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } - - unique_fd dup_fd(::dup(fd)); - if (dup_fd < 0) { - jniThrowIOException(env, errno); - return 0; - } - - std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), - friendly_name_utf8.c_str(), - system, force_shared_lib); - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", - friendly_name_utf8.c_str(), dup_fd.get()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast<jlong>(apk_assets.release()); -} - -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast<ApkAssets*>(ptr); -} - -static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); - return env->NewStringUTF(apk_assets->GetPath().c_str()); -} - -static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); - return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool()); -} - -static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); - (void)apk_assets; - return JNI_TRUE; -} - -static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { - ScopedUtfChars path_utf8(env, file_name); - if (path_utf8.c_str() == nullptr) { - return 0; - } - - const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); - std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(), - Asset::AccessMode::ACCESS_RANDOM); - if (asset == nullptr) { - jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); - return 0; - } - - // DynamicRefTable is only needed when looking up resource references. Opening an XML file - // directly from an ApkAssets has no notion of proper resource references. - std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast<jlong>(xml_tree.release()); -} - -// JNI registration. -static const JNINativeMethod gApkAssetsMethods[] = { - {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, - {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", - (void*)NativeLoadFromFd}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, - {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, - {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, - {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, -}; - -int register_android_content_res_ApkAssets(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, - arraysize(gApkAssetsMethods)); -} - -} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 403937be7088..683b4c490ec3 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1437 +1,1851 @@ -/* - * Copyright 2006, 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. - */ +/* //device/libs/android_runtime/android_util_AssetManager.cpp +** +** Copyright 2006, 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 "asset" +#include <android_runtime/android_util_AssetManager.h> + #include <inttypes.h> #include <linux/capability.h> #include <stdio.h> -#include <sys/stat.h> -#include <sys/system_properties.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/stat.h> +#include <sys/system_properties.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM -#include "android-base/logging.h" -#include "android-base/properties.h" -#include "android-base/stringprintf.h" -#include "android_runtime/android_util_AssetManager.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" -#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" -#include "nativehelper/ScopedPrimitiveArray.h" -#include "nativehelper/ScopedStringChars.h" -#include "nativehelper/ScopedUtfChars.h" +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedUtfChars.h> #include "utils/Log.h" -#include "utils/String8.h" #include "utils/misc.h" +#include "utils/String8.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); -using ::android::base::StringPrintf; namespace android { +static const bool kThrowOnBadId = false; + // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t { - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t +{ + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t { - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t +{ + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t { - jfieldID mObject; +static struct assetmanager_offsets_t +{ + jfieldID mObject; } gAssetManagerOffsets; -static struct { - jfieldID native_ptr; -} gApkAssetsFields; - -static struct sparsearray_offsets_t { - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct sparsearray_offsets_t +{ + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t { - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t +{ + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = nullptr; +jclass g_stringClass = NULL; // ---------------------------------------------------------------------------- -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1; -} - -constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { - return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie; +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + static_cast<jint>(table->getTableCookie(block))); + env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); + env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); + env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, + typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } + return block; } // This is called by zygote (running as user root) as part of preloadResources. -static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { - switch (pid_t pid = fork()) { - case -1: - PLOG(ERROR) << "failed to fork for idmap"; - break; - - // child - case 0: { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capget"; - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capset"; - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setgid"; - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setuid"; - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, 0, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR. - std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, - ""); - if (!overlay_theme_path.empty()) { - overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; - if (stat(overlay_theme_path.c_str(), &st) == 0) { - argv[argc++] = overlay_theme_path.c_str(); - } - } - - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - PLOG(ERROR) << "failed to execv for idmap"; - exit(1); // should never get here - } else { - exit(0); - } - } break; - - // parent - default: - waitpid(pid, nullptr, 0); - break; - } +static void verifySystemIdmaps() +{ + pid_t pid; + char system_id[10]; + + snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); + + switch (pid = fork()) { + case -1: + ALOGE("failed to fork for idmap: %s", strerror(errno)); + break; + case 0: // child + { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + ALOGE("capget: %s\n", strerror(errno)); + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + ALOGE("capset: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + ALOGE("setgid: %s\n", strerror(errno)); + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + ALOGE("setuid: %s\n", strerror(errno)); + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, NULL, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR. + char subdir[PROP_VALUE_MAX]; + int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); + if (len > 0) { + String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; + if (stat(overlayPath.string(), &st) == 0) { + argv[argc++] = overlayPath.string(); + } + } + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + ALOGE("failed to execv for idmap: %s", strerror(errno)); + exit(1); // should never get here + } else { + exit(0); + } + } + break; + default: // parent + waitpid(pid, NULL, 0); + break; + } } -static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, - uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(cookie)); - env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); - env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); - if (config != nullptr) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); - } - return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie)); -} // ---------------------------------------------------------------------------- -// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. -struct GuardedAssetManager : public ::AAssetManager { - Guarded<AssetManager2> guarded_assetmanager; -}; - -::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); - ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); - if (am == nullptr) { +// this guy is exported to other jni routines +AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) +{ + jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); + AssetManager* am = reinterpret_cast<AssetManager*>(amHandle); + if (am != NULL) { + return am; + } jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return nullptr; - } - return am; + return NULL; } -Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { - if (assetmanager == nullptr) { - return nullptr; - } - return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager; -} +static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, + jstring fileName, jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); -Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); + return reinterpret_cast<jlong>(a); } -static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) { - return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr)); +static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) +{ + off64_t startOffset, length; + int fd = a->openFileDescriptor(&startOffset, &length); + delete a; + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably compressed"); + return NULL; + } + + jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); + if (offsets == NULL) { + close(fd); + return NULL; + } + + offsets[0] = startOffset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); + + jobject fileDesc = jniCreateFileDescriptor(env, fd); + if (fileDesc == NULL) { + close(fd); + return NULL; + } + + return newParcelFileDescriptor(env, fileDesc); } -static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset, - jlongArray out_offsets) { - off64_t start_offset, length; - int fd = asset->openFileDescriptor(&start_offset, &length); - asset.reset(); - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably " - "compressed"); - return nullptr; - } - - jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0)); - if (offsets == nullptr) { - close(fd); - return nullptr; - } - - offsets[0] = start_offset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); - - jobject file_desc = jniCreateFileDescriptor(env, fd); - if (file_desc == nullptr) { - close(fd); - return nullptr; - } - return newParcelFileDescriptor(env, file_desc); +static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, + jstring fileName, jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { - return Asset::getGlobalCount(); +static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), + (Asset::AccessMode)mode) + : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); + + return reinterpret_cast<jlong>(a); } -static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return nullptr; - } - return env->NewStringUTF(alloc.string()); +static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { - // TODO(adamlesinski): Switch to AssetManager2. - return AssetManager::getGlobalCount(); +static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + AssetDir* dir = am->openDir(fileName8.c_str()); + + if (dir == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + size_t N = dir->getFileCount(); + + jobjectArray array = env->NewObjectArray(dir->getFileCount(), + g_stringClass, NULL); + if (array == NULL) { + delete dir; + return NULL; + } + + for (size_t i=0; i<N; i++) { + const String8& name = dir->getFileName(i); + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + delete dir; + return NULL; + } + env->SetObjectArrayElement(array, i, str); + env->DeleteLocalRef(str); + } + + delete dir; + + return array; } -static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { - // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and - // AssetManager2 in a contiguous block (GuardedAssetManager). - return reinterpret_cast<jlong>(new GuardedAssetManager()); +static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); + + //printf("Destroying Asset Stream: %p\n", a); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return; + } + + delete a; } -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast<GuardedAssetManager*>(ptr); +static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + uint8_t b; + ssize_t res = a->read(&b, 1); + return res == 1 ? b : -1; } -static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jobjectArray apk_assets_array, jboolean invalidate_caches) { - const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); - std::vector<const ApkAssets*> apk_assets; - apk_assets.reserve(apk_assets_len); - for (jsize i = 0; i < apk_assets_len; i++) { - jobject obj = env->GetObjectArrayElement(apk_assets_array, i); - if (obj == nullptr) { - std::string msg = StringPrintf("ApkAssets at index %d is null", i); - jniThrowNullPointerException(env, msg.c_str()); - return; +static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, jbyteArray bArray, + jint off, jint len) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); + + if (a == NULL || bArray == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); - if (env->ExceptionCheck()) { - return; + if (len == 0) { + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; } - apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr)); - } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetApkAssets(apk_assets, invalidate_caches); + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ssize_t res = a->read(b+off, len); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (res > 0) return static_cast<jint>(res); + + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + } + return -1; } -static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, - jstring locale, jint orientation, jint touchscreen, jint density, - jint keyboard, jint keyboard_hidden, jint navigation, - jint screen_width, jint screen_height, - jint smallest_screen_width_dp, jint screen_width_dp, - jint screen_height_dp, jint screen_layout, jint ui_mode, - jint color_mode, jint major_version) { - ResTable_config configuration; - memset(&configuration, 0, sizeof(configuration)); - configuration.mcc = static_cast<uint16_t>(mcc); - configuration.mnc = static_cast<uint16_t>(mnc); - configuration.orientation = static_cast<uint8_t>(orientation); - configuration.touchscreen = static_cast<uint8_t>(touchscreen); - configuration.density = static_cast<uint16_t>(density); - configuration.keyboard = static_cast<uint8_t>(keyboard); - configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden); - configuration.navigation = static_cast<uint8_t>(navigation); - configuration.screenWidth = static_cast<uint16_t>(screen_width); - configuration.screenHeight = static_cast<uint16_t>(screen_height); - configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp); - configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp); - configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp); - configuration.screenLayout = static_cast<uint8_t>(screen_layout); - configuration.uiMode = static_cast<uint8_t>(ui_mode); - configuration.colorMode = static_cast<uint8_t>(color_mode); - configuration.sdkVersion = static_cast<uint16_t>(major_version); - - if (locale != nullptr) { - ScopedUtfChars locale_utf8(env, locale); - CHECK(locale_utf8.c_str() != nullptr); - configuration.setBcp47Locale(locale_utf8.c_str()); - } - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - configuration.screenLayout2 = - static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetConfiguration(configuration); +static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, + jlong offset, jint whence) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + return a->seek( + offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); } -static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); - jobject sparse_array = - env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } - if (sparse_array == nullptr) { - // An exception is pending. - return nullptr; - } + return a->getLength(); +} - assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { - jstring jpackage_name = env->NewStringUTF(package_name.c_str()); - if (jpackage_name == nullptr) { - // An exception is pending. - return; +static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast<Asset*>(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id), - jpackage_name); - }); - return sparse_array; + return a->getRemainingLength(); } -static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { - ScopedUtfChars path_utf8(env, path); - if (path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - std::vector<std::string> all_file_paths; - { - StringPiece normalized_path = path_utf8.c_str(); - if (normalized_path.data()[0] == '/') { - normalized_path = normalized_path.substr(1); - } - std::string root_path = StringPrintf("assets/%s", normalized_path.data()); - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - for (const ApkAssets* assets : assetmanager->GetApkAssets()) { - assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { - if (type == FileType::kFileTypeRegular) { - all_file_paths.push_back(file_path.to_string()); - } - }); +static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, + jstring path, jboolean appAsLib) +{ + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + return 0; } - } - jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } - jsize index = 0; - for (const std::string& file_path : all_file_paths) { - jstring java_string = env->NewStringUTF(file_path.c_str()); + int32_t cookie; + bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; + return (res) ? static_cast<jint>(cookie) : 0; +} + +static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, + jstring idmapPath) +{ + ScopedUtfChars idmapPath8(env, idmapPath); + if (idmapPath8.c_str() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } - env->SetObjectArrayElement(array, index++, java_string); + int32_t cookie; + bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - return array; + return (res) ? (jint)cookie : 0; } -static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jint access_mode) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr<Asset> asset = - assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode)); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast<jlong>(asset.release()); +static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jstring debugPathName, + jboolean appAsLib) +{ + ScopedUtfChars debugPathName8(env, debugPathName); + + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + int dupfd = ::dup(fd); + if (dupfd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + int32_t cookie; + bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast<jint>(cookie) : 0; } -static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jlongArray out_offsets) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_TRUE; + } + return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; } -static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jint access_mode) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr<Asset> asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, - static_cast<Asset::AccessMode>(access_mode)); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), - static_cast<Asset::AccessMode>(access_mode)); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast<jlong>(asset.release()); +static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) +{ + Vector<String8> locales; + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + am->getLocales(&locales, includeSystemLocales); + + const int N = locales.size(); + + jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; i<N; i++) { + jstring str = env->NewStringUTF(locales[i].string()); + if (str == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, i, str); + env->DeleteLocalRef(str); + } + + return result; } -static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jlongArray out_offsets) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr<Asset> asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); } -static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr<Asset> asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - - // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast<jlong>(xml_tree.release()); +static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); } -static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jshort density, jobject typed_value, - jboolean resolve_references) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/, - static_cast<uint16_t>(density), &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = static_cast<uint32_t>(resid); - if (resolve_references) { - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); +static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = env->NewObject(gConfigurationOffsets.classObject, + gConfigurationOffsets.constructor); + if (result == NULL) { + return NULL; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + + return result; } -static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jint bag_entry_id, jobject typed_value) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t type_spec_flags = bag->type_spec_flags; - ApkAssetsCookie cookie = kInvalidCookie; - const Res_value* bag_value = nullptr; - for (const ResolvedBag::Entry& entry : bag) { - if (entry.key == static_cast<uint32_t>(bag_entry_id)) { - cookie = entry.cookie; - bag_value = &entry.value; - - // Keep searching (the old implementation did that). - } - } - - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - Res_value value = *bag_value; - uint32_t ref = static_cast<uint32_t>(resid); - ResTable_config selected_config; - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); +static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, + const Vector<ResTable_config>& configs) { + const int N = configs.size(); + jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; i<N; i++) { + jobject config = constructConfigurationObject(env, configs[i]); + if (config == NULL) { + env->DeleteLocalRef(result); + return NULL; + } + + env->SetObjectArrayElement(result, i, config); + env->DeleteLocalRef(config); + } + + return result; } -static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (env->ExceptionCheck()) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - jint attr_resid = bag->entries[i].key; - env->SetIntArrayRegion(array, i, 1, &attr_resid); - } - return array; +static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + const ResTable& res(am->getResources()); + Vector<ResTable_config> configs; + res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); + + return getSizeConfigurationsInternal(env, configs); } -static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return nullptr; - } - - jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - - // Resolve any references to their final value. - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return nullptr; - } - - if (value.dataType == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; - const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); - - jstring java_string = nullptr; - size_t str_len; - const char* str_utf8 = pool->string8At(value.data, &str_len); - if (str_utf8 != nullptr) { - java_string = env->NewStringUTF(str_utf8); - } else { - const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); - java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len); - } - - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; - } - - env->SetObjectArrayElement(array, i, java_string); - - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - } - return array; +static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, + jint mcc, jint mnc, + jstring locale, jint orientation, + jint touchscreen, jint density, + jint keyboard, jint keyboardHidden, + jint navigation, + jint screenWidth, jint screenHeight, + jint smallestScreenWidthDp, + jint screenWidthDp, jint screenHeightDp, + jint screenLayout, jint uiMode, + jint colorMode, jint sdkVersion) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + config.mcc = (uint16_t)mcc; + config.mnc = (uint16_t)mnc; + config.orientation = (uint8_t)orientation; + config.touchscreen = (uint8_t)touchscreen; + config.density = (uint16_t)density; + config.keyboard = (uint8_t)keyboard; + config.inputFlags = (uint8_t)keyboardHidden; + config.navigation = (uint8_t)navigation; + config.screenWidth = (uint16_t)screenWidth; + config.screenHeight = (uint16_t)screenHeight; + config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; + config.screenWidthDp = (uint16_t)screenWidthDp; + config.screenHeightDp = (uint16_t)screenHeightDp; + config.screenLayout = (uint8_t)screenLayout; + config.uiMode = (uint8_t)uiMode; + config.colorMode = (uint8_t)colorMode; + config.sdkVersion = (uint16_t)sdkVersion; + config.minorVersion = 0; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + config.screenLayout2 = + (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + am->setConfiguration(config, locale8); + + if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); } -static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count * 2); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - jint string_index = -1; - if (value.dataType == Res_value::TYPE_STRING) { - string_index = static_cast<jint>(value.data); - } - - buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); - buffer[(i * 2) + 1] = string_index; - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, + jstring name, + jstring defType, + jstring defPackage) +{ + ScopedStringChars name16(env, name); + if (name16.get() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType) + ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL)) + : NULL; + jsize defTypeLen = defType + ? env->GetStringLength(defType) : 0; + const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage) + ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage, + NULL)) + : NULL; + jsize defPackageLen = defPackage + ? env->GetStringLength(defPackage) : 0; + + jint ident = am->getResources().identifierForName( + reinterpret_cast<const char16_t*>(name16.get()), name16.size(), + defType16, defTypeLen, defPackage16, defPackageLen); + + if (defPackage16) { + env->ReleaseStringChars(defPackage, + reinterpret_cast<const jchar*>(defPackage16)); + } + if (defType16) { + env->ReleaseStringChars(defType, + reinterpret_cast<const jchar*>(defType16)); + } + + return ident; } -static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast<jint>(value.data); - } - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + String16 str; + if (name.package != NULL) { + str.setTo(name.package, name.packageLen); + } + if (name.type8 != NULL || name.type != NULL) { + if (str.size() > 0) { + char16_t div = ':'; + str.append(&div, 1); + } + if (name.type8 != NULL) { + str.append(String16(name.type8, name.typeLen)); + } else { + str.append(name.type, name.typeLen); + } + } + if (name.name8 != NULL || name.name != NULL) { + if (str.size() > 0) { + char16_t div = '/'; + str.append(&div, 1); + } + if (name.name8 != NULL) { + str.append(String16(name.name8, name.nameLen)); + } else { + str.append(name.name, name.nameLen); + } + } + + return env->NewString((const jchar*)str.string(), str.size()); } -static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return -1; - } - return static_cast<jint>(bag->entry_count); +static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.package != NULL) { + return env->NewString((const jchar*)name.package, name.packageLen); + } + + return NULL; } -static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jintArray out_data) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (bag == nullptr) { - return -1; - } +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } - const jsize out_data_length = env->GetArrayLength(out_data); - if (env->ExceptionCheck()) { - return -1; - } + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } - if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); - return -1; - } + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } - jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr)); - if (buffer == nullptr) { - return -1; - } - - jint* cursor = buffer; - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - selected_config.density = 0; - uint32_t flags = bag->type_spec_flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); - return -1; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - cursor[STYLE_TYPE] = static_cast<jint>(value.dataType); - cursor[STYLE_DATA] = static_cast<jint>(value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags); - cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density); - cursor += STYLE_NUM_ENTRIES; - } - env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); - return static_cast<jint>(bag->entry_count); + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; } -static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, - jstring def_type, jstring def_package) { - ScopedUtfChars name_utf8(env, name); - if (name_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - std::string type; - if (def_type != nullptr) { - ScopedUtfChars type_utf8(env, def_type); - CHECK(type_utf8.c_str() != nullptr); - type = type_utf8.c_str(); - } - - std::string package; - if (def_package != nullptr) { - ScopedUtfChars package_utf8(env, def_package); - CHECK(package_utf8.c_str() != nullptr); - package = package_utf8.c_str(); - } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); +static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.name8 != NULL) { + return env->NewStringUTF(name.name8); + } + + if (name.name != NULL) { + return env->NewString((const jchar*)name.name, name.nameLen); + } + + return NULL; } -static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { - return nullptr; - } +static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, + jint ident, + jshort density, + jobject outValue, + jboolean resolve) +{ + if (outValue == NULL) { + jniThrowNullPointerException(env, "outValue"); + return 0; + } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + Res_value value; + ResTable_config config; + uint32_t typeSpecFlags; + ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); + } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } + return static_cast<jint>(block); +} - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; +static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, + jint ident, jint bagEntryId, + jobject outValue, jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } + const ResTable& res(am->getResources()); + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + ssize_t block = -1; + Res_value value; - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + const ResTable::bag_entry* entry = NULL; + uint32_t typeSpecFlags; + ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + + for (ssize_t i=0; i<entryCount; i++) { + if (((uint32_t)bagEntryId) == entry->map.name.ident) { + block = entry->stringBlock; + value = entry->map.value; + } + entry++; } - } - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; + res.unlock(); + + if (block < 0) { + return static_cast<jint>(block); } - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); } - } - return env->NewStringUTF(result.c_str()); + + return static_cast<jint>(block); } -static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { - return nullptr; - } - - if (name.package != nullptr) { - return env->NewStringUTF(name.package); - } - return nullptr; +static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return am->getResources().getTableCount(); } -static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { - return nullptr; - } - - if (name.type != nullptr) { - return env->NewStringUTF(name.type); - } else if (name.type16 != nullptr) { - return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len); - } - return nullptr; +static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, + jint block) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block)); } -static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { - return nullptr; - } - - if (name.entry != nullptr) { - return env->NewStringUTF(name.entry); - } else if (name.entry16 != nullptr) { - return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len); - } - return nullptr; +static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, + jint cookie) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + String8 name(am->getAssetPath(static_cast<int32_t>(cookie))); + if (name.length() == 0) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); + return NULL; + } + jstring str = env->NewStringUTF(name.string()); + return str; } -static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, - jboolean exclude_system) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::set<std::string> locales = - assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); - - jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - size_t idx = 0; - for (const std::string& locale : locales) { - jstring java_string = env->NewStringUTF(locale.c_str()); - if (java_string == nullptr) { - return nullptr; - } - env->SetObjectArrayElement(array, idx++, java_string); - env->DeleteLocalRef(java_string); - } - return array; +static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const ResTable& res = am->getResources(); + + jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, + gSparseArrayOffsets.constructor); + const size_t N = res.getBasePackageCount(); + for (size_t i = 0; i < N; i++) { + const String16 name = res.getBasePackageName(i); + env->CallVoidMethod( + sparseArray, gSparseArrayOffsets.put, + static_cast<jint>(res.getBasePackageId(i)), + env->NewString(reinterpret_cast<const jchar*>(name.string()), + name.size())); + } + return sparseArray; } -static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = - env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); - if (result == nullptr) { - return nullptr; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - return result; +static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources())); } -static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - std::set<ResTable_config> configurations = - assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + delete theme; +} - jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); - if (array == nullptr) { - return nullptr; - } +static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, + jlong themeHandle, + jint styleRes, + jboolean force) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + theme->applyStyle(styleRes, force ? true : false); +} - size_t idx = 0; - for (const ResTable_config& configuration : configurations) { - jobject java_configuration = ConstructConfigurationObject(env, configuration); - if (java_configuration == nullptr) { - return nullptr; +static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, + jlong destHandle, jlong srcHandle) +{ + ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle); + ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle); + dest->setTo(*src); +} + +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + theme->clear(); +} + +static jint android_content_AssetManager_loadThemeAttributeValue( + JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + const ResTable& res(theme->getResTable()); + + Res_value value; + // XXX value could be different in different configs! + uint32_t typeSpecFlags = 0; + ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); + uint32_t ref = 0; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} - env->SetObjectArrayElement(array, idx++, java_configuration); - env->DeleteLocalRef(java_configuration); - } - return array; +static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + return theme->getChangingConfigurations(); } -static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, - jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); - uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr); - uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr); - - jsize attrs_len = env->GetArrayLength(java_attrs); - jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return; - } - - ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr), - static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len, - out_values, out_indices); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); +static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, + jlong themeHandle, jint pri, + jstring tag, jstring prefix) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + const ResTable& res(theme->getResTable()); + (void)res; + + // XXX Need to use params. + theme->dumpToLog(); } -static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jintArray java_values, - jintArray java_attrs, jintArray out_java_values, - jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* values = nullptr; - jsize values_len = 0; - if (java_values != nullptr) { - values_len = env->GetArrayLength(java_values); - values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr)); - if (values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - } - - jint* out_values = - reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); +static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, + jlong themeToken, + jint defStyleAttr, + jint defStyleRes, + jintArray inValues, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (themeToken == 0) { + jniThrowNullPointerException(env, "theme token"); return JNI_FALSE; - } - } - } - - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - bool result = ResolveAttrs( - theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid), - reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs), - attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices)); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } -static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jlong xml_parser_ptr, jintArray java_attrs, - jintArray out_java_values, jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* out_values = - reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); return JNI_FALSE; - } } - } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } - bool result = RetrieveAttributes(assetmanager.get(), xml_parser, - reinterpret_cast<uint32_t*>(attrs), attrs_len, - reinterpret_cast<uint32_t*>(out_values), - reinterpret_cast<uint32_t*>(out_indices)); + jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); + const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } -static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - return reinterpret_cast<jlong>(assetmanager->NewTheme().release()); + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken); + bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, + (uint32_t*) srcValues, NSV, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - delete reinterpret_cast<Theme*>(theme_ptr); +static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, + jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, + jlong outValuesAddress, jlong outIndicesAddress) { + jint* attrs = env->GetIntArrayElements(attrsObj, 0); + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken); + ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken); + uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress)); + uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress)); + ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, + reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices); + env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); } -static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jboolean force) { - // AssetManager is accessed via the theme, so grab an explicit lock here. - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - theme->ApplyStyle(static_cast<uint32_t>(resid), force); - - // TODO(adamlesinski): Consider surfacing exception when result is failure. - // CTS currently expects no exceptions from this method. - // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); - // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); +static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jlong xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0) { + jniThrowNullPointerException(env, "xmlParserToken"); + return JNI_FALSE; + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + bool result = RetrieveAttributes(&res, xmlParser, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, - jlong src_theme_ptr) { - Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr); - Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr); - if (!dst_theme->SetTo(*src_theme)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Themes are from different AssetManagers"); - } +static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, + jint id) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + res.lock(); + const ResTable::bag_entry* defStyleEnt = NULL; + ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); + res.unlock(); + + return static_cast<jint>(bagOff); } -static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - reinterpret_cast<Theme*>(theme_ptr)->Clear(); +static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, + jint id, + jintArray outValues) +{ + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResTable_config config; + Res_value value; + ssize_t block; + + const jsize NV = env->GetArrayLength(outValues); + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; + if (dest == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return JNI_FALSE; + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + const ResTable::bag_entry* arrayEnt = NULL; + uint32_t arrayTypeSetFlags = 0; + ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); + const ResTable::bag_entry* endArrayEnt = arrayEnt + + (bagOff >= 0 ? bagOff : 0); + + int i = 0; + uint32_t typeSetFlags; + while (i < NV && arrayEnt < endArrayEnt) { + block = arrayEnt->stringBlock; + typeSetFlags = arrayTypeSetFlags; + config.density = 0; + value = arrayEnt->map.value; + + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, + &typeSetFlags, &config); + if (kThrowOnBadId) { + if (newBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return JNI_FALSE; + } + } + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block)); + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + dest[STYLE_DENSITY] = config.density; + dest += STYLE_NUM_ENTRIES; + i+= STYLE_NUM_ENTRIES; + arrayEnt++; + } + + i /= STYLE_NUM_ENTRIES; + + res.unlock(); + + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + + return i; } -static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jobject typed_value, - jboolean resolve_references) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = 0u; - if (resolve_references) { - ResTable_config selected_config; - cookie = - theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); +static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return 0; + } + + int32_t assetCookie = static_cast<int32_t>(cookie); + Asset* a = assetCookie + ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return 0; + } + + const DynamicRefTable* dynamicRefTable = + am->getResources().getDynamicRefTableForCookie(assetCookie); + ResXMLTree* block = new ResXMLTree(dynamicRefTable); + status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); + a->close(); + delete a; + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + + return reinterpret_cast<jlong>(block); } -static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint priority, jstring tag, jstring prefix) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - (void) theme; - (void) priority; - (void) tag; - (void) prefix; +static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N * 2); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) { + jint stringIndex = -1; + jint stringBlock = 0; + value = bag->map.value; + + // Take care of resolving the found resource to its final value. + stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + stringIndex = value.data; + } + + if (kThrowOnBadId) { + if (stringBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + + //todo: It might be faster to allocate a C array to contain + // the blocknums and indices, put them in there and then + // do just one SetIntArrayRegion() + env->SetIntArrayRegion(array, j, 1, &stringBlock); + env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); + j = j + 2; + } + res.unlockBag(startOfBag); + return array; } -static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, - jlong theme_ptr) { - Theme* theme = reinterpret_cast<Theme*>(theme_ptr); - return static_cast<jint>(theme->GetChangingConfigurations()); +static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + size_t strLen = 0; + for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { + value = bag->map.value; + jstring str = NULL; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + const ResStringPool* pool = res.getTableStringBlock(block); + const char* str8 = pool->string8At(value.data, &strLen); + if (str8 != NULL) { + str = env->NewStringUTF(str8); + } else { + const char16_t* str16 = pool->stringAt(value.data, &strLen); + str = env->NewString(reinterpret_cast<const jchar*>(str16), + strLen); + } + + // If one of our NewString{UTF} calls failed due to memory, an + // exception will be pending. + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + env->SetObjectArrayElement(array, i, str); + + // str is not NULL at that point, otherwise ExceptionCheck would have been true. + // If we have a large amount of strings in our array, we might + // overflow the local reference table of the VM. + env->DeleteLocalRef(str); + } + } + res.unlockBag(startOfBag); + return array; } -static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - delete reinterpret_cast<Asset*>(asset_ptr); +static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { + value = bag->map.value; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType >= Res_value::TYPE_FIRST_INT + && value.dataType <= Res_value::TYPE_LAST_INT) { + int intVal = value.data; + env->SetIntArrayRegion(array, i, 1, &intVal); + } + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - uint8_t b; - ssize_t res = asset->read(&b, sizeof(b)); - return res == sizeof(b) ? static_cast<jint>(b) : -1; +static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, + jint styleId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(styleId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { + int resourceId = bag->map.name.ident; + env->SetIntArrayRegion(array, i, 1, &resourceId); + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, - jint offset, jint len) { - if (len == 0) { - return 0; - } +static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) +{ + if (isSystem) { + verifySystemIdmaps(); + } + AssetManager* am = new AssetManager(); + if (am == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return; + } - jsize buffer_len = env->GetArrayLength(java_buffer); - if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || - offset > buffer_len - len) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } + am->addDefaultAssets(); - ScopedByteArrayRW byte_array(env, java_buffer); - if (byte_array.get() == nullptr) { - return -1; - } + ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am)); +} - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - ssize_t res = asset->read(byte_array.get() + offset, len); - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - return -1; - } - return res > 0 ? static_cast<jint>(res) : -1; +static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) +{ + AssetManager* am = (AssetManager*) + (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); + ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); + if (am != NULL) { + delete am; + env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); + } } -static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, - jint whence) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - return static_cast<jlong>(asset->seek( - static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); +static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); } -static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - return static_cast<jlong>(asset->getLength()); +static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) +{ + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return NULL; + } + + jstring str = env->NewStringUTF(alloc.string()); + return str; } -static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast<Asset*>(asset_ptr); - return static_cast<jlong>(asset->getRemainingLength()); +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); } // ---------------------------------------------------------------------------- -// JNI registration. +/* + * JNI registration. + */ static const JNINativeMethod gAssetManagerMethods[] = { - // AssetManager setup methods. - {"nativeCreate", "()J", (void*)NativeCreate}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, - {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*)NativeSetConfiguration}, - {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", - (void*)NativeGetAssignedPackageIdentifiers}, - - // AssetManager file methods. - {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, - {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, - {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenAssetFd}, - {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, - {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenNonAssetFd}, - {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, - - // AssetManager resource methods. - {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, - {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", - (void*)NativeGetResourceBagValue}, - {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, - {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", - (void*)NativeGetResourceStringArray}, - {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, - {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, - {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, - {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, - - // AssetManager resource name/ID methods. - {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)NativeGetResourceIdentifier}, - {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, - {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, - {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, - {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, - {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, - {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeConfigurations}, - - // Style attribute related methods. - {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, - {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, - {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, - - // Theme related methods. - {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, - {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, - {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, - {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, - {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", - (void*)NativeThemeGetAttributeValue}, - {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, - {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, - - // AssetInputStream methods. - {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, - {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, - {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, - {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, - {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, - {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, - - // System/idmap related methods. - {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, - - // Global management/debug methods. - {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, - {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, - {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, + /* name, signature, funcPtr */ + + // Basic asset stuff. + { "openAsset", "(Ljava/lang/String;I)J", + (void*) android_content_AssetManager_openAsset }, + { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openAssetFd }, + { "openNonAssetNative", "(ILjava/lang/String;I)J", + (void*) android_content_AssetManager_openNonAssetNative }, + { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openNonAssetFdNative }, + { "list", "(Ljava/lang/String;)[Ljava/lang/String;", + (void*) android_content_AssetManager_list }, + { "destroyAsset", "(J)V", + (void*) android_content_AssetManager_destroyAsset }, + { "readAssetChar", "(J)I", + (void*) android_content_AssetManager_readAssetChar }, + { "readAsset", "(J[BII)I", + (void*) android_content_AssetManager_readAsset }, + { "seekAsset", "(JJI)J", + (void*) android_content_AssetManager_seekAsset }, + { "getAssetLength", "(J)J", + (void*) android_content_AssetManager_getAssetLength }, + { "getAssetRemainingLength", "(J)J", + (void*) android_content_AssetManager_getAssetRemainingLength }, + { "addAssetPathNative", "(Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetPath }, + { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetFd }, + { "addOverlayPathNative", "(Ljava/lang/String;)I", + (void*) android_content_AssetManager_addOverlayPath }, + { "isUpToDate", "()Z", + (void*) android_content_AssetManager_isUpToDate }, + + // Resources. + { "getLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getLocales }, + { "getNonSystemLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getNonSystemLocales }, + { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", + (void*) android_content_AssetManager_getSizeConfigurations }, + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*) android_content_AssetManager_setConfiguration }, + { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*) android_content_AssetManager_getResourceIdentifier }, + { "getResourceName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceName }, + { "getResourcePackageName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourcePackageName }, + { "getResourceTypeName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceTypeName }, + { "getResourceEntryName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceEntryName }, + { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceValue }, + { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceBagValue }, + { "getStringBlockCount","()I", + (void*) android_content_AssetManager_getStringBlockCount }, + { "getNativeStringBlock","(I)J", + (void*) android_content_AssetManager_getNativeStringBlock }, + { "getCookieName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getCookieName }, + { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", + (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, + + // Themes. + { "newTheme", "()J", + (void*) android_content_AssetManager_newTheme }, + { "deleteTheme", "(J)V", + (void*) android_content_AssetManager_deleteTheme }, + { "applyThemeStyle", "(JIZ)V", + (void*) android_content_AssetManager_applyThemeStyle }, + { "copyTheme", "(JJ)V", + (void*) android_content_AssetManager_copyTheme }, + { "clearTheme", "(J)V", + (void*) android_content_AssetManager_clearTheme }, + { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "getThemeChangingConfigurations", "(J)I", + (void*) android_content_AssetManager_getThemeChangingConfigurations }, + { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", + (void*) android_content_AssetManager_dumpTheme }, + { "applyStyle","(JIIJ[IIJJ)V", + (void*) android_content_AssetManager_applyStyle }, + { "resolveAttrs","(JII[I[I[I[I)Z", + (void*) android_content_AssetManager_resolveAttrs }, + { "retrieveAttributes","(J[I[I[I)Z", + (void*) android_content_AssetManager_retrieveAttributes }, + { "getArraySize","(I)I", + (void*) android_content_AssetManager_getArraySize }, + { "retrieveArray","(I[I)I", + (void*) android_content_AssetManager_retrieveArray }, + + // XML files. + { "openXmlAssetNative", "(ILjava/lang/String;)J", + (void*) android_content_AssetManager_openXmlAssetNative }, + + // Arrays. + { "getArrayStringResource","(I)[Ljava/lang/String;", + (void*) android_content_AssetManager_getArrayStringResource }, + { "getArrayStringInfo","(I)[I", + (void*) android_content_AssetManager_getArrayStringInfo }, + { "getArrayIntResource","(I)[I", + (void*) android_content_AssetManager_getArrayIntResource }, + { "getStyleAttributes","(I)[I", + (void*) android_content_AssetManager_getStyleAttributes }, + + // Bookkeeping. + { "init", "(Z)V", + (void*) android_content_AssetManager_init }, + { "destroy", "()V", + (void*) android_content_AssetManager_destroy }, + { "getGlobalAssetCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, + { "getAssetAllocations", "()Ljava/lang/String;", + (void*) android_content_AssetManager_getAssetAllocations }, + { "getGlobalAssetManagerCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetManagerCount }, }; -int register_android_content_AssetManager(JNIEnv* env) { - jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); - gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); - - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = - GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = - GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = - GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V"); - gSparseArrayOffsets.put = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", + "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, + "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", + "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, + "<init>", "()V"); + gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", + "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, + "<init>", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 956b7249660f..3b7b14c58d0a 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -85,11 +85,15 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { static const char* kOverlayDir = "/system/vendor/overlay/"; static const char* kVendorOverlayDir = "/vendor/overlay"; static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/"; + static const char* kSystemProductOverlayDir = "/system/product/overlay/"; + static const char* kProductOverlayDir = "/product/overlay"; static const char* kApkSuffix = ".apk"; if ((android::base::StartsWith(path, kOverlayDir) || android::base::StartsWith(path, kOverlaySubdir) - || android::base::StartsWith(path, kVendorOverlayDir)) + || android::base::StartsWith(path, kVendorOverlayDir) + || android::base::StartsWith(path, kSystemProductOverlayDir) + || android::base::StartsWith(path, kProductOverlayDir)) && android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) { return true; diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 2c1e3579eb92..8dd933707a6a 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_RUNTIME_ASSETMANAGER_H -#define ANDROID_RUNTIME_ASSETMANAGER_H +#ifndef android_util_AssetManager_H +#define android_util_AssetManager_H -#include "androidfw/AssetManager2.h" -#include "androidfw/MutexGuard.h" +#include <androidfw/AssetManager.h> #include "jni.h" namespace android { -extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); +extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); -} // namespace android +} -#endif // ANDROID_RUNTIME_ASSETMANAGER_H +#endif diff --git a/core/proto/android/app/alarmmanager.proto b/core/proto/android/app/alarmmanager.proto index 789e3d6293c3..7ef08cae435a 100644 --- a/core/proto/android/app/alarmmanager.proto +++ b/core/proto/android/app/alarmmanager.proto @@ -17,6 +17,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/app/pendingintent.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; option java_multiple_files = true; @@ -47,6 +48,8 @@ message AlarmManagerProto { // An android.app.AlarmManager.AlarmClockInfo object. message AlarmClockInfoProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + // This value is UTC wall clock time in milliseconds, as returned by // System#currentTimeMillis() for example. optional int64 trigger_time_ms = 1; diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto index 2de2574ad401..5eb05be1a1d1 100644 --- a/core/proto/android/app/enums.proto +++ b/core/proto/android/app/enums.proto @@ -21,6 +21,19 @@ package android.app; option java_outer_classname = "AppProtoEnums"; option java_multiple_files = true; +// ActivityManagerInternal.java's APP_TRANSITION reasons. +enum AppTransitionReasonEnum { + APP_TRANSITION_REASON_UNKNOWN = 0; + // The transition was started because we drew the splash screen. + APP_TRANSITION_SPLASH_SCREEN = 1; + // The transition was started because we all app windows were drawn. + APP_TRANSITION_WINDOWS_DRAWN = 2; + // The transition was started because of a timeout. + APP_TRANSITION_TIMEOUT = 3; + // The transition was started because of a we drew a task snapshot. + APP_TRANSITION_SNAPSHOT = 4; +} + // ActivityManager.java PROCESS_STATEs enum ProcessStateEnum { // Unlike the ActivityManager PROCESS_STATE values, the ordering and numerical values diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto index 379a4ae8e2ac..c7e313aa5801 100644 --- a/core/proto/android/app/notification.proto +++ b/core/proto/android/app/notification.proto @@ -18,10 +18,10 @@ syntax = "proto2"; option java_package = "android.app"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; - package android.app; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** * An android.app.Notification object. * Deprecated fields are not included in the proto. diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto index 0388547e009f..337aa1c20c7a 100644 --- a/core/proto/android/app/notification_channel.proto +++ b/core/proto/android/app/notification_channel.proto @@ -21,19 +21,22 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/media/audioattributes.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; /** * An android.app.NotificationChannel object. */ message NotificationChannelProto { - optional string id = 1; - optional string name = 2; - optional string description = 3; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string id = 1 [ (android.privacy).dest = DEST_EXPLICIT ]; + optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ]; + optional string description = 3 [ (android.privacy).dest = DEST_EXPLICIT ]; optional int32 importance = 4; optional bool can_bypass_dnd = 5; // Default is VISIBILITY_NO_OVERRIDE (-1000). optional int32 lockscreen_visibility = 6; - optional string sound = 7; + optional string sound = 7 [ (android.privacy).dest = DEST_EXPLICIT ]; optional bool use_lights = 8; // Default is 0. optional int32 light_color = 9; @@ -46,7 +49,7 @@ message NotificationChannelProto { optional bool show_badge = 13; // Default is false. optional bool is_deleted = 14; - optional string group = 15; + optional string group = 15 [ (android.privacy).dest = DEST_EXPLICIT ]; optional android.media.AudioAttributesProto audio_attributes = 16; // If this is a blockable system notification channel. optional bool is_blockable_system = 17; diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto index 89a540f2012c..7b270d74f2de 100644 --- a/core/proto/android/app/notification_channel_group.proto +++ b/core/proto/android/app/notification_channel_group.proto @@ -21,11 +21,14 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/app/notification_channel.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; /** * An android.app.NotificationChannelGroup object. */ message NotificationChannelGroupProto { + option (.android.msg_privacy).dest = DEST_EXPLICIT; + optional string id = 1; optional string name = 2; optional string description = 3; diff --git a/core/proto/android/app/notificationmanager.proto b/core/proto/android/app/notificationmanager.proto index 7d774aeab551..e991688c218c 100644 --- a/core/proto/android/app/notificationmanager.proto +++ b/core/proto/android/app/notificationmanager.proto @@ -20,10 +20,14 @@ option java_multiple_files = true; package android.app; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** - * An android.app.NotificationMananger.Policy object. + * An android.app.NotificationManager.Policy object. */ message PolicyProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + enum Category { CATEGORY_UNKNOWN = 0; // Reminder notifications are prioritized. @@ -36,6 +40,11 @@ message PolicyProto { CALLS = 4; // Calls from repeat callers are prioritized. REPEAT_CALLERS = 5; + // Alarms are prioritized. + ALARMS = 6; + // Media, system, game (catch-all for non-never suppressible sounds) are + // prioritized. + MEDIA_SYSTEM_OTHER = 7; } repeated Category priority_categories = 1; diff --git a/core/proto/android/app/pendingintent.proto b/core/proto/android/app/pendingintent.proto index b562c0bb38e5..ab0d34eda5df 100644 --- a/core/proto/android/app/pendingintent.proto +++ b/core/proto/android/app/pendingintent.proto @@ -20,9 +20,13 @@ option java_multiple_files = true; package android.app; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** * An android.app.PendingIntent object. */ message PendingIntentProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional string target = 1; } diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto index 6967b69352bd..aeeef977d401 100644 --- a/core/proto/android/content/clipdata.proto +++ b/core/proto/android/content/clipdata.proto @@ -21,13 +21,18 @@ option java_multiple_files = true; import "frameworks/base/core/proto/android/content/clipdescription.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; // An android.content.ClipData object. message ClipDataProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional android.content.ClipDescriptionProto description = 1; // Custom dump of an android.graphics.Bitmap object. message Icon { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 width = 1; optional int32 height = 2; } @@ -35,6 +40,8 @@ message ClipDataProto { // An android.content.ClipData.Item object. message Item { + option (.android.msg_privacy).dest = DEST_EXPLICIT; + oneof data { string html_text = 1; string text = 2; diff --git a/core/proto/android/content/clipdescription.proto b/core/proto/android/content/clipdescription.proto index 40f4ad3ed724..bc0e9407faf8 100644 --- a/core/proto/android/content/clipdescription.proto +++ b/core/proto/android/content/clipdescription.proto @@ -20,11 +20,14 @@ package android.content; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; // An android.content.ClipDescription object. message ClipDescriptionProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + repeated string mime_types = 1; - optional string label = 2; + optional string label = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional android.os.PersistableBundleProto extras = 3; optional int64 timestamp_ms = 4; } diff --git a/core/proto/android/content/component_name.proto b/core/proto/android/content/component_name.proto index fc0c8c55c2b1..4e49cf2bfcd5 100644 --- a/core/proto/android/content/component_name.proto +++ b/core/proto/android/content/component_name.proto @@ -20,10 +20,14 @@ option java_multiple_files = true; package android.content; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** * An android.content.ComponentName object. */ message ComponentNameProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional string package_name = 1; optional string class_name = 2; } diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto index c25b46db4a71..1737b625a4aa 100644 --- a/core/proto/android/content/intent.proto +++ b/core/proto/android/content/intent.proto @@ -51,14 +51,14 @@ message IntentProto { optional string action = 1; repeated string categories = 2; - optional string data = 3 [ (.android.privacy).dest = DEST_EXPLICIT ]; + optional string data = 3 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional string type = 4; optional string flag = 5; optional string package = 6; optional string component = 7; optional string source_bounds = 8; - optional string clip_data = 9 [ (.android.privacy).dest = DEST_EXPLICIT ]; - optional string extras = 10; + optional string clip_data = 9 [ (.android.privacy).dest = DEST_EXPLICIT ]; + optional string extras = 10 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int32 content_user_hint = 11; optional string selector = 12; } diff --git a/core/proto/android/internal/locallog.proto b/core/proto/android/internal/locallog.proto index 51f6c1ce2fd7..73d1492c68e1 100644 --- a/core/proto/android/internal/locallog.proto +++ b/core/proto/android/internal/locallog.proto @@ -19,6 +19,10 @@ package com.android.internal.util; option java_multiple_files = true; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + message LocalLogProto { + option (.android.msg_privacy).dest = DEST_EXPLICIT; + repeated string lines = 1; } diff --git a/core/proto/android/net/network.proto b/core/proto/android/net/network.proto index 9c7ea5da8c84..e13ca9f682eb 100644 --- a/core/proto/android/net/network.proto +++ b/core/proto/android/net/network.proto @@ -19,9 +19,14 @@ option java_multiple_files = true; package android.net; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** * An android.net.Network object. */ message NetworkProto { - optional int32 net_id = 1; + // The netId is an implementation detail which might be changed in the + // future, or which alone (i.e. in the absence of some additional context) + // might not be sufficient to fully identify a Network. + optional int32 net_id = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ]; } diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto index e1c2af191f71..0338bf8f37b6 100644 --- a/core/proto/android/net/networkcapabilities.proto +++ b/core/proto/android/net/networkcapabilities.proto @@ -20,10 +20,14 @@ package android.net; option java_multiple_files = true; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + /** * An android.net.NetworkCapabilities object. */ message NetworkCapabilitiesProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + enum Transport { // Indicates this network uses a Cellular transport. TRANSPORT_CELLULAR = 0; @@ -118,7 +122,7 @@ message NetworkCapabilitiesProto { optional int32 link_up_bandwidth_kbps = 3; optional int32 link_down_bandwidth_kbps = 4; - optional string network_specifier = 5; + optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ]; // True if this object specifies a signal strength. optional bool can_report_signal_strength = 6; diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index 9884464c5a3a..d260b13a29eb 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -21,11 +21,14 @@ package android.net; option java_multiple_files = true; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; /** * An android.net.NetworkRequest object. */ message NetworkRequestProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + enum Type { TYPE_UNKNOWN = 0; // Only used by applications. When an application creates a diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index ce1d5c9bafd6..9f9fd05d8874 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -21,7 +21,7 @@ package android.os; import "frameworks/base/core/proto/android/app/jobparameters.proto"; import "frameworks/base/core/proto/android/os/powermanager.proto"; -import "frameworks/base/core/proto/android/telephony/signalstrength.proto"; +import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; message BatteryStatsProto { @@ -339,7 +339,7 @@ message SystemProto { message PhoneSignalStrength { option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional android.telephony.SignalStrengthProto.StrengthName name = 1; + optional android.telephony.SignalStrengthEnum name = 1; optional TimerProto total = 2; }; repeated PhoneSignalStrength phone_signal_strength = 16; diff --git a/core/proto/android/os/bundle.proto b/core/proto/android/os/bundle.proto index 69902816ae7d..5556936c3c78 100644 --- a/core/proto/android/os/bundle.proto +++ b/core/proto/android/os/bundle.proto @@ -19,10 +19,14 @@ package android.os; option java_multiple_files = true; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + // An android.os.Bundle object. message BundleProto { oneof data { - int32 parcelled_data_size = 1; - string map_data = 2; + int32 parcelled_data_size = 1 [ + (.android.privacy).dest = DEST_AUTOMATIC + ]; + string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; } } diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto new file mode 100644 index 000000000000..fe9b7ac01291 --- /dev/null +++ b/core/proto/android/os/enums.proto @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.os; + +option java_outer_classname = "OsProtoEnums"; +option java_multiple_files = true; + +// These constants are defined in hardware/interfaces/health/1.0/types.hal +// They are primarily used by android/os/BatteryManager.java. +enum BatteryHealthEnum { + BATTERY_HEALTH_INVALID = 0; + BATTERY_HEALTH_UNKNOWN = 1; + BATTERY_HEALTH_GOOD = 2; + BATTERY_HEALTH_OVERHEAT = 3; + BATTERY_HEALTH_DEAD = 4; + BATTERY_HEALTH_OVER_VOLTAGE = 5; + BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6; + BATTERY_HEALTH_COLD = 7; +} + +// Plug states, primarily used by android/os/BatteryManager.java. +enum BatteryPluggedStateEnum { + // Note that NONE is not in BatteryManager.java's constants. + BATTERY_PLUGGED_NONE = 0; + // Power source is an AC charger. + BATTERY_PLUGGED_AC = 1; + // Power source is a USB port. + BATTERY_PLUGGED_USB = 2; + // Power source is wireless. + BATTERY_PLUGGED_WIRELESS = 4; +} + +// These constants are defined in hardware/interfaces/health/1.0/types.hal +// They are primarily used by android/os/BatteryManager.java. +enum BatteryStatusEnum { + BATTERY_STATUS_INVALID = 0; + BATTERY_STATUS_UNKNOWN = 1; + BATTERY_STATUS_CHARGING = 2; + BATTERY_STATUS_DISCHARGING = 3; + BATTERY_STATUS_NOT_CHARGING = 4; + BATTERY_STATUS_FULL = 5; +} + +// Wakelock types, primarily used by android/os/PowerManager.java. +enum WakeLockLevelEnum { + // NOTE: Wake lock levels were previously defined as a bit field, except + // that only a few combinations were actually supported so the bit field + // was removed. This explains why the numbering scheme is so odd. If + // adding a new wake lock level, any unused value can be used. + + // Ensures that the CPU is running; the screen and keyboard backlight + // will be allowed to go off. + PARTIAL_WAKE_LOCK = 1; + + // Ensures that the screen is on (but may be dimmed); the keyboard + // backlight will be allowed to go off. If the user presses the power + // button, then the SCREEN_DIM_WAKE_LOCK will be implicitly released by + // the system, causing both the screen and the CPU to be turned off. + SCREEN_DIM_WAKE_LOCK = 6 [deprecated = true]; + + // Ensures that the screen is on at full brightness; the keyboard + // backlight will be allowed to go off. If the user presses the power + // button, then the SCREEN_BRIGHT_WAKE_LOCK will be implicitly released + // by the system, causing both the screen and the CPU to be turned off. + SCREEN_BRIGHT_WAKE_LOCK = 10 [deprecated = true]; + + // Ensures that the screen and keyboard backlight are on at full + // brightness. If the user presses the power button, then the + // FULL_WAKE_LOCK will be implicitly released by the system, causing + // both the screen and the CPU to be turned off. + FULL_WAKE_LOCK = 26 [deprecated = true]; + + // Turns the screen off when the proximity sensor activates. If the + // proximity sensor detects that an object is nearby, the screen turns + // off immediately. Shortly after the object moves away, the screen + // turns on again. + // A proximity wake lock does not prevent the device from falling asleep + // unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and + // SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake + // locks are held, then the device will fall asleep (and lock) as usual. + // However, the device will not fall asleep while the screen has been + // turned off by the proximity sensor because it effectively counts as + // ongoing user activity. + PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; + + // Put the screen in a low power state and allow the CPU to suspend if + // no other wake locks are held. This is used by the dream manager to + // implement doze mode. It currently has no effect unless the power + // manager is in the dozing state. + DOZE_WAKE_LOCK = 64; + + // Keep the device awake enough to allow drawing to occur. This is used + // by the window manager to allow applications to draw while the system + // is dozing. It currently has no effect unless the power manager is in + // the dozing state. + DRAW_WAKE_LOCK = 128; +} diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 828a55f45979..3ec6f057af88 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -123,7 +123,7 @@ message IncidentProto { // Linux services optional Procrank procrank = 2000 [ - (section).type = SECTION_COMMAND, + (section).type = SECTION_NONE, // disable procrank until figure out permission (section).args = "/system/xbin/procrank" ]; diff --git a/core/proto/android/os/persistablebundle.proto b/core/proto/android/os/persistablebundle.proto index 75ff78718cbc..712f87c9b147 100644 --- a/core/proto/android/os/persistablebundle.proto +++ b/core/proto/android/os/persistablebundle.proto @@ -19,10 +19,14 @@ package android.os; option java_multiple_files = true; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + // An android.os.PersistableBundle object. message PersistableBundleProto { oneof data { - int32 parcelled_data_size = 1; - string map_data = 2; + int32 parcelled_data_size = 1 [ + (.android.privacy).dest = DEST_AUTOMATIC + ]; + string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; } } diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto index 8e0a607ef5a5..78a28ed4a0e0 100644 --- a/core/proto/android/os/powermanager.proto +++ b/core/proto/android/os/powermanager.proto @@ -20,6 +20,7 @@ package android.os; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/worksource.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; message PowerManagerProto { /* User activity events in PowerManager.java. */ @@ -34,62 +35,10 @@ message PowerManagerProto { USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; } - enum WakeLockLevel { - // NOTE: Wake lock levels were previously defined as a bit field, except - // that only a few combinations were actually supported so the bit field - // was removed. This explains why the numbering scheme is so odd. If - // adding a new wake lock level, any unused value can be used. - - // Ensures that the CPU is running; the screen and keyboard backlight - // will be allowed to go off. - PARTIAL_WAKE_LOCK = 1; - - // Ensures that the screen is on (but may be dimmed); the keyboard - // backlight will be allowed to go off. If the user presses the power - // button, then the SCREEN_DIM_WAKE_LOCK will be implicitly released by - // the system, causing both the screen and the CPU to be turned off. - SCREEN_DIM_WAKE_LOCK = 6 [deprecated = true]; - - // Ensures that the screen is on at full brightness; the keyboard - // backlight will be allowed to go off. If the user presses the power - // button, then the SCREEN_BRIGHT_WAKE_LOCK will be implicitly released - // by the system, causing both the screen and the CPU to be turned off. - SCREEN_BRIGHT_WAKE_LOCK = 10 [deprecated = true]; - - // Ensures that the screen and keyboard backlight are on at full - // brightness. If the user presses the power button, then the - // FULL_WAKE_LOCK will be implicitly released by the system, causing - // both the screen and the CPU to be turned off. - FULL_WAKE_LOCK = 26 [deprecated = true]; - - // Turns the screen off when the proximity sensor activates. If the - // proximity sensor detects that an object is nearby, the screen turns - // off immediately. Shortly after the object moves away, the screen - // turns on again. - // A proximity wake lock does not prevent the device from falling asleep - // unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and - // SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake - // locks are held, then the device will fall asleep (and lock) as usual. - // However, the device will not fall asleep while the screen has been - // turned off by the proximity sensor because it effectively counts as - // ongoing user activity. - PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; - - // Put the screen in a low power state and allow the CPU to suspend if - // no other wake locks are held. This is used by the dream manager to - // implement doze mode. It currently has no effect unless the power - // manager is in the dozing state. - DOZE_WAKE_LOCK = 64; - - // Keep the device awake enough to allow drawing to occur. This is used - // by the window manager to allow applications to draw while the system - // is dozing. It currently has no effect unless the power manager is in - // the dozing state. - DRAW_WAKE_LOCK = 128; - } - // WakeLock class in android.os.PowerManager, it is the one used by sdk message WakeLockProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional string hex_string = 1; optional bool held = 2; optional int32 internal_count = 3; diff --git a/core/proto/android/os/ps.proto b/core/proto/android/os/ps.proto index 88c6609a92b6..9cce7274d000 100644 --- a/core/proto/android/os/ps.proto +++ b/core/proto/android/os/ps.proto @@ -20,10 +20,17 @@ package android.os; option java_multiple_files = true; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + message PsDumpProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + message Process { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + // Security label, most commonly used for SELinux context data. optional string label = 1; + // String representation of uid. optional string user = 2; // Process ID number. optional int32 pid = 3; diff --git a/core/proto/android/os/worksource.proto b/core/proto/android/os/worksource.proto index 2f8b2fbbaa2b..0a9c2ed6e259 100644 --- a/core/proto/android/os/worksource.proto +++ b/core/proto/android/os/worksource.proto @@ -17,15 +17,23 @@ syntax = "proto2"; package android.os; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + option java_multiple_files = true; message WorkSourceProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + message WorkSourceContentProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string name = 2; } message WorkChain { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + repeated WorkSourceContentProto nodes = 1; } diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 27fbb24566f0..bfd575a5b18d 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -20,7 +20,11 @@ package android.providers.settings; option java_multiple_files = true; option java_outer_classname = "SettingsServiceProto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + message SettingsServiceDumpProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + // Per user settings repeated UserSettingsProto user_settings = 1; @@ -29,8 +33,10 @@ message SettingsServiceDumpProto { } message UserSettingsProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + // Should be 0, 10, 11, 12, etc. where 0 is the owner. - optional int32 user_id = 1; + optional int32 user_id = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; // The secure settings for this user optional SecureSettingsProto secure_settings = 2; @@ -42,246 +48,266 @@ message UserSettingsProto { // Note: it's a conscious decision to add each setting as a separate field. This // allows annotating each setting with its own privacy tag. message GlobalSettingsProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + repeated SettingsOperationProto historical_operations = 1; - optional SettingProto add_users_when_locked = 2; - optional SettingProto enable_accessibility_global_gesture_enabled = 3; - optional SettingProto airplane_mode_on = 4; - optional SettingProto theater_mode_on = 5; - optional SettingProto radio_bluetooth = 6; - optional SettingProto radio_wifi = 7; - optional SettingProto radio_wimax = 8; - optional SettingProto radio_cell = 9; - optional SettingProto radio_nfc = 10; - optional SettingProto airplane_mode_radios = 11; - optional SettingProto airplane_mode_toggleable_radios = 12; - optional SettingProto bluetooth_class_of_device = 293; + optional SettingProto add_users_when_locked = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_accessibility_global_gesture_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto airplane_mode_on = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto theater_mode_on = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + reserved 6,7,8,9,10; // Accidentally used. They are currently free to be reused. + // A comma-separated list of radios that need to be disabled when airplane + // mode is on. This overrides wifi_on and bluetooth_on if wifi and bluetooth + // are included in the comma-separated list. + optional SettingProto airplane_mode_radios = 11 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto airplane_mode_toggleable_radios = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_class_of_device = 293 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto bluetooth_disabled_profiles = 13; optional SettingProto bluetooth_interoperability_list = 14; - optional SettingProto wifi_sleep_policy = 15; - optional SettingProto auto_time = 16; - optional SettingProto auto_time_zone = 17; + optional SettingProto wifi_sleep_policy = 15 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto auto_time = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto auto_time_zone = 17 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto car_dock_sound = 18; optional SettingProto car_undock_sound = 19; optional SettingProto desk_dock_sound = 20; optional SettingProto desk_undock_sound = 21; - optional SettingProto dock_sounds_enabled = 22; - optional SettingProto dock_sounds_enabled_when_accessibility = 23; + optional SettingProto dock_sounds_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dock_sounds_enabled_when_accessibility = 23 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto lock_sound = 24; optional SettingProto unlock_sound = 25; optional SettingProto trusted_sound = 26; optional SettingProto low_battery_sound = 27; - optional SettingProto power_sounds_enabled = 28; + optional SettingProto power_sounds_enabled = 28 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto wireless_charging_started_sound = 29; - optional SettingProto charging_sounds_enabled = 30; - optional SettingProto stay_on_while_plugged_in = 31; - optional SettingProto bugreport_in_power_menu = 32; - optional SettingProto adb_enabled = 33; - optional SettingProto debug_view_attributes = 34; - optional SettingProto assisted_gps_enabled = 35; - optional SettingProto bluetooth_on = 36; - optional SettingProto cdma_cell_broadcast_sms = 37; - optional SettingProto cdma_roaming_mode = 38; - optional SettingProto cdma_subscription_mode = 39; - optional SettingProto data_activity_timeout_mobile = 40; - optional SettingProto data_activity_timeout_wifi = 41; - optional SettingProto data_roaming = 42; - optional SettingProto mdc_initial_max_retry = 43; - optional SettingProto force_allow_on_external = 44; - optional SettingProto euicc_provisioned = 294; - optional SettingProto development_force_resizable_activities = 45; - optional SettingProto development_enable_freeform_windows_support = 46; - optional SettingProto development_settings_enabled = 47; - optional SettingProto device_provisioned = 48; - optional SettingProto device_provisioning_mobile_data_enabled = 49; - optional SettingProto display_size_forced = 50; - optional SettingProto display_scaling_force = 51; - optional SettingProto download_max_bytes_over_mobile = 52; - optional SettingProto download_recommended_max_bytes_over_mobile = 53; - optional SettingProto hdmi_control_enabled = 54; - optional SettingProto hdmi_system_audio_control_enabled = 55; - optional SettingProto hdmi_control_auto_wakeup_enabled = 56; - optional SettingProto hdmi_control_auto_device_off_enabled = 57; - optional SettingProto location_background_throttle_interval_ms = 295; - optional SettingProto location_background_throttle_proximity_alert_interval_ms = 296; - optional SettingProto location_background_throttle_package_whitelist = 297; - optional SettingProto wifi_scan_background_throttle_interval_ms = 298; - optional SettingProto wifi_scan_background_throttle_package_whitelist = 299; - optional SettingProto mhl_input_switching_enabled = 58; - optional SettingProto mhl_power_charge_enabled = 59; - optional SettingProto mobile_data = 60; - optional SettingProto mobile_data_always_on = 61; - optional SettingProto connectivity_metrics_buffer_size = 62; - optional SettingProto netstats_enabled = 63; - optional SettingProto netstats_poll_interval = 64; - optional SettingProto netstats_time_cache_max_age = 65; - optional SettingProto netstats_global_alert_bytes = 66; - optional SettingProto netstats_sample_enabled = 67; - optional SettingProto netstats_augment_enabled = 300; - optional SettingProto netstats_dev_bucket_duration = 68; - optional SettingProto netstats_dev_persist_bytes = 69; - optional SettingProto netstats_dev_rotate_age = 70; - optional SettingProto netstats_dev_delete_age = 71; - optional SettingProto netstats_uid_bucket_duration = 72; - optional SettingProto netstats_uid_persist_bytes = 73; - optional SettingProto netstats_uid_rotate_age = 74; - optional SettingProto netstats_uid_delete_age = 75; - optional SettingProto netstats_uid_tag_bucket_duration = 76; - optional SettingProto netstats_uid_tag_persist_bytes = 77; - optional SettingProto netstats_uid_tag_rotate_age = 78; - optional SettingProto netstats_uid_tag_delete_age = 79; + optional SettingProto charging_sounds_enabled = 30 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto stay_on_while_plugged_in = 31 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bugreport_in_power_menu = 32 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto adb_enabled = 33 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Whether views are allowed to save their attribute data. + optional SettingProto debug_view_attributes = 34 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assisted_gps_enabled = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_on = 36 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto cdma_cell_broadcast_sms = 37 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto cdma_roaming_mode = 38 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto cdma_subscription_mode = 39 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto data_activity_timeout_mobile = 40 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto data_activity_timeout_wifi = 41 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto data_roaming = 42 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mdc_initial_max_retry = 43 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto force_allow_on_external = 44 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto euicc_provisioned = 294 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto development_force_resizable_activities = 45 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto development_enable_freeform_windows_support = 46 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto development_settings_enabled = 47 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto device_provisioned = 48 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto device_provisioning_mobile_data_enabled = 49 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto display_size_forced = 50 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto display_scaling_force = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto download_max_bytes_over_mobile = 52 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto download_recommended_max_bytes_over_mobile = 53 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hdmi_control_enabled = 54 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hdmi_system_audio_control_enabled = 55 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hdmi_control_auto_wakeup_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hdmi_control_auto_device_off_enabled = 57 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto location_background_throttle_interval_ms = 295 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto location_background_throttle_proximity_alert_interval_ms = 296 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Packages that are whitelisted for background throttling (throttling will + // not be applied). + optional SettingProto location_background_throttle_package_whitelist = 297 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_scan_background_throttle_interval_ms = 298 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_scan_background_throttle_package_whitelist = 299 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mhl_input_switching_enabled = 58 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mhl_power_charge_enabled = 59 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mobile_data = 60 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mobile_data_always_on = 61 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto connectivity_metrics_buffer_size = 62 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_enabled = 63 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_poll_interval = 64 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_time_cache_max_age = 65 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_global_alert_bytes = 66 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_sample_enabled = 67 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_augment_enabled = 300 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_dev_bucket_duration = 68 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_dev_persist_bytes = 69 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_dev_rotate_age = 70 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_dev_delete_age = 71 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_bucket_duration = 72 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_persist_bytes = 73 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_rotate_age = 74 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_delete_age = 75 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_tag_bucket_duration = 76 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_tag_persist_bytes = 77 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_tag_rotate_age = 78 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto netstats_uid_tag_delete_age = 79 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // User preference for which network(s) should be used. optional SettingProto network_preference = 80; - optional SettingProto network_scorer_app = 81; - optional SettingProto nitz_update_diff = 82; - optional SettingProto nitz_update_spacing = 83; + optional SettingProto network_scorer_app = 81 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto nitz_update_diff = 82 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto nitz_update_spacing = 83 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto ntp_server = 84; - optional SettingProto ntp_timeout = 85; - optional SettingProto storage_benchmark_interval = 86; - optional SettingProto dns_resolver_sample_validity_seconds = 87; - optional SettingProto dns_resolver_success_threshold_percent = 88; - optional SettingProto dns_resolver_min_samples = 89; - optional SettingProto dns_resolver_max_samples = 90; - optional SettingProto ota_disable_automatic_update = 91; - optional SettingProto package_verifier_enable = 92; - optional SettingProto package_verifier_timeout = 93; + optional SettingProto ntp_timeout = 85 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto storage_benchmark_interval = 86 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dns_resolver_sample_validity_seconds = 87 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dns_resolver_success_threshold_percent = 88 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dns_resolver_min_samples = 89 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dns_resolver_max_samples = 90 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Whether to disable the automatic scheduling of system updates. + optional SettingProto ota_disable_automatic_update = 91 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto package_verifier_enable = 92 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto package_verifier_timeout = 93 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto package_verifier_default_response = 94; - optional SettingProto package_verifier_setting_visible = 95; - optional SettingProto package_verifier_include_adb = 96; - optional SettingProto fstrim_mandatory_interval = 97; - optional SettingProto pdp_watchdog_poll_interval_ms = 98; - optional SettingProto pdp_watchdog_long_poll_interval_ms = 99; - optional SettingProto pdp_watchdog_error_poll_interval_ms = 100; - optional SettingProto pdp_watchdog_trigger_packet_count = 101; - optional SettingProto pdp_watchdog_error_poll_count = 102; - optional SettingProto pdp_watchdog_max_pdp_reset_fail_count = 103; + optional SettingProto package_verifier_setting_visible = 95 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto package_verifier_include_adb = 96 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto fstrim_mandatory_interval = 97 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_poll_interval_ms = 98 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_long_poll_interval_ms = 99 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_error_poll_interval_ms = 100 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_trigger_packet_count = 101 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_error_poll_count = 102 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pdp_watchdog_max_pdp_reset_fail_count = 103 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto setup_prepaid_data_service_url = 105; optional SettingProto setup_prepaid_detection_target_url = 106; optional SettingProto setup_prepaid_detection_redir_host = 107; - optional SettingProto sms_outgoing_check_interval_ms = 108; - optional SettingProto sms_outgoing_check_max_count = 109; - optional SettingProto sms_short_code_confirmation = 110; - optional SettingProto sms_short_code_rule = 111; - optional SettingProto tcp_default_init_rwnd = 112; - optional SettingProto tether_supported = 113; - optional SettingProto tether_dun_required = 114; + optional SettingProto sms_outgoing_check_interval_ms = 108 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sms_outgoing_check_max_count = 109 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Used to disable SMS short code confirmation. Defaults to true. + optional SettingProto sms_short_code_confirmation = 110 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sms_short_code_rule = 111 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tcp_default_init_rwnd = 112 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tether_supported = 113 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tether_dun_required = 114 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto tether_dun_apn = 115; - optional SettingProto tether_offload_disabled = 301; - optional SettingProto carrier_app_whitelist = 116; - optional SettingProto usb_mass_storage_enabled = 117; - optional SettingProto use_google_mail = 118; + optional SettingProto tether_offload_disabled = 301 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // List of carrier apps which are whitelisted to prompt the user for install + // when a SIM card with marchin UICC carrier privilege rules is inserted. + optional SettingProto carrier_app_whitelist = 116 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto usb_mass_storage_enabled = 117 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto use_google_mail = 118 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto webview_data_reduction_proxy_key = 119; - optional SettingProto webview_fallback_logic_enabled = 120; - optional SettingProto webview_provider = 121; - optional SettingProto webview_multiprocess = 122; - optional SettingProto network_switch_notification_daily_limit = 123; - optional SettingProto network_switch_notification_rate_limit_millis = 124; - optional SettingProto network_avoid_bad_wifi = 125; - optional SettingProto network_metered_multipath_preference = 302; - optional SettingProto network_watchlist_last_report_time = 303; - optional SettingProto wifi_badging_thresholds = 304; - optional SettingProto wifi_display_on = 126; - optional SettingProto wifi_display_certification_on = 127; - optional SettingProto wifi_display_wps_config = 128; - optional SettingProto wifi_networks_available_notification_on = 129; - optional SettingProto wimax_networks_available_notification_on = 130; - optional SettingProto wifi_networks_available_repeat_delay = 131; + optional SettingProto webview_fallback_logic_enabled = 120 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Name of the package used as WebView provider. + optional SettingProto webview_provider = 121 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto webview_multiprocess = 122 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_switch_notification_daily_limit = 123 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_switch_notification_rate_limit_millis = 124 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_avoid_bad_wifi = 125 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_metered_multipath_preference = 302 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_watchlist_last_report_time = 303 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_badging_thresholds = 304 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_display_on = 126 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_display_certification_on = 127 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_display_wps_config = 128 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_networks_available_notification_on = 129 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wimax_networks_available_notification_on = 130 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_networks_available_repeat_delay = 131 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto wifi_country_code = 132; - optional SettingProto wifi_framework_scan_interval_ms = 133; - optional SettingProto wifi_idle_ms = 134; - optional SettingProto wifi_num_open_networks_kept = 135; - optional SettingProto wifi_on = 136; - optional SettingProto wifi_scan_always_available = 137; - optional SettingProto wifi_wakeup_enabled = 138; - optional SettingProto wifi_wakeup_available = 305; - optional SettingProto network_scoring_ui_enabled = 306; - optional SettingProto speed_label_cache_eviction_age_millis = 307; - optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308; - optional SettingProto network_recommendations_enabled = 139; - optional SettingProto network_recommendations_package = 286; - optional SettingProto use_open_wifi_package = 309; - optional SettingProto network_recommendation_request_timeout_ms = 310; - optional SettingProto ble_scan_always_available = 140; - optional SettingProto wifi_saved_state = 141; - optional SettingProto wifi_supplicant_scan_interval_ms = 142; - optional SettingProto wifi_enhanced_auto_join = 143; - optional SettingProto wifi_network_show_rssi = 144; - optional SettingProto wifi_scan_interval_when_p2p_connected_ms = 145; - optional SettingProto wifi_watchdog_on = 146; - optional SettingProto wifi_watchdog_poor_network_test_enabled = 147; - optional SettingProto wifi_suspend_optimizations_enabled = 148; - optional SettingProto wifi_verbose_logging_enabled = 149; - optional SettingProto wifi_max_dhcp_retry_count = 150; - optional SettingProto wifi_mobile_data_transition_wakelock_timeout_ms = 151; - optional SettingProto wifi_device_owner_configs_lockdown = 152; - optional SettingProto wifi_frequency_band = 153; + optional SettingProto wifi_framework_scan_interval_ms = 133 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_idle_ms = 134 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_num_open_networks_kept = 135 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_on = 136 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_scan_always_available = 137 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_wakeup_enabled = 138 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_wakeup_available = 305 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_scoring_ui_enabled = 306 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto speed_label_cache_eviction_age_millis = 307 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_recommendations_enabled = 139 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_recommendations_package = 286 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto use_open_wifi_package = 309 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_recommendation_request_timeout_ms = 310 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto ble_scan_always_available = 140 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_saved_state = 141 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_supplicant_scan_interval_ms = 142 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_enhanced_auto_join = 143 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_network_show_rssi = 144 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_scan_interval_when_p2p_connected_ms = 145 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_watchdog_on = 146 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_watchdog_poor_network_test_enabled = 147 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_suspend_optimizations_enabled = 148 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_verbose_logging_enabled = 149 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_connected_mac_randomization_enabled = 350 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_max_dhcp_retry_count = 150 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_mobile_data_transition_wakelock_timeout_ms = 151 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_device_owner_configs_lockdown = 152 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_frequency_band = 153 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto wifi_p2p_device_name = 154; - optional SettingProto wifi_reenable_delay_ms = 155; - optional SettingProto wifi_ephemeral_out_of_range_timeout_ms = 156; - optional SettingProto data_stall_alarm_non_aggressive_delay_in_ms = 157; - optional SettingProto data_stall_alarm_aggressive_delay_in_ms = 158; - optional SettingProto provisioning_apn_alarm_delay_in_ms = 159; - optional SettingProto gprs_register_check_period_ms = 160; - optional SettingProto wtf_is_fatal = 161; - optional SettingProto mode_ringer = 162; - optional SettingProto overlay_display_devices = 163; - optional SettingProto battery_discharge_duration_threshold = 164; - optional SettingProto battery_discharge_threshold = 165; - optional SettingProto send_action_app_error = 166; - optional SettingProto dropbox_age_seconds = 167; - optional SettingProto dropbox_max_files = 168; - optional SettingProto dropbox_quota_kb = 169; - optional SettingProto dropbox_quota_percent = 170; - optional SettingProto dropbox_reserve_percent = 171; - optional SettingProto dropbox_tag_prefix = 172; - optional SettingProto error_logcat_prefix = 173; - optional SettingProto sys_free_storage_log_interval = 174; - optional SettingProto disk_free_change_reporting_threshold = 175; - optional SettingProto sys_storage_threshold_percentage = 176; - optional SettingProto sys_storage_threshold_max_bytes = 177; - optional SettingProto sys_storage_full_threshold_bytes = 178; - optional SettingProto sys_storage_cache_percentage = 311; - optional SettingProto sys_storage_cache_max_bytes = 312; - optional SettingProto sync_max_retry_delay_in_seconds = 179; - optional SettingProto connectivity_change_delay = 180; - optional SettingProto connectivity_sampling_interval_in_seconds = 181; - optional SettingProto pac_change_delay = 182; - optional SettingProto captive_portal_mode = 183; - optional SettingProto captive_portal_detection_enabled = 313; + optional SettingProto wifi_reenable_delay_ms = 155 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_ephemeral_out_of_range_timeout_ms = 156 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto data_stall_alarm_non_aggressive_delay_in_ms = 157 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto data_stall_alarm_aggressive_delay_in_ms = 158 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto provisioning_apn_alarm_delay_in_ms = 159 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto gprs_register_check_period_ms = 160 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wtf_is_fatal = 161 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Ringer mode. A change in this value will not reflect as a change in the + // ringer mode. + optional SettingProto mode_ringer = 162 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Overlay display devices setting. + // The value is a specially formatted string that describes the size and + // density of simulated secondary devices. + // Format: {width}x{height}/dpi;... + optional SettingProto overlay_display_devices = 163 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto battery_discharge_duration_threshold = 164 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto battery_discharge_threshold = 165 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto send_action_app_error = 166 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_age_seconds = 167 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_max_files = 168 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_quota_kb = 169 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_quota_percent = 170 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_reserve_percent = 171 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dropbox_tag_prefix = 172 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto error_logcat_prefix = 173 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_free_storage_log_interval = 174 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto disk_free_change_reporting_threshold = 175 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_storage_threshold_percentage = 176 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_storage_threshold_max_bytes = 177 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_storage_full_threshold_bytes = 178 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_storage_cache_percentage = 311 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sys_storage_cache_max_bytes = 312 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sync_max_retry_delay_in_seconds = 179 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto connectivity_change_delay = 180 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto connectivity_sampling_interval_in_seconds = 181 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pac_change_delay = 182 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto captive_portal_mode = 183 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto captive_portal_detection_enabled = 313 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto captive_portal_server = 184; optional SettingProto captive_portal_https_url = 185; optional SettingProto captive_portal_http_url = 186; optional SettingProto captive_portal_fallback_url = 187; optional SettingProto captive_portal_other_fallback_urls = 314; - optional SettingProto captive_portal_use_https = 188; + optional SettingProto captive_portal_use_https = 188 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto captive_portal_user_agent = 189; - optional SettingProto nsd_on = 190; - optional SettingProto set_install_location = 191; - optional SettingProto default_install_location = 192; - optional SettingProto inet_condition_debounce_up_delay = 193; - optional SettingProto inet_condition_debounce_down_delay = 194; - optional SettingProto read_external_storage_enforced_default = 195; + optional SettingProto nsd_on = 190 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Let user pick default install location. + optional SettingProto set_install_location = 191 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto default_install_location = 192 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto inet_condition_debounce_up_delay = 193 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto inet_condition_debounce_down_delay = 194 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto read_external_storage_enforced_default = 195 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto http_proxy = 196; optional SettingProto global_http_proxy_host = 197; optional SettingProto global_http_proxy_port = 198; optional SettingProto global_http_proxy_exclusion_list = 199; optional SettingProto global_http_proxy_pac = 200; - optional SettingProto set_global_http_proxy = 201; + // Enables the UI setting to allow the user to specify the global HTTP proxy + // and associated exclusion list. + optional SettingProto set_global_http_proxy = 201 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto default_dns_server = 202; // The requested Private DNS mode and an accompanying specifier. optional SettingProto private_dns_mode = 315; optional SettingProto private_dns_specifier = 316; - optional SettingProto bluetooth_headset_priority_prefix = 203; - optional SettingProto bluetooth_a2dp_sink_priority_prefix = 204; - optional SettingProto bluetooth_a2dp_src_priority_prefix = 205; - optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287; - optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288; - optional SettingProto bluetooth_input_device_priority_prefix = 206; - optional SettingProto bluetooth_map_priority_prefix = 207; - optional SettingProto bluetooth_map_client_priority_prefix = 208; - optional SettingProto bluetooth_pbap_client_priority_prefix = 209; - optional SettingProto bluetooth_sap_priority_prefix = 210; - optional SettingProto bluetooth_pan_priority_prefix = 211; - optional SettingProto bluetooth_hearing_aid_priority_prefix = 345; + optional SettingProto bluetooth_headset_priority_prefix = 203 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_a2dp_sink_priority_prefix = 204 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_a2dp_src_priority_prefix = 205 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_input_device_priority_prefix = 206 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_map_priority_prefix = 207 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_map_client_priority_prefix = 208 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_pbap_client_priority_prefix = 209 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_sap_priority_prefix = 210 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_pan_priority_prefix = 211 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_hearing_aid_priority_prefix = 345 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto activity_manager_constants = 317; optional SettingProto device_idle_constants = 212; optional SettingProto device_idle_constants_watch = 213; @@ -295,26 +321,28 @@ message GlobalSettingsProto { optional SettingProto shortcut_manager_constants = 217; optional SettingProto device_policy_constants = 322; optional SettingProto text_classifier_constants = 323; - optional SettingProto window_animation_scale = 218; - optional SettingProto transition_animation_scale = 219; - optional SettingProto animator_duration_scale = 220; - optional SettingProto fancy_ime_animations = 221; - optional SettingProto compatibility_mode = 222; - optional SettingProto emergency_tone = 223; - optional SettingProto call_auto_retry = 224; - optional SettingProto emergency_affordance_needed = 225; - optional SettingProto preferred_network_mode = 226; + optional SettingProto window_animation_scale = 218 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto transition_animation_scale = 219 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto animator_duration_scale = 220 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto fancy_ime_animations = 221 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto compatibility_mode = 222 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto emergency_tone = 223 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto call_auto_retry = 224 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto emergency_affordance_needed = 225 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto preferred_network_mode = 226 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Name of an application package to be debugged. optional SettingProto debug_app = 227; - optional SettingProto wait_for_debugger = 228; - optional SettingProto enable_gpu_debug_layers = 342; + optional SettingProto wait_for_debugger = 228 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_gpu_debug_layers = 342 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // App allowed to load GPU debug layers. optional SettingProto gpu_debug_app = 343; - optional SettingProto gpu_debug_layers = 344; - optional SettingProto low_power_mode = 229; - optional SettingProto low_power_mode_trigger_level = 230; - optional SettingProto always_finish_activities = 231; - optional SettingProto dock_audio_media_enabled = 232; - optional SettingProto encoded_surround_output = 233; - optional SettingProto audio_safe_volume_state = 234; + optional SettingProto gpu_debug_layers = 344 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto low_power_mode = 229 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto low_power_mode_trigger_level = 230 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto always_finish_activities = 231 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dock_audio_media_enabled = 232 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto encoded_surround_output = 233 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto audio_safe_volume_state = 234 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto tzinfo_update_content_url = 235; optional SettingProto tzinfo_update_metadata_url = 236; optional SettingProto selinux_update_content_url = 237; @@ -331,308 +359,349 @@ message GlobalSettingsProto { optional SettingProto lang_id_update_metadata_url = 325; optional SettingProto smart_selection_update_content_url = 326; optional SettingProto smart_selection_update_metadata_url = 327; - optional SettingProto selinux_status = 247; - optional SettingProto development_force_rtl = 248; - optional SettingProto low_battery_sound_timeout = 249; - optional SettingProto wifi_bounce_delay_override_ms = 250; - optional SettingProto policy_control = 251; - optional SettingProto zen_mode = 252; - optional SettingProto zen_mode_ringer_level = 253; + optional SettingProto selinux_status = 247 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto development_force_rtl = 248 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto low_battery_sound_timeout = 249 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wifi_bounce_delay_override_ms = 250 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto policy_control = 251 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto zen_mode = 252 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto zen_mode_ringer_level = 253 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto zen_mode_config_etag = 254; - optional SettingProto heads_up_notifications_enabled = 255; + optional SettingProto heads_up_notifications_enabled = 255 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto device_name = 256; - optional SettingProto network_scoring_provisioned = 257; - optional SettingProto require_password_to_decrypt = 258; - optional SettingProto enhanced_4g_mode_enabled = 259; - optional SettingProto vt_ims_enabled = 260; - optional SettingProto wfc_ims_enabled = 261; - optional SettingProto wfc_ims_mode = 262; - optional SettingProto wfc_ims_roaming_mode = 263; - optional SettingProto wfc_ims_roaming_enabled = 264; - optional SettingProto lte_service_forced = 265; - optional SettingProto ephemeral_cookie_max_size_bytes = 266; - optional SettingProto enable_ephemeral_feature = 267; - optional SettingProto instant_app_dexopt_enabled = 328; - optional SettingProto installed_instant_app_min_cache_period = 268; - optional SettingProto installed_instant_app_max_cache_period = 289; - optional SettingProto uninstalled_instant_app_min_cache_period = 290; - optional SettingProto uninstalled_instant_app_max_cache_period = 291; - optional SettingProto unused_static_shared_lib_min_cache_period = 292; - optional SettingProto allow_user_switching_when_system_user_locked = 269; - optional SettingProto boot_count = 270; - optional SettingProto safe_boot_disallowed = 271; - optional SettingProto device_demo_mode = 272; - optional SettingProto network_access_timeout_ms = 329; + optional SettingProto network_scoring_provisioned = 257 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto require_password_to_decrypt = 258 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enhanced_4g_mode_enabled = 259 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto vt_ims_enabled = 260 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wfc_ims_enabled = 261 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wfc_ims_mode = 262 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wfc_ims_roaming_mode = 263 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wfc_ims_roaming_enabled = 264 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lte_service_forced = 265 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto ephemeral_cookie_max_size_bytes = 266 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_ephemeral_feature = 267 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto instant_app_dexopt_enabled = 328 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto installed_instant_app_min_cache_period = 268 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto installed_instant_app_max_cache_period = 289 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto uninstalled_instant_app_min_cache_period = 290 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto uninstalled_instant_app_max_cache_period = 291 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto unused_static_shared_lib_min_cache_period = 292 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto allow_user_switching_when_system_user_locked = 269 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto boot_count = 270 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto safe_boot_disallowed = 271 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto device_demo_mode = 272 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto network_access_timeout_ms = 329 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto database_downgrade_reason = 274; - optional SettingProto database_creation_buildid = 330; - optional SettingProto contacts_database_wal_enabled = 275; - optional SettingProto location_settings_link_to_permissions_enabled = 331; + optional SettingProto database_creation_buildid = 330 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto contacts_database_wal_enabled = 275 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto location_settings_link_to_permissions_enabled = 331 [ (android.privacy).dest = DEST_AUTOMATIC ]; reserved 332; // Removed backup_refactored_service_disabled - optional SettingProto euicc_factory_reset_timeout_millis = 333; - optional SettingProto storage_settings_clobber_threshold = 334; - optional SettingProto multi_sim_voice_call_subscription = 276; - optional SettingProto multi_sim_voice_prompt = 277; - optional SettingProto multi_sim_data_call_subscription = 278; - optional SettingProto multi_sim_sms_subscription = 279; - optional SettingProto multi_sim_sms_prompt = 280; - optional SettingProto new_contact_aggregator = 281; - optional SettingProto contact_metadata_sync_enabled = 282; - optional SettingProto enable_cellular_on_boot = 283; - optional SettingProto max_notification_enqueue_rate = 284; - optional SettingProto show_notification_channel_warnings = 335; - optional SettingProto cell_on = 285; - optional SettingProto show_temperature_warning = 336; - optional SettingProto warning_temperature = 337; - optional SettingProto enable_diskstats_logging = 338; - optional SettingProto enable_cache_quota_calculation = 339; - optional SettingProto enable_deletion_helper_no_threshold_toggle = 340; - optional SettingProto notification_snooze_options = 341; - optional SettingProto enable_gnss_raw_meas_full_tracking = 346; - optional SettingProto zram_enabled = 347; - optional SettingProto enable_smart_replies_in_notifications = 348; - optional SettingProto show_first_crash_dialog = 349; - optional SettingProto wifi_connected_mac_randomization_enabled = 350; - optional SettingProto show_restart_in_crash_dialog = 351; - optional SettingProto show_mute_in_crash_dialog = 352; - optional SettingProto chained_battery_attribution_enabled = 353; + optional SettingProto euicc_factory_reset_timeout_millis = 333 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto storage_settings_clobber_threshold = 334 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto chained_battery_attribution_enabled = 353 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Subscription to be used for voice call on a multi sim device. The + // supported values are 0 = SUB1, 1 = SUB2 and etc. + optional SettingProto multi_sim_voice_call_subscription = 276 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto multi_sim_voice_prompt = 277 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto multi_sim_data_call_subscription = 278 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto multi_sim_sms_subscription = 279 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto multi_sim_sms_prompt = 280 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Whether to enable new contacts aggregator or not. + // 1 = enable, 0 = disable. + optional SettingProto new_contact_aggregator = 281 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto contact_metadata_sync_enabled = 282 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_cellular_on_boot = 283 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto max_notification_enqueue_rate = 284 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_notification_channel_warnings = 335 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto cell_on = 285 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_temperature_warning = 336 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto warning_temperature = 337 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_diskstats_logging = 338 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_cache_quota_calculation = 339 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_deletion_helper_no_threshold_toggle = 340 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto notification_snooze_options = 341 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_gnss_raw_meas_full_tracking = 346 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto zram_enabled = 347 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enable_smart_replies_in_notifications = 348 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_restart_in_crash_dialog = 351 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in the same order as in + // frameworks/base/core/java/android/provider/Settings.java. // Next tag = 354; } message SecureSettingsProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + repeated SettingsOperationProto historical_operations = 1; optional SettingProto android_id = 2; - optional SettingProto default_input_method = 3; - optional SettingProto selected_input_method_subtype = 4; - optional SettingProto input_methods_subtype_history = 5; - optional SettingProto input_method_selector_visibility = 6; - optional SettingProto voice_interaction_service = 7; - optional SettingProto autofill_service = 8; + optional SettingProto default_input_method = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto selected_input_method_subtype = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto input_methods_subtype_history = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto input_method_selector_visibility = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The currently selected voice interaction service flattened ComponentName. + optional SettingProto voice_interaction_service = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The currently selected autofill service flattened ComponentName. + optional SettingProto autofill_service = 8 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto bluetooth_hci_log = 9; - optional SettingProto user_setup_complete = 10; + optional SettingProto user_setup_complete = 10 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Whether the current user has been set up via setup wizard (0 = false, // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be // reset back to 0 in case SetupWizard has been re-enabled on TV devices. - optional SettingProto tv_user_setup_complete = 170; - optional SettingProto completed_category_prefix = 11; - optional SettingProto enabled_input_methods = 12; - optional SettingProto disabled_system_input_methods = 13; - optional SettingProto show_ime_with_hard_keyboard = 14; + optional SettingProto tv_user_setup_complete = 170 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto completed_category_prefix = 11 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enabled_input_methods = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto disabled_system_input_methods = 13 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_ime_with_hard_keyboard = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto always_on_vpn_app = 15; - optional SettingProto always_on_vpn_lockdown = 16; - optional SettingProto install_non_market_apps = 17; - optional SettingProto unknown_sources_default_reversed = 171; - optional SettingProto location_mode = 18; + optional SettingProto always_on_vpn_lockdown = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto install_non_market_apps = 17 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto unknown_sources_default_reversed = 171 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The degree of location access enabled by the user. + optional SettingProto location_mode = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto location_previous_mode = 19; - optional SettingProto lock_to_app_exit_locked = 20; - optional SettingProto lock_screen_lock_after_timeout = 21; - optional SettingProto lock_screen_allow_private_notifications = 172; - optional SettingProto lock_screen_allow_remote_input = 22; - optional SettingProto show_note_about_notification_hiding = 23; - optional SettingProto trust_agents_initialized = 24; - optional SettingProto parental_control_enabled = 25; - optional SettingProto parental_control_last_update = 26; - optional SettingProto parental_control_redirect_url = 27; - optional SettingProto settings_classname = 28; - optional SettingProto accessibility_enabled = 29; - optional SettingProto accessibility_shortcut_enabled = 173; - optional SettingProto accessibility_shortcut_on_lock_screen = 174; - optional SettingProto accessibility_shortcut_dialog_shown = 175; - optional SettingProto accessibility_shortcut_target_service = 176; - optional SettingProto accessibility_button_target_component = 177; - optional SettingProto touch_exploration_enabled = 30; + // Whether lock-to-app will lock the keyguard when exiting. + optional SettingProto lock_to_app_exit_locked = 20 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lock_screen_lock_after_timeout = 21 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lock_screen_allow_private_notifications = 172 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lock_screen_allow_remote_input = 22 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_note_about_notification_hiding = 23 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto trust_agents_initialized = 24 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto parental_control_enabled = 25 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto parental_control_last_update = 26 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto parental_control_redirect_url = 27 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto settings_classname = 28 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_enabled = 29 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_shortcut_enabled = 173 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_shortcut_on_lock_screen = 174 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_shortcut_dialog_shown = 175 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_shortcut_target_service = 176 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Setting specifying the accessibility service or feature to be toggled via + // the accessibility button in the navigation bar. This is either a + // flattened ComponentName or the class name of a system class implementing + // a supported accessibility feature. + optional SettingProto accessibility_button_target_component = 177 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto touch_exploration_enabled = 30 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // List of the enabled accessibility providers. optional SettingProto enabled_accessibility_services = 31; + // List of the accessibility services to which the user has granted + // permission to put the device into touch exploration mode. optional SettingProto touch_exploration_granted_accessibility_services = 32; - optional SettingProto accessibility_speak_password = 33; - optional SettingProto accessibility_high_text_contrast_enabled = 34; + // Whether to speak passwords while in accessibility mode. + optional SettingProto accessibility_speak_password = 33 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_high_text_contrast_enabled = 34 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_script_injection = 35; optional SettingProto accessibility_screen_reader_url = 36; optional SettingProto accessibility_web_content_key_bindings = 37; - optional SettingProto accessibility_display_magnification_enabled = 38; - optional SettingProto accessibility_display_magnification_navbar_enabled = 178; - optional SettingProto accessibility_display_magnification_scale = 39; - optional SettingProto accessibility_display_magnification_auto_update = 179; - optional SettingProto accessibility_soft_keyboard_mode = 40; - optional SettingProto accessibility_captioning_enabled = 41; - optional SettingProto accessibility_captioning_locale = 42; - optional SettingProto accessibility_captioning_preset = 43; - optional SettingProto accessibility_captioning_background_color = 44; - optional SettingProto accessibility_captioning_foreground_color = 45; - optional SettingProto accessibility_captioning_edge_type = 46; - optional SettingProto accessibility_captioning_edge_color = 47; - optional SettingProto accessibility_captioning_window_color = 48; - optional SettingProto accessibility_captioning_typeface = 49; - optional SettingProto accessibility_captioning_font_scale = 50; - optional SettingProto accessibility_display_inversion_enabled = 51; - optional SettingProto accessibility_display_daltonizer_enabled = 52; - optional SettingProto accessibility_display_daltonizer = 53; - optional SettingProto accessibility_autoclick_enabled = 54; - optional SettingProto accessibility_autoclick_delay = 55; - optional SettingProto accessibility_large_pointer_icon = 56; - optional SettingProto long_press_timeout = 57; - optional SettingProto multi_press_timeout = 58; + optional SettingProto accessibility_display_magnification_enabled = 38 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_magnification_navbar_enabled = 178 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_magnification_scale = 39 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_magnification_auto_update = 179 [deprecated = true]; + optional SettingProto accessibility_soft_keyboard_mode = 40 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_locale = 42 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_preset = 43 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_background_color = 44 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_foreground_color = 45 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_edge_type = 46 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_edge_color = 47 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_window_color = 48 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_typeface = 49 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_captioning_font_scale = 50 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_inversion_enabled = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_daltonizer_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_display_daltonizer = 53 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_autoclick_enabled = 54 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_autoclick_delay = 55 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_large_pointer_icon = 56 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto long_press_timeout = 57 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto multi_press_timeout = 58 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto enabled_print_services = 59; optional SettingProto disabled_print_services = 60; - optional SettingProto display_density_forced = 61; - optional SettingProto tts_default_rate = 62; - optional SettingProto tts_default_pitch = 63; - optional SettingProto tts_default_synth = 64; - optional SettingProto tts_default_locale = 65; + optional SettingProto display_density_forced = 61 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tts_default_rate = 62 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tts_default_pitch = 63 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tts_default_synth = 64 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tts_default_locale = 65 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto tts_enabled_plugins = 66; - optional SettingProto connectivity_release_pending_intent_delay_ms = 67; + optional SettingProto connectivity_release_pending_intent_delay_ms = 67 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto allowed_geolocation_origins = 68; - optional SettingProto preferred_tty_mode = 69; - optional SettingProto enhanced_voice_privacy_enabled = 70; - optional SettingProto tty_mode_enabled = 71; - optional SettingProto backup_enabled = 72; - optional SettingProto backup_auto_restore = 73; - optional SettingProto backup_provisioned = 74; - optional SettingProto backup_transport = 75; - optional SettingProto last_setup_shown = 76; - optional SettingProto search_global_search_activity = 77; - optional SettingProto search_num_promoted_sources = 78; - optional SettingProto search_max_results_to_display = 79; - optional SettingProto search_max_results_per_source = 80; - optional SettingProto search_web_results_override_limit = 81; - optional SettingProto search_promoted_source_deadline_millis = 82; - optional SettingProto search_source_timeout_millis = 83; - optional SettingProto search_prefill_millis = 84; - optional SettingProto search_max_stat_age_millis = 85; - optional SettingProto search_max_source_event_age_millis = 86; - optional SettingProto search_min_impressions_for_source_ranking = 87; - optional SettingProto search_min_clicks_for_source_ranking = 88; - optional SettingProto search_max_shortcuts_returned = 89; - optional SettingProto search_query_thread_core_pool_size = 90; - optional SettingProto search_query_thread_max_pool_size = 91; - optional SettingProto search_shortcut_refresh_core_pool_size = 92; - optional SettingProto search_shortcut_refresh_max_pool_size = 93; - optional SettingProto search_thread_keepalive_seconds = 94; - optional SettingProto search_per_source_concurrent_query_limit = 95; - optional SettingProto mount_play_notification_snd = 96; - optional SettingProto mount_ums_autostart = 97; - optional SettingProto mount_ums_prompt = 98; - optional SettingProto mount_ums_notify_enabled = 99; - optional SettingProto anr_show_background = 100; - optional SettingProto voice_recognition_service = 101; - optional SettingProto package_verifier_user_consent = 102; - optional SettingProto selected_spell_checker = 103; - optional SettingProto selected_spell_checker_subtype = 104; - optional SettingProto spell_checker_enabled = 105; - optional SettingProto incall_power_button_behavior = 106; - optional SettingProto incall_back_button_behavior = 107; - optional SettingProto wake_gesture_enabled = 108; - optional SettingProto doze_enabled = 109; - optional SettingProto doze_always_on = 110; - optional SettingProto doze_pulse_on_pick_up = 111; - optional SettingProto doze_pulse_on_long_press = 180; - optional SettingProto doze_pulse_on_double_tap = 112; - optional SettingProto ui_night_mode = 113; - optional SettingProto screensaver_enabled = 114; - optional SettingProto screensaver_components = 115; - optional SettingProto screensaver_activate_on_dock = 116; - optional SettingProto screensaver_activate_on_sleep = 117; - optional SettingProto screensaver_default_component = 118; - optional SettingProto nfc_payment_default_component = 119; - optional SettingProto nfc_payment_foreground = 120; - optional SettingProto sms_default_application = 121; - optional SettingProto dialer_default_application = 122; - optional SettingProto emergency_assistance_application = 123; - optional SettingProto assist_structure_enabled = 124; - optional SettingProto assist_screenshot_enabled = 125; - optional SettingProto assist_disclosure_enabled = 126; - optional SettingProto enabled_notification_assistant = 127; - optional SettingProto enabled_notification_listeners = 128; - optional SettingProto enabled_notification_policy_access_packages = 129; - optional SettingProto sync_parent_sounds = 130; - optional SettingProto immersive_mode_confirmations = 131; - optional SettingProto print_service_search_uri = 132; - optional SettingProto payment_service_search_uri = 133; - optional SettingProto autofill_service_search_uri = 181; - optional SettingProto skip_first_use_hints = 134; - optional SettingProto unsafe_volume_music_active_ms = 135; - optional SettingProto lock_screen_show_notifications = 136; + optional SettingProto preferred_tty_mode = 69 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enhanced_voice_privacy_enabled = 70 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tty_mode_enabled = 71 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto backup_enabled = 72 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto backup_auto_restore = 73 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto backup_provisioned = 74 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto backup_transport = 75 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto last_setup_shown = 76 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_global_search_activity = 77 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_num_promoted_sources = 78 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_max_results_to_display = 79 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_max_results_per_source = 80 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_web_results_override_limit = 81 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_promoted_source_deadline_millis = 82 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_source_timeout_millis = 83 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_prefill_millis = 84 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_max_stat_age_millis = 85 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_max_source_event_age_millis = 86 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_min_impressions_for_source_ranking = 87 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_min_clicks_for_source_ranking = 88 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_max_shortcuts_returned = 89 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_query_thread_core_pool_size = 90 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_query_thread_max_pool_size = 91 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_shortcut_refresh_core_pool_size = 92 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_shortcut_refresh_max_pool_size = 93 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_thread_keepalive_seconds = 94 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto search_per_source_concurrent_query_limit = 95 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Whether or not alert sounds are played on StorageManagerService events. + optional SettingProto mount_play_notification_snd = 96 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mount_ums_autostart = 97 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mount_ums_prompt = 98 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mount_ums_notify_enabled = 99 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto anr_show_background = 100 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The ComponentName string of the service to be used as the voice + // recognition service. + optional SettingProto voice_recognition_service = 101 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto package_verifier_user_consent = 102 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto selected_spell_checker = 103 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto selected_spell_checker_subtype = 104 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto spell_checker_enabled = 105 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto incall_power_button_behavior = 106 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto incall_back_button_behavior = 107 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto wake_gesture_enabled = 108 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto doze_enabled = 109 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto doze_always_on = 110 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto doze_pulse_on_pick_up = 111 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto doze_pulse_on_long_press = 180 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto doze_pulse_on_double_tap = 112 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto ui_night_mode = 113 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screensaver_enabled = 114 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screensaver_components = 115 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screensaver_activate_on_dock = 116 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screensaver_activate_on_sleep = 117 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screensaver_default_component = 118 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto nfc_payment_default_component = 119 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto nfc_payment_foreground = 120 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sms_default_application = 121 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dialer_default_application = 122 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto emergency_assistance_application = 123 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_structure_enabled = 124 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_screenshot_enabled = 125 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_disclosure_enabled = 126 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Read only list of the service components that the current user has + // explicitly allowed to see and assist with all of the user's + // notifications. + optional SettingProto enabled_notification_assistant = 127 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enabled_notification_listeners = 128 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto enabled_notification_policy_access_packages = 129 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Defines whether managed profile ringtones should be synced from its + // parent profile. + optional SettingProto sync_parent_sounds = 130 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto immersive_mode_confirmations = 131 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The query URI to find a print service to install. + optional SettingProto print_service_search_uri = 132 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The query URI to find an NFC service to install. + optional SettingProto payment_service_search_uri = 133 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The query URI to find an auto fill service to install. + optional SettingProto autofill_service_search_uri = 181 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto skip_first_use_hints = 134 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto unsafe_volume_music_active_ms = 135 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lock_screen_show_notifications = 136 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto tv_input_hidden_inputs = 137; optional SettingProto tv_input_custom_labels = 138; - optional SettingProto usb_audio_automatic_routing_disabled = 139; - optional SettingProto sleep_timeout = 140; - optional SettingProto double_tap_to_wake = 141; - optional SettingProto assistant = 142; - optional SettingProto camera_gesture_disabled = 143; - optional SettingProto camera_double_tap_power_gesture_disabled = 144; - optional SettingProto camera_double_twist_to_flip_enabled = 145; - optional SettingProto camera_lift_trigger_enabled = 182; - optional SettingProto assist_gesture_enabled = 183; - optional SettingProto assist_gesture_sensitivity = 184; - optional SettingProto assist_gesture_silence_alerts_enabled = 185; - optional SettingProto assist_gesture_wake_enabled = 186; - optional SettingProto assist_gesture_setup_complete = 187; - optional SettingProto night_display_activated = 146; - optional SettingProto night_display_auto_mode = 147; - optional SettingProto night_display_color_temperature = 188; - optional SettingProto night_display_custom_start_time = 148; - optional SettingProto night_display_custom_end_time = 149; - optional SettingProto night_display_last_activated_time = 189; + optional SettingProto usb_audio_automatic_routing_disabled = 139 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sleep_timeout = 140 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto double_tap_to_wake = 141 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // The current assistant component. It could be a voice interaction service, + // or an activity that handles ACTION_ASSIST, or empty, which means using + // the default handling. + optional SettingProto assistant = 142 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto camera_gesture_disabled = 143 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto camera_double_tap_power_gesture_disabled = 144 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto camera_double_twist_to_flip_enabled = 145 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto camera_lift_trigger_enabled = 182 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_gesture_enabled = 183 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_gesture_sensitivity = 184 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_gesture_silence_alerts_enabled = 185 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_gesture_wake_enabled = 186 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto assist_gesture_setup_complete = 187 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_activated = 146 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_auto_mode = 147 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_color_temperature = 188 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_custom_start_time = 148 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_custom_end_time = 149 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto night_display_last_activated_time = 189 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto brightness_use_twilight = 150; - optional SettingProto enabled_vr_listeners = 151; - optional SettingProto vr_display_mode = 152; - optional SettingProto carrier_apps_handled = 153; - optional SettingProto managed_profile_contact_remote_search = 154; - optional SettingProto automatic_storage_manager_enabled = 155; - optional SettingProto automatic_storage_manager_days_to_retain = 156; - optional SettingProto automatic_storage_manager_bytes_cleared = 157; - optional SettingProto automatic_storage_manager_last_run = 158; - optional SettingProto automatic_storage_manager_turned_off_by_policy = 190; - optional SettingProto system_navigation_keys_enabled = 159; + optional SettingProto enabled_vr_listeners = 151 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto vr_display_mode = 152 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto carrier_apps_handled = 153 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto managed_profile_contact_remote_search = 154 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto automatic_storage_manager_enabled = 155 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto automatic_storage_manager_days_to_retain = 156 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto automatic_storage_manager_bytes_cleared = 157 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto automatic_storage_manager_last_run = 158 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto automatic_storage_manager_turned_off_by_policy = 190 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto system_navigation_keys_enabled = 159 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto downloads_backup_enabled = 160; optional SettingProto downloads_backup_allow_metered = 161; optional SettingProto downloads_backup_charging_only = 162; optional SettingProto automatic_storage_manager_downloads_days_to_retain = 163; - optional SettingProto qs_tiles = 164; + // Holds comma-separated list of ordering of QuickSettings tiles. + optional SettingProto qs_tiles = 164 [ (android.privacy).dest = DEST_AUTOMATIC ]; reserved 165; // Removed demo_user_setup_complete - optional SettingProto instant_apps_enabled = 166; - optional SettingProto device_paired = 167; - optional SettingProto package_verifier_state = 191; - optional SettingProto cmas_additional_broadcast_pkg = 192; - optional SettingProto notification_badging = 168; - optional SettingProto qs_auto_added_tiles = 193; - optional SettingProto lockdown_in_power_menu = 194; + optional SettingProto instant_apps_enabled = 166 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto device_paired = 167 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto package_verifier_state = 191 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto cmas_additional_broadcast_pkg = 192 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto notification_badging = 168 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto qs_auto_added_tiles = 193 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lockdown_in_power_menu = 194 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto backup_manager_constants = 169; - optional SettingProto show_first_crash_dialog_dev_option = 195; - optional SettingProto bluetooth_on_while_driving = 196; + optional SettingProto show_first_crash_dialog_dev_option = 195 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_on_while_driving = 196 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in the same order as in + // frameworks/base/core/java/android/provider/Settings.java. // Next tag = 197 } message SystemSettingsProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + repeated SettingsOperationProto historical_operations = 1; - optional SettingProto end_button_behavior = 2; - optional SettingProto advanced_settings = 3; - optional SettingProto bluetooth_discoverability = 4; - optional SettingProto bluetooth_discoverability_timeout = 5; - optional SettingProto font_scale = 6; - optional SettingProto system_locales = 7; - optional SettingProto display_color_mode = 67; - optional SettingProto screen_off_timeout = 8; - optional SettingProto screen_brightness = 9; - optional SettingProto screen_brightness_for_vr = 10; - optional SettingProto screen_brightness_mode = 11; - optional SettingProto screen_auto_brightness_adj = 12; - optional SettingProto mode_ringer_streams_affected = 13; - optional SettingProto mute_streams_affected = 14; - optional SettingProto vibrate_on = 15; - optional SettingProto vibrate_input_devices = 16; - optional SettingProto volume_ring = 17; - optional SettingProto volume_system = 18; - optional SettingProto volume_voice = 19; - optional SettingProto volume_music = 20; - optional SettingProto volume_alarm = 21; - optional SettingProto volume_notification = 22; - optional SettingProto volume_bluetooth_sco = 23; - optional SettingProto volume_accessibility = 68; - optional SettingProto volume_master = 24; - optional SettingProto master_mono = 25; + optional SettingProto end_button_behavior = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto advanced_settings = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_discoverability = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto bluetooth_discoverability_timeout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto font_scale = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto system_locales = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto display_color_mode = 67 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screen_off_timeout = 8 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screen_brightness = 9 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screen_brightness_for_vr = 10 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screen_brightness_mode = 11 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto screen_auto_brightness_adj = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Determines which streams are affected by ringer mode changes. The stream + // type's bit will be set to 1 if it should be muted when going into an + // inaudible ringer mode. + optional SettingProto mode_ringer_streams_affected = 13 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto mute_streams_affected = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto vibrate_on = 15 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto vibrate_input_devices = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_ring = 17 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_system = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_voice = 19 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_music = 20 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_alarm = 21 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_notification = 22 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_bluetooth_sco = 23 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_accessibility = 68 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto volume_master = 24 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto master_mono = 25 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Whether silent mode should allow vibration feedback. This is used // internally in AudioService and the Sound settings activity to coordinate // decoupling of vibrate and silent modes. This setting will likely be @@ -641,8 +710,10 @@ message SystemSettingsProto { // Not used anymore. On devices with vibrator, the user explicitly selects // silent or vibrate mode. Kept for use by legacy database upgrade code in // DatabaseHelper. - optional SettingProto vibrate_in_silent = 26; - optional SettingProto append_for_last_audible = 27; + optional SettingProto vibrate_in_silent = 26 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Appended to various volume related settings to record the previous values + // before the settings were affected by a silent/vibrate ringer mode change. + optional SettingProto append_for_last_audible = 27 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto ringtone = 28; optional SettingProto ringtone_cache = 29; optional SettingProto notification_sound = 30; @@ -650,44 +721,47 @@ message SystemSettingsProto { optional SettingProto alarm_alert = 32; optional SettingProto alarm_alert_cache = 33; optional SettingProto media_button_receiver = 34; - optional SettingProto text_auto_replace = 35; - optional SettingProto text_auto_caps = 36; - optional SettingProto text_auto_punctuate = 37; - optional SettingProto text_show_password = 38; - optional SettingProto show_gtalk_service_status = 39; - optional SettingProto time_12_24 = 40; - optional SettingProto date_format = 41; - optional SettingProto setup_wizard_has_run = 42; - optional SettingProto accelerometer_rotation = 43; - optional SettingProto user_rotation = 44; - optional SettingProto hide_rotation_lock_toggle_for_accessibility = 45; - optional SettingProto vibrate_when_ringing = 46; - optional SettingProto dtmf_tone_when_dialing = 47; - optional SettingProto dtmf_tone_type_when_dialing = 48; - optional SettingProto hearing_aid = 49; - optional SettingProto tty_mode = 50; - optional SettingProto sound_effects_enabled = 51; - optional SettingProto haptic_feedback_enabled = 52; - optional SettingProto notification_light_pulse = 53; - optional SettingProto pointer_location = 54; - optional SettingProto show_touches = 55; + optional SettingProto text_auto_replace = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto text_auto_caps = 36 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto text_auto_punctuate = 37 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto text_show_password = 38 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_gtalk_service_status = 39 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto time_12_24 = 40 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto date_format = 41 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto setup_wizard_has_run = 42 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accelerometer_rotation = 43 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto user_rotation = 44 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hide_rotation_lock_toggle_for_accessibility = 45 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto vibrate_when_ringing = 46 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dtmf_tone_when_dialing = 47 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto dtmf_tone_type_when_dialing = 48 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto hearing_aid = 49 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto tty_mode = 50 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sound_effects_enabled = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto haptic_feedback_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto notification_light_pulse = 53 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Show pointer location on screen? 0 = no, 1 = yes. + optional SettingProto pointer_location = 54 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_touches = 55 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Log raw orientation data from {@link // com.android.server.policy.WindowOrientationListener} for use with the // orientationplot.py tool. // 0 = no, 1 = yes - optional SettingProto window_orientation_listener_log = 56; - optional SettingProto lockscreen_sounds_enabled = 57; - optional SettingProto lockscreen_disabled = 58; - optional SettingProto sip_receive_calls = 59; - optional SettingProto sip_call_options = 60; - optional SettingProto sip_always = 61; - optional SettingProto sip_address_only = 62; - optional SettingProto pointer_speed = 63; - optional SettingProto lock_to_app_enabled = 64; - optional SettingProto egg_mode = 65; - optional SettingProto show_battery_percent = 69; - optional SettingProto when_to_make_wifi_calls = 66; + optional SettingProto window_orientation_listener_log = 56 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lockscreen_sounds_enabled = 57 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lockscreen_disabled = 58 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sip_receive_calls = 59 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sip_call_options = 60 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sip_always = 61 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sip_address_only = 62 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_speed = 63 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto lock_to_app_enabled = 64 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto egg_mode = 65 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_battery_percent = 69 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto when_to_make_wifi_calls = 66 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in the same order as in + // frameworks/base/core/java/android/provider/Settings.java. // Next tag = 70; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 55e6a74804ee..788d90127c3d 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -130,6 +130,8 @@ message BroadcastProto { repeated StickyBroadcastProto sticky_broadcasts = 4; message MainHandler { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional string handler = 1; optional .android.os.LooperProto looper = 2; } @@ -663,7 +665,7 @@ message ProcessesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional .android.os.PowerManagerInternalProto.Wakefulness wakefulness = 1; - repeated string sleep_tokens = 2; + repeated string sleep_tokens = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional bool sleeping = 3; optional bool shutting_down = 4; optional bool test_pss_mode = 5; @@ -671,7 +673,7 @@ message ProcessesProto { optional SleepStatus sleep_status = 27; message VoiceProto { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; + option (.android.msg_privacy).dest = DEST_EXPLICIT; optional string session = 1; optional .android.os.PowerManagerProto.WakeLockProto wakelock = 2; @@ -725,7 +727,7 @@ message ProcessesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional string proc_name = 1; - optional string file = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; + optional string file = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int32 pid = 3; optional int32 uid = 4; } @@ -998,6 +1000,8 @@ message AppTimeTrackerProto { optional int64 total_duration_ms = 2; message PackageTime { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional string package = 1; optional int64 duration_ms = 2; } diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto index 53b4be498992..aa2663fa2cc4 100644 --- a/core/proto/android/server/alarmmanagerservice.proto +++ b/core/proto/android/server/alarmmanagerservice.proto @@ -21,12 +21,15 @@ import "frameworks/base/core/proto/android/app/pendingintent.proto"; import "frameworks/base/core/proto/android/internal/locallog.proto"; import "frameworks/base/core/proto/android/os/worksource.proto"; import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; package com.android.server; option java_multiple_files = true; message AlarmManagerServiceProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int64 current_time = 1; optional int64 elapsed_realtime = 2; optional int64 last_time_change_clock_time = 3; @@ -106,6 +109,8 @@ message AlarmManagerServiceProto { optional int64 allow_while_idle_min_duration_ms = 35; message LastAllowWhileIdleDispatch { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; // In the 'elapsed' timebase. optional int64 time_ms = 2; @@ -117,6 +122,8 @@ message AlarmManagerServiceProto { optional com.android.internal.util.LocalLogProto recent_problems = 37; message TopAlarm { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string package_name = 2; optional FilterStatsProto filter = 3; @@ -124,6 +131,8 @@ message AlarmManagerServiceProto { repeated TopAlarm top_alarms = 38; message AlarmStat { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional BroadcastStatsProto broadcast = 1; repeated FilterStatsProto filters = 2; } @@ -136,6 +145,8 @@ message AlarmManagerServiceProto { // This is a soft wrapper for alarm clock information. It is not representative // of an android.app.AlarmManager.AlarmClockInfo object. message AlarmClockMetadataProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 user = 1; optional bool is_pending_send = 2; // This value is UTC wall clock time in milliseconds, as returned by @@ -145,7 +156,9 @@ message AlarmClockMetadataProto { // A com.android.server.AlarmManagerService.Alarm object. message AlarmProto { - optional string tag = 1; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string tag = 1 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional .android.app.AlarmManagerProto.AlarmType type = 2; // How long until the alarm goes off, in the 'elapsed' timebase. Can be // negative if 'when' is in the past. @@ -156,11 +169,13 @@ message AlarmProto { optional int32 flags = 7; optional .android.app.AlarmClockInfoProto alarm_clock = 8; optional .android.app.PendingIntentProto operation = 9; - optional string listener = 10; + optional string listener = 10 [ (.android.privacy).dest = DEST_EXPLICIT ]; } // A com.android.server.AlarmManagerService.Batch object. message BatchProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // Start time in terms of elapsed realtime. optional int64 start_realtime = 1; // End time in terms of elapsed realtime. @@ -171,6 +186,8 @@ message BatchProto { // A com.android.server.AlarmManagerService.BroadcastStats object. message BroadcastStatsProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string package_name = 2; // The total amount of time this broadcast was in flight. @@ -186,6 +203,8 @@ message BroadcastStatsProto { // A com.android.server.AlarmManagerService.Constants object. message ConstantsProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // Minimum futurity of a new alarm. optional int64 min_futurity_duration_ms = 1; // Minimum alarm recurrence interval. @@ -202,7 +221,9 @@ message ConstantsProto { // A com.android.server.AlarmManagerService.FilterStats object. message FilterStatsProto { - optional string tag = 1; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string tag = 1 [ (.android.privacy).dest = DEST_EXPLICIT ]; // The last time this filter when in flight, in terms of elapsed realtime. optional int64 last_flight_time_realtime = 2; // The total amount of time this filter was in flight. @@ -218,9 +239,11 @@ message FilterStatsProto { // A com.android.server.AlarmManagerService.IdleDispatchEntry object. message IdleDispatchEntryProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string pkg = 2; - optional string tag = 3; + optional string tag = 3 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional string op = 4; // Time when this entry was created, in terms of elapsed realtime. optional int64 entry_creation_realtime = 5; @@ -232,8 +255,10 @@ message IdleDispatchEntryProto { // A com.android.server.AlarmManagerService.InFlight object. message InFlightProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; - optional string tag = 2; + optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int64 when_elapsed_ms = 3; optional .android.app.AlarmManagerProto.AlarmType alarm_type = 4; optional .android.app.PendingIntentProto pending_intent = 5; @@ -244,6 +269,8 @@ message InFlightProto { // A com.android.server.AlarmManagerService.WakeupEvent object. message WakeupEventProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string action = 2; optional int64 when = 3; diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto index c9f7d52ae83f..d0fd0b4bbc4b 100644 --- a/core/proto/android/server/forceappstandbytracker.proto +++ b/core/proto/android/server/forceappstandbytracker.proto @@ -16,12 +16,18 @@ syntax = "proto2"; +import "frameworks/base/core/proto/android/server/statlogger.proto"; + package com.android.server; option java_multiple_files = true; -// Dump from ForceAppStandbyTracker. +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + +// Dump from com.android.server.ForceAppStandbyTracker. message ForceAppStandbyTrackerProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // Whether all apps are forced standby or not. optional bool force_all_apps_standby = 1; @@ -35,10 +41,11 @@ message ForceAppStandbyTrackerProto { repeated int32 temp_power_save_whitelist_app_ids = 4; message RunAnyInBackgroundRestrictedPackages { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string package_name = 2; } - // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND. repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5; @@ -48,6 +55,17 @@ message ForceAppStandbyTrackerProto { // Whether force app standby for small battery device setting is enabled optional bool force_all_apps_standby_for_small_battery = 7; - // Whether device is charging - optional bool is_charging = 8; + // Whether device is plugged in to the charger + optional bool is_plugged_in = 8; + + // Performance stats. + optional StatLoggerProto stats = 9; + + message ExemptedPackage { + optional int32 userId = 1; + optional string package_name = 2; + } + + // Packages that are in the EXEMPT bucket. + repeated ExemptedPackage exempted_packages = 10; } diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 739fca3ceb1d..304e63f28151 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -29,13 +29,18 @@ import "frameworks/base/core/proto/android/net/networkrequest.proto"; import "frameworks/base/core/proto/android/os/bundle.proto"; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; message JobSchedulerServiceDumpProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional ConstantsProto settings = 1; repeated int32 started_users = 2; message RegisteredJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional JobStatusDumpProto dump = 2; @@ -56,6 +61,8 @@ message JobSchedulerServiceDumpProto { // Which uids are currently in the foreground. message PriorityOverride { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; // Use sint32 instead of an enum since priorities can technically be // negative. @@ -71,6 +78,8 @@ message JobSchedulerServiceDumpProto { optional JobPackageTrackerDumpProto package_tracker = 8; message PendingJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional JobStatusDumpProto dump = 2; optional sint32 evaluated_priority = 3; @@ -81,12 +90,18 @@ message JobSchedulerServiceDumpProto { // From a service that has currently active or pending jobs. message ActiveJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + message InactiveJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int64 time_since_stopped_ms = 1; // This is not always provided. optional string stopped_reason = 2; } message RunningJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; // How long this job has been running for. optional int64 running_duration_ms = 2; @@ -119,6 +134,8 @@ message JobSchedulerServiceDumpProto { // A com.android.server.job.JobSchedulerService.Constants object. message ConstantsProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // Minimum # of idle jobs that must be ready in order to force the JMS to // schedule things early. optional int32 min_idle_count = 1; @@ -187,10 +204,16 @@ message ConstantsProto { } message StateControllerProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + message AppIdleController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional bool is_parole_on = 1; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional string source_package_name = 3; @@ -201,9 +224,13 @@ message StateControllerProto { repeated TrackedJob tracked_jobs = 2; } message BackgroundJobsController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional com.android.server.ForceAppStandbyTrackerProto force_app_standby_tracker = 1; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional string source_package_name = 3; @@ -217,6 +244,8 @@ message StateControllerProto { repeated TrackedJob tracked_jobs = 2; } message BatteryController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional bool is_on_stable_power = 1; optional bool is_battery_not_low = 2; @@ -226,15 +255,21 @@ message StateControllerProto { optional int32 last_broadcast_sequence_number = 4; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; } repeated TrackedJob tracked_jobs = 5; } message ConnectivityController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional bool is_connected = 1; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional .android.net.NetworkRequestProto required_network = 3; @@ -242,31 +277,47 @@ message StateControllerProto { repeated TrackedJob tracked_jobs = 2; } message ContentObserverController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; } repeated TrackedJob tracked_jobs = 1; message Observer { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 user_id = 1; message TriggerContentData { - optional string uri = 1; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string uri = 1 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; optional int32 flags = 2; // A // com.android.server.job.controllers.ContentObserverController.JobInstance // object. message JobInstance { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional int64 trigger_content_update_delay_ms = 3; optional int64 trigger_content_max_delay_ms = 4; - repeated string changed_authorities = 5; - repeated string changed_uris = 6; + repeated string changed_authorities = 5 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; + repeated string changed_uris = 6 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; } repeated JobInstance jobs = 3; } @@ -275,10 +326,14 @@ message StateControllerProto { repeated Observer observers = 2; } message DeviceIdleJobsController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // True when in device idle mode. optional bool is_device_idle_mode = 1; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; optional string source_package_name = 3; @@ -293,30 +348,42 @@ message StateControllerProto { repeated TrackedJob tracked_jobs = 2; } message IdleController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional bool is_idle = 1; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; } repeated TrackedJob tracked_jobs = 2; } message StorageController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional bool is_storage_not_low = 1; optional int32 last_broadcast_sequence_number = 2; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; } repeated TrackedJob tracked_jobs = 3; } message TimeController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int64 now_elapsed_realtime = 1; optional int64 time_until_next_delay_alarm_ms = 2; optional int64 time_until_next_deadline_alarm_ms = 3; message TrackedJob { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional JobStatusShortInfoProto info = 1; optional int32 source_uid = 2; @@ -347,6 +414,8 @@ message StateControllerProto { // A com.android.server.job.JobPackageTracker.DataSet object. message DataSetProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int64 start_clock_time_ms = 1; // How much time has elapsed since the DataSet was instantiated. optional int64 elapsed_time_ms = 2; @@ -355,10 +424,14 @@ message DataSetProto { // Represents a com.android.server.job.JobPackageTracker.PackageEntry // object, but with some extra data. message PackageEntryProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 uid = 1; optional string package_name = 2; message State { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int64 duration_ms = 1; optional int32 count = 2; } @@ -377,6 +450,8 @@ message DataSetProto { optional bool active_top = 8; message StopReasonCount { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional .android.app.JobParametersProto.CancelReason reason = 1; optional int32 count = 2; } @@ -390,19 +465,27 @@ message DataSetProto { // Dump from com.android.server.job.GrantedUriPermissions. message GrantedUriPermissionsDumpProto { - optional int32 flags = 1; - optional int32 source_user_id = 2; + option (.android.msg_privacy).dest = DEST_EXPLICIT; + + optional int32 flags = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ]; + optional int32 source_user_id = 2 [ + (.android.privacy).dest = DEST_AUTOMATIC + ]; optional string tag = 3; optional string permission_owner = 4; repeated string uris = 5; } message JobPackageTrackerDumpProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + repeated DataSetProto historical_stats = 1; optional DataSetProto current_stats = 2; } message JobPackageHistoryProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + enum Event { UNKNOWN = 0; START_JOB = 1; @@ -411,12 +494,14 @@ message JobPackageHistoryProto { STOP_PERIODIC_JOB = 4; } message HistoryEvent { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional Event event = 1; optional int64 time_since_event_ms = 2; optional int32 uid = 3; // Job IDs can technically be negative. optional int32 job_id = 4; - optional string tag = 5; + optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ]; // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events. optional .android.app.JobParametersProto.CancelReason stop_reason = 6; } @@ -424,17 +509,23 @@ message JobPackageHistoryProto { } message JobStatusShortInfoProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 calling_uid = 1; // Job IDs can technically be negative. optional int32 job_id = 2; - optional string battery_name = 3; + optional string battery_name = 3 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; } // Dump from a com.android.server.job.controllers.JobStatus object. message JobStatusDumpProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + // The UID that scheduled the job. optional int32 calling_uid = 1; - optional string tag = 2; + optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; // The UID for which the job is being run. optional int32 source_uid = 3; @@ -444,6 +535,8 @@ message JobStatusDumpProto { // Custom dump of android.app.job.JobInfo object. message JobInfo { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional .android.content.ComponentNameProto service = 1; optional bool is_periodic = 2; @@ -461,8 +554,10 @@ message JobStatusDumpProto { optional bool requires_device_idle = 10; message TriggerContentUri { - optional int32 flags = 1; - optional string uri = 2; + optional int32 flags = 1 [ + (.android.privacy).dest = DEST_AUTOMATIC + ]; + optional string uri = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; } repeated TriggerContentUri trigger_content_uris = 11; optional int64 trigger_content_update_delay_ms = 12; @@ -482,6 +577,8 @@ message JobStatusDumpProto { optional int64 max_execution_delay_ms = 21; message Backoff { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + enum Policy { BACKOFF_POLICY_LINEAR = 0; BACKOFF_POLICY_EXPONENTIAL = 1; @@ -524,13 +621,19 @@ message JobStatusDumpProto { // Controllers that are currently tracking the job. repeated TrackingController tracking_controllers = 11; - repeated string changed_authorities = 12; - repeated string changed_uris = 13; + repeated string changed_authorities = 12 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; + repeated string changed_uris = 13 [ + (.android.privacy).dest = DEST_EXPLICIT + ]; optional .android.net.NetworkProto network = 14; // Only the desired data from an android.app.job.JobWorkItem object. message JobWorkItem { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 work_id = 1; optional int32 delivery_count = 2; optional .android.content.IntentProto intent = 3; diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index babbef06fa8d..b5c3ac086512 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -21,13 +21,13 @@ option java_multiple_files = true; import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; -import "frameworks/base/core/proto/android/os/batterymanager.proto"; +import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/os/looper.proto"; import "frameworks/base/core/proto/android/os/powermanager.proto"; import "frameworks/base/core/proto/android/os/worksource.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto"; -import "frameworks/base/core/proto/android/view/display.proto"; +import "frameworks/base/core/proto/android/view/enums.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; message PowerManagerServiceDumpProto { @@ -80,7 +80,7 @@ message PowerManagerServiceDumpProto { // True if the device is plugged into a power source. optional bool is_powered = 5; // The current plug type - optional .android.os.BatteryManagerProto.PlugType plug_type = 6; + optional .android.os.BatteryPluggedStateEnum plug_type = 6; // The current battery level percentage. optional int32 battery_level = 7; // The battery level percentage at the time the dream started. @@ -197,7 +197,7 @@ message WakeLockProto { optional bool is_on_after_release = 2; } - optional .android.os.PowerManagerProto.WakeLockLevel lock_level = 1; + optional .android.os.WakeLockLevelEnum lock_level = 1; optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional WakeLockFlagsProto flags = 3; optional bool is_disabled = 4; @@ -322,7 +322,7 @@ message PowerServiceSettingsAndConfigurationDumpProto { // Use NaN to disable. optional float temporary_screen_auto_brightness_adjustment_setting_override = 37; // The screen state to use while dozing. - optional .android.view.DisplayProto.DisplayState doze_screen_state_override_from_dream_manager = 38; + optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 38; // The screen brightness to use while dozing. optional float dozed_screen_brightness_override_from_dream_manager = 39; // Screen brightness settings limits. diff --git a/core/proto/android/os/batterymanager.proto b/core/proto/android/server/statlogger.proto index 669bf2d0a6e7..fa430d8264d0 100644 --- a/core/proto/android/os/batterymanager.proto +++ b/core/proto/android/server/statlogger.proto @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,19 @@ */ syntax = "proto2"; -package android.os; + +package com.android.server; option java_multiple_files = true; -message BatteryManagerProto { - enum PlugType { - PLUG_TYPE_NONE = 0; - PLUG_TYPE_AC = 1; - PLUG_TYPE_USB = 2; - PLUG_TYPE_WIRELESS = 4; - } +// Dump from StatLogger. +message StatLoggerProto { + message Event { + optional int32 eventId = 1; + optional string label = 2; + optional int32 count = 3; + optional int64 total_duration_micros = 4; + } + + repeated Event events = 1; } diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto index 42fa72ca6bf9..34cb2292fc5f 100644 --- a/core/proto/android/service/battery.proto +++ b/core/proto/android/service/battery.proto @@ -20,35 +20,16 @@ package android.service.battery; option java_multiple_files = true; option java_outer_classname = "BatteryServiceProto"; -import "frameworks/base/core/proto/android/os/batterymanager.proto"; +import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; message BatteryServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; - enum BatteryStatus { - BATTERY_STATUS_INVALID = 0; - BATTERY_STATUS_UNKNOWN = 1; - BATTERY_STATUS_CHARGING = 2; - BATTERY_STATUS_DISCHARGING = 3; - BATTERY_STATUS_NOT_CHARGING = 4; - BATTERY_STATUS_FULL = 5; - } - enum BatteryHealth { - BATTERY_HEALTH_INVALID = 0; - BATTERY_HEALTH_UNKNOWN = 1; - BATTERY_HEALTH_GOOD = 2; - BATTERY_HEALTH_OVERHEAT = 3; - BATTERY_HEALTH_DEAD = 4; - BATTERY_HEALTH_OVER_VOLTAGE = 5; - BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6; - BATTERY_HEALTH_COLD = 7; - } - // If true: UPDATES STOPPED -- use 'reset' to restart optional bool are_updates_stopped = 1; // Plugged status of power sources - optional android.os.BatteryManagerProto.PlugType plugged = 2; + optional android.os.BatteryPluggedStateEnum plugged = 2; // Max current in microamperes. This may be 0 if the device's kernel drivers // don't support it. optional int32 max_charging_current = 3; @@ -58,9 +39,9 @@ message BatteryServiceDumpProto { // Battery capacity in microampere-hours optional int32 charge_counter = 5; // Charging status - optional BatteryStatus status = 6; + optional android.os.BatteryStatusEnum status = 6; // Battery health - optional BatteryHealth health = 7; + optional android.os.BatteryHealthEnum health = 7; // True if the battery is present optional bool is_present = 8; // Charge level, from 0 through "scale" inclusive diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 65df89a9a85e..9013a23664f6 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -25,8 +25,11 @@ import "frameworks/base/core/proto/android/app/notification_channel_group.proto" import "frameworks/base/core/proto/android/app/notificationmanager.proto"; import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/media/audioattributes.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; message NotificationServiceDumpProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + repeated NotificationRecordProto records = 1; optional ZenModeProto zen = 2; @@ -45,7 +48,9 @@ message NotificationServiceDumpProto { } message NotificationRecordProto { - optional string key = 1; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string key = 1 [ (.android.privacy).dest = DEST_EXPLICIT ]; enum State { ENQUEUED = 0; @@ -54,21 +59,25 @@ message NotificationRecordProto { } optional State state = 2; optional int32 flags = 3; - optional string channelId = 4; - optional string sound = 5; + optional string channelId = 4 [ (.android.privacy).dest = DEST_EXPLICIT ]; + optional string sound = 5 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional .android.media.AudioAttributesProto audio_attributes = 6; optional bool can_vibrate = 7; optional bool can_show_light = 8; - optional string group_key = 9; + optional string group_key = 9 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int32 importance = 10; } message ListenersDisablingEffectsProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 hint = 1; repeated ManagedServiceInfoProto listeners = 2; } message ManagedServiceInfoProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional android.content.ComponentNameProto component = 1; optional int32 user_id = 2; optional string service = 3; @@ -77,10 +86,14 @@ message ManagedServiceInfoProto { } message ManagedServicesProto { - optional string caption = 1; + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string caption = 1 [ (.android.privacy).dest = DEST_EXPLICIT ]; message ServiceProto { - repeated string name = 1; + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + repeated string name = 1 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int32 user_id = 2; optional bool is_primary = 3; } @@ -97,9 +110,13 @@ message ManagedServicesProto { } message RankingHelperProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + repeated string notification_signal_extractors = 1; message RecordProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional string package = 1; // Default value is UNKNOWN_UID = USER_NULL = -10000. optional int32 uid = 2; @@ -118,16 +135,72 @@ message RankingHelperProto { repeated RecordProto records_restored_without_uid = 3; } -message ZenModeProto { - enum ZenMode { - ZEN_MODE_OFF = 0; - ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; - ZEN_MODE_NO_INTERRUPTIONS = 2; - ZEN_MODE_ALARMS = 3; +enum ZenMode { + ZEN_MODE_OFF = 0; + ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; + ZEN_MODE_NO_INTERRUPTIONS = 2; + ZEN_MODE_ALARMS = 3; +} + +// An android.service.notification.Condition object. +message ConditionProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional string id = 1; + optional string summary = 2; + optional string line_1 = 3; + optional string line_2 = 4; + optional int32 icon = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + enum State { + // Indicates that Do Not Disturb should be turned off. + STATE_FALSE = 0; + // Indicates that Do Not Disturb should be turned on. + STATE_TRUE = 1; + STATE_UNKNOWN = 2; + STATE_ERROR = 3; } + optional State state = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + optional int32 flags = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; +} + +// An android.service.notification.ZenModeConfig.ZenRule object. +message ZenRuleProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + // Required for automatic (unique). + optional string id = 1; + // Required for automatic. + optional string name = 2; + // Required for automatic. + optional int64 creation_time_ms = 3 [ + (android.privacy).dest = DEST_AUTOMATIC + ]; + optional bool enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Package name, only used for manual rules. + optional string enabler = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // User manually disabled this instance. + optional bool is_snoozing = 6 [ + (android.privacy).dest = DEST_AUTOMATIC + ]; + optional ZenMode zen_mode = 7 [ + (android.privacy).dest = DEST_AUTOMATIC + ]; + + // Required for automatic. + optional string condition_id = 8; + optional ConditionProto condition = 9; + optional android.content.ComponentNameProto component = 10; +} + +// A dump from com.android.server.notification.ZenModeHelper. +message ZenModeProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional ZenMode zen_mode = 1; - repeated string enabled_active_conditions = 2; + repeated ZenRuleProto enabled_active_conditions = 2; optional int32 suppressed_effects = 3; - repeated string suppressors = 4; + repeated android.content.ComponentNameProto suppressors = 4; optional android.app.PolicyProto policy = 5; } diff --git a/core/proto/android/telephony/signalstrength.proto b/core/proto/android/telephony/enums.proto index 366f1d19f46a..60f8d8d78545 100644 --- a/core/proto/android/telephony/signalstrength.proto +++ b/core/proto/android/telephony/enums.proto @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +15,24 @@ */ syntax = "proto2"; -option java_package = "android.telephony"; +package android.telephony; + +option java_outer_classname = "TelephonyProtoEnums"; option java_multiple_files = true; -package android.telephony; +// Data conn. power states, primarily used by android/telephony/DataConnectionRealTimeInfo.java. +enum DataConnectionPowerStateEnum { + DATA_CONNECTION_POWER_STATE_LOW = 1; + DATA_CONNECTION_POWER_STATE_MEDIUM = 2; + DATA_CONNECTION_POWER_STATE_HIGH = 3; + DATA_CONNECTION_POWER_STATE_UNKNOWN = 2147483647; // Java Integer.MAX_VALUE; +} -/** - * An android.telephony.SignalStrength object. - */ -message SignalStrengthProto { - enum StrengthName { +// Signal strength levels, primarily used by android/telephony/SignalStrength.java. +enum SignalStrengthEnum { SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; SIGNAL_STRENGTH_POOR = 1; SIGNAL_STRENGTH_MODERATE = 2; SIGNAL_STRENGTH_GOOD = 3; SIGNAL_STRENGTH_GREAT = 4; - } } diff --git a/core/proto/android/view/display.proto b/core/proto/android/view/display.proto index cac083075ec3..30046c3bdd81 100644 --- a/core/proto/android/view/display.proto +++ b/core/proto/android/view/display.proto @@ -20,24 +20,6 @@ package android.view; option java_multiple_files = true; message DisplayProto { - enum DisplayState { - // The display state is unknown. - DISPLAY_STATE_UNKNOWN = 0; - // The display state is off. - DISPLAY_STATE_OFF = 1; - // The display state is on. - DISPLAY_STATE_ON = 2; - // The display is dozing in a low power state; it is still on but is - // optimized for showing system-provided content while the device is - // non-interactive. - DISPLAY_STATE_DOZE = 3; - // The display is dozing in a suspended low power state; it is still on - // but is optimized for showing static system-provided content while the - // device is non-interactive. - DISPLAY_STATE_DOZE_SUSPEND = 4; - // The display is on and optimized for VR mode. - DISPLAY_STATE_VR = 5; - } enum ColorMode { COLOR_MODE_INVALID = -1; COLOR_MODE_BT601_625 = 1; diff --git a/core/proto/android/view/enums.proto b/core/proto/android/view/enums.proto new file mode 100644 index 000000000000..10785cebe239 --- /dev/null +++ b/core/proto/android/view/enums.proto @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.view; + +option java_outer_classname = "ViewProtoEnums"; +option java_multiple_files = true; + +// Screen states, primarily used by android/view/Display.java. +enum DisplayStateEnum { + // The display state is unknown. + DISPLAY_STATE_UNKNOWN = 0; + // The display state is off. + DISPLAY_STATE_OFF = 1; + // The display state is on. + DISPLAY_STATE_ON = 2; + // The display is dozing in a low power state; it is still on but is + // optimized for showing system-provided content while the device is + // non-interactive. + DISPLAY_STATE_DOZE = 3; + // The display is dozing in a suspended low power state; it is still on + // but is optimized for showing static system-provided content while the + // device is non-interactive. + DISPLAY_STATE_DOZE_SUSPEND = 4; + // The display is on and optimized for VR mode. + DISPLAY_STATE_VR = 5; + // The display is in a suspended full power state; it is still on but the + // CPU is not updating it. + DISPLAY_STATE_ON_SUSPEND = 6; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0861e710a224..2779cd6846d4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -580,6 +580,7 @@ <protected-broadcast android:name="android.app.action.TRANSFER_OWNERSHIP_COMPLETE" /> <protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" /> <protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" /> + <protected-broadcast android:name="android.app.action.STATSD_STARTED" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -3740,15 +3741,6 @@ <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" android:protectionLevel="signature|development|instant|appop" /> - <!-- Allows a regular application to use {@link android.app.Service#startForeground - Service.startForeground}. - <p>Protection level: normal - --> - <permission android:name="android.permission.FOREGROUND_SERVICE" - android:description="@string/permdesc_foregroundService" - android:label="@string/permlab_foregroundService" - android:protectionLevel="normal|instant" /> - <!-- @hide Allows system components to access all app shortcuts. --> <permission android:name="android.permission.ACCESS_SHORTCUTS" android:protectionLevel="signature" /> @@ -3975,6 +3967,7 @@ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert" android:excludeFromRecents="true" android:process=":ui" + android:label="@string/harmful_app_warning_title" android:exported="false"> </activity> diff --git a/core/res/res/drawable/red_shield.xml b/core/res/res/drawable/red_shield.xml new file mode 100644 index 000000000000..7f425c78c708 --- /dev/null +++ b/core/res/res/drawable/red_shield.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="34dp" + android:height="37dp" + android:viewportWidth="34" + android:viewportHeight="37"> + + <group + android:translateX="-3.000000" + android:translateY="-2.000000"> + <path + android:fillType="evenOdd" + android:strokeWidth="1" + android:pathData="M 0 0 H 40 V 40 H 0 V 0 Z" /> + <path + android:fillColor="#D0021B" + android:fillType="evenOdd" + android:strokeWidth="1" + android:pathData="M35.5858891,6.865 C27.841629,3.02166667 19.6666667,2 19.6666667,2 C19.6666667,2 +11.4917044,3.02166667 3.74744428,6.865 C3.25808614,8.915 3,11.0533333 +3,13.2533333 C3,15.515 3.27484498,17.715 3.79269315,19.8216667 +C4.89374895,24.3033333 7.09753645,28.355 10.1023965,31.6783333 +C12.7385621,34.5983333 15.9964806,36.955 19.6666667,38.5433333 +C23.3368527,36.955 26.5947712,34.5983333 29.2326127,31.6783333 +C32.2357969,28.355 34.4395844,24.3033333 35.5423161,19.8216667 +C36.0584884,17.715 36.3333333,15.515 36.3333333,13.2533333 +C36.3333333,11.0533333 36.0769231,8.915 35.5858891,6.865 M21.3333333,27.8333333 +L18,27.8333333 L18,24.5 L21.3333333,24.5 L21.3333333,27.8333333 +L21.3333333,27.8333333 Z M21.3333333,22 L18,22 L18,12 L21.3333333,12 +L21.3333333,22 L21.3333333,22 Z" /> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout/harmful_app_warning_dialog.xml b/core/res/res/layout/harmful_app_warning_dialog.xml new file mode 100644 index 000000000000..d41691f4bb1b --- /dev/null +++ b/core/res/res/layout/harmful_app_warning_dialog.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2018, Google Inc. + * + * 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. + */ +--> + +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="true"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="@dimen/harmful_app_padding_top" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/harmful_app_name_padding_bottom" + android:paddingLeft="@dimen/harmful_app_name_padding_left" + android:paddingRight="@dimen/harmful_app_name_padding_right" + android:paddingTop="@dimen/harmful_app_name_padding_top" + android:orientation="horizontal"> + + <ImageView + android:layout_width="@dimen/harmful_app_icon_size" + android:layout_height="@dimen/harmful_app_icon_size" + android:scaleType="fitCenter" + android:src="@drawable/red_shield"/> + + <TextView + android:id="@+id/app_name_text" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:textColor="@color/primary_text_material_light" + android:textSize="@dimen/text_size_subhead_material" + android:paddingLeft="@dimen/harmful_app_icon_name_padding"> + </TextView> + </LinearLayout> + + <TextView + android:id="@+id/message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/harmful_app_message_padding_left" + android:paddingRight="@dimen/harmful_app_message_padding_right" + android:paddingBottom="@dimen/harmful_app_message_padding_bottom" + android:lineSpacingMultiplier="@dimen/harmful_app_message_line_spacing_modifier" + android:textSize="@dimen/text_size_body_1_material"/> + </LinearLayout> +</ScrollView>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 354d65811164..5184dda70252 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5878,6 +5878,13 @@ <attr name="insetBottom" format="fraction|dimension" /> </declare-styleable> + <!-- Drawable used to draw animated images (gif) --> + <declare-styleable name="AnimatedImageDrawable"> + <!-- Identifier of the image file. This attribute is mandatory. + It must be an image file with multiple frames, e.g. gif or webp --> + <attr name="src" /> + </declare-styleable> + <!-- Drawable used to draw bitmaps. --> <declare-styleable name="BitmapDrawable"> <!-- Identifier of the bitmap file. This attribute is mandatory. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 2c824ea01be7..e57b8b242490 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -33,6 +33,10 @@ <dimen name="toast_y_offset">24dp</dimen> <!-- Height of the status bar --> <dimen name="status_bar_height">24dp</dimen> + <!-- Height of area above QQS where battery/time go --> + <dimen name="quick_qs_offset_height">48dp</dimen> + <!-- Total height of QQS (quick_qs_offset_height + 128) --> + <dimen name="quick_qs_total_height">176dp</dimen> <!-- Height of the bottom navigation / system bar. --> <dimen name="navigation_bar_height">48dp</dimen> <!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height --> @@ -635,4 +639,26 @@ <!-- Size of thumbnail used in the cross profile apps animation --> <dimen name="cross_profile_apps_thumbnail_size">72dp</dimen> + <!-- Padding between the title and content in the harmful app dialog --> + <dimen name="harmful_app_padding_top">10dp</dimen> + <!-- Bottom padding for the "app name" section of the harmful app dialog --> + <dimen name="harmful_app_name_padding_bottom">20dp</dimen> + <!-- Left padding for the "app name" section of the harmful app dialog --> + <dimen name="harmful_app_name_padding_left">24dp</dimen> + <!-- Right padding for the "app name" section of the harmful app dialog --> + <dimen name="harmful_app_name_padding_right">24dp</dimen> + <!-- Top padding for the "app name" section of the harmful app dialog --> + <dimen name="harmful_app_name_padding_top">8dp</dimen> + <!-- Padding between the icon and app name in the harmful app dialog --> + <dimen name="harmful_app_icon_name_padding">20dp</dimen> + <!-- The size of the icon on the harmful app dialog --> + <dimen name="harmful_app_icon_size">44dp</dimen> + <!-- Left padding for the message section of the harmful app dialog --> + <dimen name="harmful_app_message_padding_left">24dp</dimen> + <!-- Right padding for the message section of the harmful app dialog --> + <dimen name="harmful_app_message_padding_right">24dp</dimen> + <!-- Bottom padding for the message section of the harmful app dialog --> + <dimen name="harmful_app_message_padding_bottom">24dp</dimen> + <!-- Line spacing modifier for the message field of the harmful app dialog --> + <item name="harmful_app_message_line_spacing_modifier" type="dimen">1.22</item> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 69d96fcaf9dd..3cde765e04cb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -916,11 +916,6 @@ <string name="permdesc_persistentActivity" product="default">Allows the app to make parts of itself persistent in memory. This can limit memory available to other apps slowing down the phone.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_foregroundService">run foreground service</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_getPackageSize">measure app storage space</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string> @@ -4847,12 +4842,12 @@ <!--Battery saver warning. STOPSHIP: Remove it eventually. --> <string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string> - <!-- Label for the uninstall button on the harmful app warning dialog. --> - <string name="harmful_app_warning_uninstall">Uninstall</string> - <!-- Label for the launch anyway button on the harmful app warning dialog. --> - <string name="harmful_app_warning_launch_anyway">Launch anyway</string> - <!-- Title for the harmful app warning dialog. --> - <string name="harmful_app_warning_title">Uninstall harmful app?</string> + <!-- Label for the uninstall button on the harmful app warning dialog. [CHAR LIMIT=20] --> + <string name="harmful_app_warning_uninstall">UNINSTALL</string> + <!-- Label for the open anyway button on the harmful app warning dialog. [CHAR LIMIT=20] --> + <string name="harmful_app_warning_open_anyway">OPEN ANYWAY</string> + <!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] --> + <string name="harmful_app_warning_title">Harmful app detected</string> <!-- Text describing a permission request for one app to show another app's slices [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 710bbfbb1b14..9442d15dfff2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1636,6 +1636,7 @@ <java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" /> <java-symbol type="dimen" name="navigation_bar_width_car_mode" /> <java-symbol type="dimen" name="status_bar_height" /> + <java-symbol type="dimen" name="quick_qs_total_height" /> <java-symbol type="drawable" name="ic_jog_dial_sound_off" /> <java-symbol type="drawable" name="ic_jog_dial_sound_on" /> <java-symbol type="drawable" name="ic_jog_dial_unlock" /> @@ -3228,8 +3229,9 @@ <java-symbol type="string" name="shortcut_disabled_reason_unknown" /> <java-symbol type="string" name="harmful_app_warning_uninstall" /> - <java-symbol type="string" name="harmful_app_warning_launch_anyway" /> + <java-symbol type="string" name="harmful_app_warning_open_anyway" /> <java-symbol type="string" name="harmful_app_warning_title" /> + <java-symbol type="layout" name="harmful_app_warning_dialog" /> <java-symbol type="string" name="config_defaultAssistantAccessPackage" /> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 53c22f624e8c..7d5c60aa292b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -51,7 +51,6 @@ <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 2f747ec30ef9..733f7a107fa8 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -377,6 +377,7 @@ public class SettingsBackupTest { Settings.Global.TETHER_SUPPORTED, Settings.Global.TEXT_CLASSIFIER_CONSTANTS, Settings.Global.THEATER_MODE_ON, + Settings.Global.TIME_ONLY_MODE_ENABLED, Settings.Global.TRANSITION_ANIMATION_SCALE, Settings.Global.TRUSTED_SOUND, Settings.Global.TZINFO_UPDATE_CONTENT_URL, diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index 4d34721b5aba..ce3ffb9e58ab 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -772,7 +772,8 @@ public class BstatsCpuTimesValidationTest { latch.countDown(); } }); - launchIntent.putExtras(extras); + launchIntent.putExtras(extras) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); sContext.startActivity(launchIntent); if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { if (binders[0] == null) { diff --git a/docs/html/reference/images/text/style/clickablespan.png b/docs/html/reference/images/text/style/clickablespan.png Binary files differnew file mode 100644 index 000000000000..8e3e6bfc6b5c --- /dev/null +++ b/docs/html/reference/images/text/style/clickablespan.png diff --git a/docs/html/reference/images/text/style/customquotespan.png b/docs/html/reference/images/text/style/customquotespan.png Binary files differnew file mode 100644 index 000000000000..27f521a1b0d0 --- /dev/null +++ b/docs/html/reference/images/text/style/customquotespan.png diff --git a/docs/html/reference/images/text/style/defaultquotespan.png b/docs/html/reference/images/text/style/defaultquotespan.png Binary files differnew file mode 100644 index 000000000000..6c5a41f35e0b --- /dev/null +++ b/docs/html/reference/images/text/style/defaultquotespan.png diff --git a/docs/html/reference/images/text/style/ltralignmentspan.png b/docs/html/reference/images/text/style/ltralignmentspan.png Binary files differnew file mode 100644 index 000000000000..6ee5943da235 --- /dev/null +++ b/docs/html/reference/images/text/style/ltralignmentspan.png diff --git a/docs/html/reference/images/text/style/rtlalignmentspan.png b/docs/html/reference/images/text/style/rtlalignmentspan.png Binary files differnew file mode 100644 index 000000000000..952258ded842 --- /dev/null +++ b/docs/html/reference/images/text/style/rtlalignmentspan.png diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 3de050b5ffa5..3ead5911d890 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -339,6 +339,16 @@ public final class ImageDecoder implements AutoCloseable { public String getMimeType() { return mDecoder.getMimeType(); } + + /** + * Whether the image is animated. + * + * <p>Calling {@link #decodeDrawable} will return an + * {@link AnimatedImageDrawable}.</p> + */ + public boolean isAnimated() { + return mDecoder.mAnimated; + } }; /** @@ -454,6 +464,10 @@ public final class ImageDecoder implements AutoCloseable { mCloseGuard.warnIfOpen(); } + // Avoid closing these in finalizer. + mInputStream = null; + mAssetFd = null; + close(); } finally { super.finalize(); diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index 3034a1056000..0ec19f9a4aee 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -20,14 +20,26 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.InflateException; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.ImageDecoder; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.SystemClock; +import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; +import android.util.TypedValue; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import libcore.io.IoUtils; import libcore.util.NativeAllocationRegistry; @@ -35,17 +47,38 @@ import libcore.util.NativeAllocationRegistry; import java.io.IOException; import java.io.InputStream; import java.lang.Runnable; +import java.util.ArrayList; /** - * @hide + * {@link Drawable} for drawing animated images (like GIF). + * + * <p>Created by {@link ImageDecoder#decodeDrawable}. A user needs to call + * {@link #start} to start the animation.</p> */ -public class AnimatedImageDrawable extends Drawable implements Animatable { - private final long mNativePtr; - private final InputStream mInputStream; - private final AssetFileDescriptor mAssetFd; +public class AnimatedImageDrawable extends Drawable implements Animatable2 { + private int mIntrinsicWidth; + private int mIntrinsicHeight; + + private boolean mStarting; + private boolean mRunning; + + private Handler mHandler; + + private class State { + State(long nativePtr, InputStream is, AssetFileDescriptor afd) { + mNativePtr = nativePtr; + mInputStream = is; + mAssetFd = afd; + } + + public final long mNativePtr; + + // These just keep references so the native code can continue using them. + private final InputStream mInputStream; + private final AssetFileDescriptor mAssetFd; + } - private final int mIntrinsicWidth; - private final int mIntrinsicHeight; + private State mState; private Runnable mRunnable = new Runnable() { @Override @@ -55,6 +88,95 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { }; /** + * Pass this to {@link #setLoopCount} to loop infinitely. + * + * <p>{@link Animatable2.AnimationCallback#onAnimationEnd} will never be + * called unless there is an error.</p> + */ + public static final int LOOP_INFINITE = -1; + + /** + * Specify the number of times to loop the animation. + * + * <p>By default, the loop count in the encoded data is respected.</p> + */ + public void setLoopCount(int loopCount) { + if (mState == null) { + throw new IllegalStateException("called setLoopCount on empty AnimatedImageDrawable"); + } + nSetLoopCount(mState.mNativePtr, loopCount); + } + + /** + * Create an empty AnimatedImageDrawable. + */ + public AnimatedImageDrawable() { + mState = null; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) + throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs, theme); + + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedImageDrawable); + updateStateFromTypedArray(a, mSrcDensityOverride); + } + + private void updateStateFromTypedArray(TypedArray a, int srcDensityOverride) + throws XmlPullParserException { + final Resources r = a.getResources(); + final int srcResId = a.getResourceId(R.styleable.AnimatedImageDrawable_src, 0); + if (srcResId != 0) { + // Follow the density handling in BitmapDrawable. + final TypedValue value = new TypedValue(); + r.getValueForDensity(srcResId, srcDensityOverride, value, true); + if (srcDensityOverride > 0 && value.density > 0 + && value.density != TypedValue.DENSITY_NONE) { + if (value.density == srcDensityOverride) { + value.density = r.getDisplayMetrics().densityDpi; + } else { + value.density = + (value.density * r.getDisplayMetrics().densityDpi) / srcDensityOverride; + } + } + + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + + Drawable drawable = null; + try { + InputStream is = r.openRawResource(srcResId, value); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { + if (!info.isAnimated()) { + throw new IllegalArgumentException("image is not animated"); + } + }); + } catch (IOException e) { + throw new XmlPullParserException(a.getPositionDescription() + + ": <animated-image> requires a valid 'src' attribute", null, e); + } + + if (!(drawable instanceof AnimatedImageDrawable)) { + throw new XmlPullParserException(a.getPositionDescription() + + ": <animated-image> did not decode animated"); + } + + // Transfer the state of other to this one. other will be discarded. + AnimatedImageDrawable other = (AnimatedImageDrawable) drawable; + mState = other.mState; + other.mState = null; + mIntrinsicWidth = other.mIntrinsicWidth; + mIntrinsicHeight = other.mIntrinsicHeight; + } + } + + /** * @hide * This should only be called by ImageDecoder. * @@ -80,30 +202,14 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { mIntrinsicHeight = cropRect.height(); } - mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); - mInputStream = inputStream; - mAssetFd = afd; + mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect), + inputStream, afd); // FIXME: Use the right size for the native allocation. long nativeSize = 200; NativeAllocationRegistry registry = new NativeAllocationRegistry( AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); - registry.registerNativeAllocation(this, mNativePtr); - } - - @Override - protected void finalize() throws Throwable { - // FIXME: It's a shame that we have *both* a native finalizer and a Java - // one. The native one is necessary to report how much memory is being - // used natively, and this one is necessary to close the input. An - // alternative might be to read the entire stream ahead of time, so we - // can eliminate the Java finalizer. - try { - IoUtils.closeQuietly(mInputStream); - IoUtils.closeQuietly(mAssetFd); - } finally { - super.finalize(); - } + registry.registerNativeAllocation(mState, mState.mNativePtr); } @Override @@ -116,13 +222,34 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { return mIntrinsicHeight; } + // nDraw returns -2 if the animation is not running. + private static final int NOT_RUNNING = -2; + @Override public void draw(@NonNull Canvas canvas) { - long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper()); + if (mState == null) { + throw new IllegalStateException("called draw on empty AnimatedImageDrawable"); + } + + if (mStarting) { + mStarting = false; + + postOnAnimationStart(); + + mRunning = true; + } + + long nextUpdate = nDraw(mState.mNativePtr, canvas.getNativeCanvasWrapper()); // a value <= 0 indicates that the drawable is stopped or that renderThread // will manage the animation if (nextUpdate > 0) { scheduleSelf(mRunnable, nextUpdate); + } else if (nextUpdate == NOT_RUNNING) { + // -2 means the animation ended, when drawn in software mode. + if (mRunning) { + postOnAnimationEnd(); + mRunning = false; + } } } @@ -132,19 +259,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { throw new IllegalArgumentException("Alpha must be between 0 and" + " 255! provided " + alpha); } - nSetAlpha(mNativePtr, alpha); + + if (mState == null) { + throw new IllegalStateException("called setAlpha on empty AnimatedImageDrawable"); + } + + nSetAlpha(mState.mNativePtr, alpha); invalidateSelf(); } @Override public int getAlpha() { - return nGetAlpha(mNativePtr); + if (mState == null) { + throw new IllegalStateException("called getAlpha on empty AnimatedImageDrawable"); + } + return nGetAlpha(mState.mNativePtr); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { + if (mState == null) { + throw new IllegalStateException("called setColorFilter on empty AnimatedImageDrawable"); + } + long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance(); - nSetColorFilter(mNativePtr, nativeFilter); + nSetColorFilter(mState.mNativePtr, nativeFilter); invalidateSelf(); } @@ -153,29 +292,116 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { return PixelFormat.TRANSLUCENT; } - // TODO: Add a Constant State? - // @Override - // public @Nullable ConstantState getConstantState() {} - - // Animatable overrides + /** + * Return whether the animation is currently running. + * + * <p>When this drawable is created, this will return {@code false}. A client + * needs to call {@link #start} to start the animation.</p> + */ @Override public boolean isRunning() { - return nIsRunning(mNativePtr); + return mRunning; } + /** + * Start the animation. + * + * <p>Does nothing if the animation is already running. + * + * <p>If the animation starts, this will call + * {@link Animatable2.AnimationCallback#onAnimationStart}.</p> + */ @Override public void start() { - if (nStart(mNativePtr)) { + if (mState == null) { + throw new IllegalStateException("called start on empty AnimatedImageDrawable"); + } + + if (nStart(mState.mNativePtr)) { + mStarting = true; invalidateSelf(); } } + /** + * Stop the animation. + * + * <p>If the animation is stopped, it will continue to display the frame + * it was displaying when stopped.</p> + */ @Override public void stop() { - nStop(mNativePtr); + if (mState == null) { + throw new IllegalStateException("called stop on empty AnimatedImageDrawable"); + } + nStop(mState.mNativePtr); + mRunning = false; } + // Animatable2 overrides + private ArrayList<Animatable2.AnimationCallback> mAnimationCallbacks = null; + + @Override + public void registerAnimationCallback(@NonNull AnimationCallback callback) { + if (callback == null) { + return; + } + + if (mAnimationCallbacks == null) { + mAnimationCallbacks = new ArrayList<Animatable2.AnimationCallback>(); + nSetOnAnimationEndListener(mState.mNativePtr, this); + } + + mAnimationCallbacks.add(callback); + } + + @Override + public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) { + if (callback == null || mAnimationCallbacks == null) { + return false; + } + + return mAnimationCallbacks.remove(callback); + } + + @Override + public void clearAnimationCallbacks() { + mAnimationCallbacks = null; + } + + private void postOnAnimationStart() { + if (mAnimationCallbacks == null) { + return; + } + + getHandler().post(() -> { + for (Animatable2.AnimationCallback callback : mAnimationCallbacks) { + callback.onAnimationStart(this); + } + }); + } + + private void postOnAnimationEnd() { + if (mAnimationCallbacks == null) { + return; + } + + getHandler().post(() -> { + for (Animatable2.AnimationCallback callback : mAnimationCallbacks) { + callback.onAnimationEnd(this); + } + }); + } + + private Handler getHandler() { + if (mHandler == null) { + mHandler = new Handler(Looper.getMainLooper()); + } + return mHandler; + } + + private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) throws IOException; @@ -185,7 +411,12 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { private static native int nGetAlpha(long nativePtr); private static native void nSetColorFilter(long nativePtr, long nativeFilter); private static native boolean nIsRunning(long nativePtr); + // Return whether the animation started. private static native boolean nStart(long nativePtr); private static native void nStop(long nativePtr); + private static native void nSetLoopCount(long nativePtr, int loopCount); + // Pass the drawable down to native so it can call onAnimationEnd. + private static native void nSetOnAnimationEndListener(long nativePtr, + @Nullable AnimatedImageDrawable drawable); private static native long nNativeByteSize(long nativePtr); } diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java index eea7048ca534..0ee9071f4d06 100644 --- a/graphics/java/android/graphics/drawable/DrawableInflater.java +++ b/graphics/java/android/graphics/drawable/DrawableInflater.java @@ -185,6 +185,8 @@ public final class DrawableInflater { return new BitmapDrawable(); case "nine-patch": return new NinePatchDrawable(); + case "animated-image": + return new AnimatedImageDrawable(); default: return null; } diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 70d52164ff74..251b2e773cfb 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,7 +145,6 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], - static_libs: ["libgmock"], target: { android: { srcs: [ @@ -172,7 +171,6 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", - "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 0485625e81e8..a5698af18f6b 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -73,6 +73,7 @@ static volatile int32_t gCount = 0; const char* AssetManager::RESOURCES_FILENAME = "resources.arsc"; const char* AssetManager::IDMAP_BIN = "/system/bin/idmap"; const char* AssetManager::OVERLAY_DIR = "/vendor/overlay"; +const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; const char* AssetManager::TARGET_PACKAGE_NAME = "android"; const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk"; diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a558ff7ccfc1..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,31 +36,6 @@ namespace android { -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This is already swapped to host - // endianness. - ResTable_config config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; - AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -69,7 +44,6 @@ bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast<uint32_t>(-1)); } @@ -105,7 +79,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); + package_group->packages_.push_back(package.get()); package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); // Add the package name -> build time ID mappings. @@ -120,7 +94,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); + const std::string& package_name = iter->packages_[0]->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -134,20 +108,17 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group : package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), - package.loaded_package_->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + for (const auto& package_group: package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; } } @@ -186,54 +157,52 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { - RebuildFilterList(); InvalidateCaches(static_cast<uint32_t>(diff)); } } std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { + bool exclude_mipmap) { ATRACE_CALL(); std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + package->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) const { + bool merge_equivalent_languages) { ATRACE_CALL(); std::set<std::string> locales; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); + package->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, - Asset::AccessMode mode) const { +std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const { +std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -267,7 +236,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con // is inconsistent for split APKs. std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) const { + ApkAssetsCookie* out_cookie) { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); @@ -286,8 +255,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + ApkAssetsCookie cookie, Asset::AccessMode mode) { ATRACE_CALL(); if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { return {}; @@ -296,13 +264,14 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - FindEntryResult* out_entry) const { + bool stop_at_first_match, FindEntryResult* out_entry) { + ATRACE_CALL(); + // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; + ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -316,135 +285,53 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_idx = get_entry_id(resid); + const uint16_t entry_id = get_entry_id(resid); - const uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + const uint8_t idx = package_ids_[package_id]; + if (idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); - + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; - const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; - const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; - - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; - - for (size_t pi = 0; pi < package_count; pi++) { - const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; - const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); - if (UNLIKELY(type_spec == nullptr)) { + uint32_t cumulated_flags = 0u; + + const PackageGroup& package_group = package_groups_[idx]; + const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; + for (size_t i = 0; i < package_count; i++) { + const LoadedPackage* loaded_package = package_group.packages_[i]; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { continue; } - uint16_t local_entry_idx = entry_idx; + cumulated_flags |= current_entry.type_flags; - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } - } - - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); - - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - if (use_fast_path) { - const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; - } - } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } - } + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { + best_entry = current_entry; + best_cookie = package_group.cookies_[i]; + if (stop_at_first_match) { + break; } } } - if (UNLIKELY(best_cookie == kInvalidCookie)) { + if (best_cookie == kInvalidCookie) { return kInvalidCookie; } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; - } - - out_entry->entry = best_entry; - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); FindEntryResult entry; @@ -454,8 +341,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); if (package == nullptr) { return false; } @@ -483,7 +369,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -497,7 +383,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const { + uint32_t* out_flags) { ATRACE_CALL(); FindEntryResult entry; @@ -516,7 +402,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -528,7 +414,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -536,14 +422,16 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const { + uint32_t* out_last_reference) { ATRACE_CALL(); constexpr const int kMaxIterations = 20; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - *out_last_reference = in_out_value->data; + if (out_last_reference != nullptr) { + *out_last_reference = in_out_value->data; + } uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); @@ -604,8 +492,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -637,8 +524,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, - resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); return nullptr; } @@ -657,8 +543,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); return nullptr; } } @@ -697,8 +582,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -754,7 +638,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { + const std::string& fallback_package) { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -786,8 +670,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package_impl : package_group.packages_) { - const LoadedPackage* package = package_impl.loaded_package_; + for (const LoadedPackage* package : package_group.packages_) { if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -809,32 +692,6 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } -void AssetManager2::RebuildFilterList() { - for (PackageGroup& group : package_groups_) { - for (ConfiguredPackage& impl : group.packages_) { - // Destroy it. - impl.filtered_configs_.~ByteBucketArray(); - - // Re-create it. - new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>(); - - // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); - } - } - }); - } - } -} - void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -1015,7 +872,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { + uint32_t* out_last_ref) { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index f912af4f7190..60e3845d98a9 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,18 +20,13 @@ #include <log/log.h> -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" +#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); -} - class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { public: @@ -49,53 +44,58 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { + : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> { public: - BagAttributeFinder(const ResolvedBag* bag) - : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, - bag != nullptr ? bag->entries + bag->entry_count : nullptr) { - } + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} - inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { - return entry->key; + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_end = + def_style_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; + ssize_t block = -1; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,14 +122,15 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, + value.data); } } else { - const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_entry != def_style_end) { + block = def_style_entry->stringBlock; + type_set_flags = def_style_type_set_flags; + value = def_style_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -139,26 +140,22 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + // If we still don't have a value for this attribute, try to find + // it in the theme! + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -172,7 +169,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = -1; } if (kDebugStyles) { @@ -182,7 +179,9 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -196,80 +195,90 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", + theme, def_style_attr, def_style_res, xml_parser); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; + def_style_res = value.data; } } } - // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { value.dataType = Res_value::TYPE_NULL; } } - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; + style = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_attr_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_attr_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_attr_end = + def_style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_attr_start, + def_style_attr_end); // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); + const ResTable::bag_entry* style_attr_start = nullptr; + uint32_t style_type_set_flags = 0; + bag_off = + style != 0 + ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) + : -1; + style_type_set_flags |= style_bag_type_set_flags; + const ResTable::bag_entry* const style_attr_end = + style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); // Retrieve the XML attributes, if requested. + static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); + const size_t xml_attr_end = + xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -280,8 +289,8 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -293,7 +302,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_finder.end()) { + if (xml_attr_idx != xml_attr_end) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -303,12 +312,12 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); - if (entry != xml_style_attr_finder.end()) { + const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); + if (style_attr_entry != style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + block = style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -317,25 +326,25 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { + const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_attr_entry != def_style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + block = def_style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = def_style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -343,15 +352,14 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -367,7 +375,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } if (kDebugStyles) { @@ -377,7 +385,9 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -392,28 +402,36 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, + uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; + // Now lock down the resource object and start pulling stuff from it. + res->lock(); + // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); + static const ssize_t kXmlBlock = 0x10000000; + // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -432,27 +450,28 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + // printf("Resolving attribute reference\n"); + ssize_t new_block = res->resolveReference(&value, block, &resid, + &type_set_flags, &config); + if (new_block >= 0) block = new_block; } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) + : static_cast<uint32_t>(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -466,6 +485,8 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u out_values += STYLE_NUM_ENTRIES; } + res->unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,6 +44,44 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; +// Element of a TypeSpec array. See TypeSpec. +struct Type { + // The configuration for which this type defines entries. + // This is already converted to host endianness. + ResTable_config configuration; + + // Pointer to the mmapped data where entry definitions are kept. + const ResTable_type* type; +}; + +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration + // that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const Type types[0]; +}; + +// TypeSpecPtr points to the block of memory that holds +// a TypeSpec struct, followed by an array of Type structs. +// TypeSpecPtr is a managed pointer that knows how to delete +// itself. +using TypeSpecPtr = util::unique_cptr<TypeSpec>; + namespace { // Builder that helps accumulate Type structs and then create a single @@ -57,22 +95,21 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - types_.push_back(type); + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; - if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) < - types_.size()) { + if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { return {}; } - TypeSpec* type_spec = - (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); } @@ -81,7 +118,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector<const ResTable_type*> types_; + std::vector<Type> types_; }; } // namespace @@ -125,17 +162,18 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; return false; } @@ -143,7 +181,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry at offset " << entry_offset + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large. No room for ResTable_entry."; return false; } @@ -153,13 +191,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too large."; return false; } @@ -167,7 +205,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx << " for type " << (int)type->id << "."; return false; } @@ -176,12 +214,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx << " is too large."; return false; } @@ -190,76 +228,119 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; return false; } } return true; } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; - } - return GetEntryFromOffset(type_chunk, entry_offset); -} - -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { + const ResTable_config* best_config = nullptr; + const ResTable_type* best_type = nullptr; + uint32_t best_offset = 0; + + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; + + if (type->configuration.match(config) && + (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast<const ResTable_sparseTypeEntry*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } - // Check if there is the desired entry in this type. + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = uint32_t{dtohs(result->offset)} * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; + } - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast<const ResTable_sparseTypeEntry*>( + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { - // No entry found. - return ResTable_type::NO_ENTRY; + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + // There is an entry for this resource, record it. + best_offset = offset; + } + + best_config = &type->configuration; + best_type = type_chunk; } + } - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + if (best_type == nullptr) { + return false; } - // This type is encoded as a dense array. - if (entry_index >= entry_count) { - // This entry cannot be here. - return ResTable_type::NO_ENTRY; + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; } - const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); + out_entry->entry = best_entry; + out_entry->config = best_config; + out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); + out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); + return true; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (UNLIKELY(ptr == nullptr)) { + return false; } - return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -267,7 +348,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -288,11 +369,8 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); } } } @@ -302,12 +380,10 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; if (type_spec != nullptr) { - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config configuration; - configuration.copyFromDtoH((*iter)->config); + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -335,17 +411,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; - size_t entry_count = dtohl(type->entryCount); + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize)); + reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset); + const ResTable_entry* entry = + reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) + + dtohl(type->type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -357,7 +433,8 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -605,6 +682,26 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (UNLIKELY(type_id == 0)) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; +} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index ecc5dc1ad331..08da7319de85 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -60,6 +60,7 @@ public: static const char* RESOURCES_FILENAME; static const char* IDMAP_BIN; static const char* OVERLAY_DIR; + static const char* PRODUCT_OVERLAY_DIR; /* * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index ef08897d997a..b033137b4764 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,8 +69,6 @@ struct ResolvedBag { Entry entries[0]; }; -struct FindEntryResult; - // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -129,7 +127,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + bool exclude_mipmap = false); // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -138,24 +136,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set<std::string> GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false) const; + bool merge_equivalent_languages = false); // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const; + std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode); // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const; + std::unique_ptr<AssetDir> OpenDir(const std::string& dirname); // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -163,24 +161,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr) const; + ApkAssetsCookie* out_cookie = nullptr); // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + bool GetResourceName(uint32_t resid, ResourceName* out_name); // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -188,7 +186,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; + const std::string& fallback_package = {}); // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -201,7 +199,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + uint32_t* out_flags); // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -217,7 +215,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + uint32_t* out_last_reference); // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -235,9 +233,9 @@ class AssetManager2 { std::unique_ptr<Theme> NewTheme(); template <typename Func> - void ForEachPackage(Func func) const { + void ForEachPackage(Func func) { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), + func(package_group.packages_.front()->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -262,7 +260,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -272,43 +270,13 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); - // Triggers the re-construction of lists of types that match the set configuration. - // This should always be called when mutating the AssetManager's configuration or ApkAssets set. - void RebuildFilterList(); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector<ResTable_config> configurations; - std::vector<const ResTable_type*> types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray<FilteredConfigGroup> filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - // The set of packages that make-up this group. - std::vector<ConfiguredPackage> packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. + std::vector<const LoadedPackage*> packages_; std::vector<ApkAssetsCookie> cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -382,7 +350,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + uint32_t* out_last_ref = nullptr); private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index 03fad4947dfe..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,7 +58,6 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); - inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -202,11 +201,6 @@ Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) { return end_; } -template <typename Derived, typename Iterator> -Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() { - return end_; -} - } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..69b760414846 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,8 +17,7 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" +#include <androidfw/ResourceTypes.h> namespace android { @@ -43,19 +42,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 35ae5fcd9e7b..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,40 +41,32 @@ class DynamicPackageEntry { int package_id = 0; }; -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; - - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { - if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; - } - - const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1); - return flags[entry_index]; - } +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry = nullptr; + + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; + + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table = nullptr; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; }; -// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of -// ResTable_type pointers. -// TypeSpecPtr is a managed pointer that knows how to delete itself. -using TypeSpecPtr = util::unique_cptr<TypeSpec>; +struct TypeSpec; +class LoadedArsc; class LoadedPackage { public: @@ -84,6 +76,9 @@ class LoadedPackage { ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -91,12 +86,6 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); - // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -146,32 +135,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; - // type_idx is TT - 1 from 0xPPTTEEEE. - inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - return type_specs_[type_index - type_id_offset_].get(); - } - - template <typename Func> - void ForEachTypeSpec(Func f) const { - for (size_t i = 0; i < type_specs_.size(); i++) { - const TypeSpecPtr& ptr = type_specs_[i]; - if (ptr != nullptr) { - uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } - f(ptr.get(), type_id - 1); - } - } - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); + bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; + ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -181,7 +152,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray<TypeSpecPtr> type_specs_; + ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; }; @@ -209,20 +180,25 @@ class LoadedArsc { return &global_string_pool_; } - // Gets a pointer to the package with the specified package ID, or nullptr if no such package - // exists. - const LoadedPackage* GetPackageById(uint8_t package_id) const; + // Finds the resource with ID `resid` with the best value for configuration `config`. + // The parameter `out_entry` will be filled with the resulting resource entry. + // The resource entry can be a simple entry (ResTable_entry) or a complex bag + // (ResTable_entry_map). + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { - return packages_; - } + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { + return packages_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h deleted file mode 100644 index 64924f433245..000000000000 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROIDFW_MUTEXGUARD_H -#define ANDROIDFW_MUTEXGUARD_H - -#include <mutex> -#include <type_traits> - -#include "android-base/macros.h" - -namespace android { - -template <typename T> -class ScopedLock; - -// Owns the guarded object and protects access to it via a mutex. -// The guarded object is inaccessible via this class. -// The mutex is locked and the object accessed via the ScopedLock<T> class. -// -// NOTE: The template parameter T should not be a raw pointer, since ownership -// is ambiguous and error-prone. Instead use an std::unique_ptr<>. -// -// Example use: -// -// Guarded<std::string> shared_string("hello"); -// { -// ScopedLock<std::string> locked_string(shared_string); -// *locked_string += " world"; -// } -// -template <typename T> -class Guarded { - static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); - - public: - explicit Guarded() : guarded_() { - } - - template <typename U = T> - explicit Guarded(const T& guarded, - typename std::enable_if<std::is_copy_constructible<U>::value>::type = void()) - : guarded_(guarded) { - } - - template <typename U = T> - explicit Guarded(T&& guarded, - typename std::enable_if<std::is_move_constructible<U>::value>::type = void()) - : guarded_(std::move(guarded)) { - } - - private: - friend class ScopedLock<T>; - - DISALLOW_COPY_AND_ASSIGN(Guarded); - - std::mutex lock_; - T guarded_; -}; - -template <typename T> -class ScopedLock { - public: - explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { - } - - T& operator*() { - return guarded_; - } - - T* operator->() { - return &guarded_; - } - - T* get() { - return &guarded_; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLock); - - std::lock_guard<std::mutex> lock_; - T& guarded_; -}; - -} // namespace android - -#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..6c43a67e602f 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,56 +26,58 @@ using ::android::base::unique_fd; using ::com::android::basic::R; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_THAT(fd.get(), Ge(0)); + ASSERT_GE(fd.get(), 0); std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); - + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -84,22 +86,19 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); util::unique_cptr<void> idmap_data; void* temp_data; size_t idmap_len; - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -109,30 +108,37 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); + std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_THAT(asset, NotNull()); + ASSERT_NE(nullptr, asset); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - ASSERT_THAT(fd.get(), Ge(0)); + EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); @@ -140,7 +146,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); + EXPECT_EQ("This should be uncompressed.\n\n", buffer); } } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 437e14772964..85e8f25394e9 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,18 +81,17 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); +static void BM_AssetManagerGetResource(benchmark::State& state) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); } -BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); -BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); +BENCHMARK(BM_AssetManagerGetResource); -static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, - state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); } -BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); -BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); +BENCHMARK(BM_AssetManagerGetResourceOld); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -197,7 +196,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - true /*isSystemAssets*/)) { + false /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -212,44 +211,4 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); -static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { - std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); - if (apk == nullptr) { - state.SkipWithError("Failed to load assets"); - return; - } - - AssetManager2 assets; - assets.SetApkAssets({apk.get()}); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - while (state.KeepRunning()) { - config.sdkVersion = ~config.sdkVersion; - assets.SetConfiguration(config); - } -} -BENCHMARK(BM_AssetManagerSetConfigurationFramework); - -static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { - AssetManager assets; - if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - true /*isSystemAssets*/)) { - state.SkipWithError("Failed to load assets"); - return; - } - - const ResTable& table = assets.getResources(true); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - while (state.KeepRunning()) { - config.sdkVersion = ~config.sdkVersion; - assets.setConfiguration(config); - } -} -BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); - } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp deleted file mode 100644 index fa300c50218a..000000000000 --- a/libs/androidfw/tests/AttributeResolution_bench.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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. - */ - -#include "benchmark/benchmark.h" - -//#include "android-base/stringprintf.h" -#include "androidfw/ApkAssets.h" -#include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" -#include "androidfw/AttributeResolution.h" -#include "androidfw/ResourceTypes.h" - -#include "BenchmarkHelpers.h" -#include "data/basic/R.h" -#include "data/styles/R.h" - -namespace app = com::android::app; -namespace basic = com::android::basic; - -namespace android { - -constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; -constexpr const static uint32_t Theme_Material_Light = 0x01030237u; - -static void BM_ApplyStyle(benchmark::State& state) { - std::unique_ptr<const ApkAssets> styles_apk = - ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - if (styles_apk == nullptr) { - state.SkipWithError("failed to load assets"); - return; - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets({styles_apk.get()}); - - std::unique_ptr<Asset> asset = - assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - if (asset == nullptr) { - state.SkipWithError("failed to load layout"); - return; - } - - ResXMLTree xml_tree; - if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { - state.SkipWithError("corrupt xml layout"); - return; - } - - // Skip to the first tag. - while (xml_tree.next() != ResXMLParser::START_TAG) { - } - - std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - theme->ApplyStyle(app::R::style::StyleTwo); - - std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two, - app::R::attr::attr_three, app::R::attr::attr_four, - app::R::attr::attr_five, app::R::attr::attr_empty}}; - std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - std::array<uint32_t, attrs.size() + 1> indices; - - while (state.KeepRunning()) { - ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), - attrs.size(), values.data(), indices.data()); - } -} -BENCHMARK(BM_ApplyStyle); - -static void BM_ApplyStyleFramework(benchmark::State& state) { - std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath); - if (framework_apk == nullptr) { - state.SkipWithError("failed to load framework assets"); - return; - } - - std::unique_ptr<const ApkAssets> basic_apk = - ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - if (basic_apk == nullptr) { - state.SkipWithError("failed to load assets"); - return; - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); - - ResTable_config device_config; - memset(&device_config, 0, sizeof(device_config)); - device_config.language[0] = 'e'; - device_config.language[1] = 'n'; - device_config.country[0] = 'U'; - device_config.country[1] = 'S'; - device_config.orientation = ResTable_config::ORIENTATION_PORT; - device_config.smallestScreenWidthDp = 700; - device_config.screenWidthDp = 700; - device_config.screenHeightDp = 1024; - device_config.sdkVersion = 27; - - Res_value value; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = - assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, - 0u /*density_override*/, &value, &config, &flags); - if (cookie == kInvalidCookie) { - state.SkipWithError("failed to find R.layout.layout"); - return; - } - - size_t len = 0u; - const char* layout_path = - assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); - if (layout_path == nullptr || len == 0u) { - state.SkipWithError("failed to lookup layout path"); - return; - } - - std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset( - StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); - if (asset == nullptr) { - state.SkipWithError("failed to load layout"); - return; - } - - ResXMLTree xml_tree; - if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { - state.SkipWithError("corrupt xml layout"); - return; - } - - // Skip to the first tag. - while (xml_tree.next() != ResXMLParser::START_TAG) { - } - - std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - theme->ApplyStyle(Theme_Material_Light); - - std::array<uint32_t, 92> attrs{ - {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, - 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, - 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, - 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, - 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, - 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, - 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, - 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, - 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, - 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, - 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, - 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, - 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, - 0x011100ca}}; - - std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - std::array<uint32_t, attrs.size() + 1> indices; - while (state.KeepRunning()) { - ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, - attrs.data(), attrs.size(), values.data(), indices.data()); - } -} -BENCHMARK(BM_ApplyStyleFramework); - -} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index cc3053798e7b..2d73ce8f8ee3 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,7 +21,6 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" -#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -33,14 +32,15 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - ASSERT_NE(nullptr, styles_assets_); - assetmanager_.SetApkAssets({styles_assets_.get()}); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); + ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), + 1 /*cookie*/, true /*copyData*/)); } protected: - std::unique_ptr<const ApkAssets> styles_assets_; - AssetManager2 assetmanager_; + ResTable table_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,12 +48,13 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::unique_ptr<Asset> asset = - assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, asset); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); - ASSERT_EQ(NO_ERROR, - xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,14 +66,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -125,8 +126,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), + nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -170,15 +171,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index faddfe599af4..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,21 +33,19 @@ void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTab } } - // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. - const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } + const ResTable& table = assetmanager.getResources(true); + Res_value value; ResTable_config selected_config; uint32_t flags; - uint32_t last_ref = 0u; while (state.KeepRunning()) { - ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); - table.resolveReference(&value, block, &last_ref, &flags, &selected_config); + table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); } } @@ -74,12 +72,10 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; - uint32_t last_id = 0u; while (state.KeepRunning()) { - ApkAssetsCookie cookie = assetmanager.GetResource( - resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); - assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); + assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, + &selected_config, &flags); } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f..37ddafb14fd3 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,8 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "androidfw/ResourceUtils.h" - #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -29,13 +27,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; - namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -44,24 +35,39 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; + + FindEntryResult entry; - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); - ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); - EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} - const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; - const uint16_t entry_index = get_entry_id(app::R::string::string_one); +TEST(LoadedArscTest, FindDefaultEntry) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -70,22 +76,15 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -94,13 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,23 +114,25 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); - EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); - EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); + ASSERT_EQ(2u, dynamic_pkg_map.size()); - EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); - EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -141,12 +143,13 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -154,27 +157,21 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - uint8_t type_index = get_type_id(basic::R::string::test3) - 1; - uint8_t entry_index = get_entry_id(basic::R::string::test3); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); + EXPECT_EQ(std::string("string"), type_name); } class MockLoadedIdmap : public LoadedIdmap { @@ -202,33 +199,23 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents; + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); + &overlay_contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); + LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index df0c642f4565..43a995536d89 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,7 +20,6 @@ #include <string> #include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index b7e814fea079..94a2a14ced87 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,7 +34,6 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, - layoutt = 0x7f020001, }; }; @@ -56,7 +55,6 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, - deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk Binary files differindex 1733b6a16546..18ef75e91ded 100644 --- a/libs/androidfw/tests/data/basic/basic.apk +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml index b3435629265b..6c474596b5cd 100644 --- a/libs/androidfw/tests/data/basic/res/values/values.xml +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -22,7 +22,6 @@ <attr name="attr2" format="reference|integer" /> <public type="layout" name="main" id="0x7f020000" /> - <public type="layout" name="layout" id="0x7f020001" /> <public type="string" name="test1" id="0x7f030000" /> <string name="test1">test1</string> @@ -44,18 +43,6 @@ <public type="integer" name="ref2" id="0x7f040003" /> <integer name="ref2">12000</integer> - <public type="integer" name="deep_ref" id="0x7f040004" /> - <integer name="deep_ref">@integer/deep_ref_1</integer> - <integer name="deep_ref_1">@integer/deep_ref_2</integer> - <integer name="deep_ref_2">@integer/deep_ref_3</integer> - <integer name="deep_ref_3">@integer/deep_ref_4</integer> - <integer name="deep_ref_4">@integer/deep_ref_5</integer> - <integer name="deep_ref_5">@integer/deep_ref_6</integer> - <integer name="deep_ref_6">@integer/deep_ref_7</integer> - <integer name="deep_ref_7">@integer/deep_ref_8</integer> - <integer name="deep_ref_8">@integer/deep_ref_9</integer> - <integer name="deep_ref_9">100</integer> - <public type="style" name="Theme1" id="0x7f050000" /> <style name="Theme1"> <item name="com.android.basic:attr1">100</item> diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index 1ebb58561f4d..df8a5e48d18e 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -33,6 +33,13 @@ namespace uirenderer { class TaskManager; } +class OnAnimationEndListener { +public: + virtual ~OnAnimationEndListener() {} + + virtual void onAnimationEnd() = 0; +}; + /** * Native component of android.graphics.drawable.AnimatedImageDrawables.java. This class can be * drawn into Canvas.h and maintains the state needed to drive the animation from the RenderThread. @@ -62,6 +69,13 @@ public: bool start(); void stop(); bool isRunning(); + void setRepetitionCount(int count) { + mSkAnimatedImage->setRepetitionCount(count); + } + + void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) { + mEndListener = std::move(listener); + } void scheduleUpdate(uirenderer::TaskManager* taskManager); @@ -87,6 +101,8 @@ private: class AnimatedImageTaskProcessor; sp<AnimatedImageTask> mDecodeTask; sp<AnimatedImageTaskProcessor> mDecodeTaskProcessor; + + std::unique_ptr<OnAnimationEndListener> mEndListener; }; }; // namespace android diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 1a97b6ba762f..41f9f09fbb88 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -142,7 +142,8 @@ public final class AudioDeviceInfo { TYPE_LINE_DIGITAL, TYPE_FM, TYPE_AUX_LINE, - TYPE_IP } + TYPE_IP, + TYPE_BUS } ) @Retention(RetentionPolicy.SOURCE) public @interface AudioDeviceTypeOut {} @@ -168,6 +169,7 @@ public final class AudioDeviceInfo { case TYPE_FM: case TYPE_AUX_LINE: case TYPE_IP: + case TYPE_BUS: return true; default: return false; diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl index b10a40bbb0b0..f79711a389a7 100644 --- a/media/java/android/media/IMediaSession2.aidl +++ b/media/java/android/media/IMediaSession2.aidl @@ -42,6 +42,8 @@ interface IMediaSession2 { // send command ////////////////////////////////////////////////////////////////////////////////////////////// oneway void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args); + oneway void sendTransportControlCommand(IMediaSession2Callback caller, + int commandCode, long arg); Bundle getPlaybackState(); diff --git a/media/java/android/media/IMediaSession2Callback.aidl b/media/java/android/media/IMediaSession2Callback.aidl index eb02fa7a6bd2..45d40e6cc155 100644 --- a/media/java/android/media/IMediaSession2Callback.aidl +++ b/media/java/android/media/IMediaSession2Callback.aidl @@ -30,6 +30,7 @@ import android.media.IMediaSession2; */ oneway interface IMediaSession2Callback { void onPlaybackStateChanged(in Bundle state); + void onPlaylistParamsChanged(in Bundle params); /** * Called only when the controller is created with service's token. diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java index 983ca754acd1..dcd4dce5f3eb 100644 --- a/media/java/android/media/MediaActionSound.java +++ b/media/java/android/media/MediaActionSound.java @@ -47,11 +47,16 @@ public class MediaActionSound { private SoundPool mSoundPool; private SoundState[] mSounds; + private static final String[] SOUND_DIRS = { + "/product/media/audio/ui/", + "/system/media/audio/ui/", + }; + private static final String[] SOUND_FILES = { - "/system/media/audio/ui/camera_click.ogg", - "/system/media/audio/ui/camera_focus.ogg", - "/system/media/audio/ui/VideoRecord.ogg", - "/system/media/audio/ui/VideoStop.ogg" + "camera_click.ogg", + "camera_focus.ogg", + "VideoRecord.ogg", + "VideoStop.ogg" }; private static final String TAG = "MediaActionSound"; @@ -132,12 +137,16 @@ public class MediaActionSound { } private int loadSound(SoundState sound) { - int id = mSoundPool.load(SOUND_FILES[sound.name], 1); - if (id > 0) { - sound.state = STATE_LOADING; - sound.id = id; + final String soundFileName = SOUND_FILES[sound.name]; + for (String soundDir : SOUND_DIRS) { + int id = mSoundPool.load(soundDir + soundFileName, 1); + if (id > 0) { + sound.state = STATE_LOADING; + sound.id = id; + return id; + } } - return id; + return 0; } /** diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index 6064ec4bbccf..e2d5c5de7c42 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -138,6 +138,13 @@ public class MediaController2 implements AutoCloseable { * @param state */ public void onPlaybackStateChanged(@NonNull PlaybackState2 state) { } + + /** + * Called when the playlist parameters are changed. + * + * @param params The new play list parameters. + */ + public void onPlaylistParamsChanged(@NonNull PlaylistParams params) { } } /** @@ -359,6 +366,17 @@ public class MediaController2 implements AutoCloseable { } /** + * Sets the {@link PlaylistParams} for the current play list. Repeat/shuffle mode and metadata + * for the list can be set by calling this method. + * + * @param params A {@link PlaylistParams} object to set. + * @throws IllegalArgumentException if given {@param param} is null. + */ + public void setPlaylistParams(PlaylistParams params) { + mProvider.setPlaylistParams_impl(params); + } + + /** * @hide */ public void skipForward() { diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java index 5dadcb5f2540..94ada135964b 100644 --- a/media/java/android/media/MediaLibraryService2.java +++ b/media/java/android/media/MediaLibraryService2.java @@ -66,7 +66,7 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public class MediaLibrarySession extends MediaSession2 { private final MediaLibrarySessionProvider mProvider; - MediaLibrarySession(Context context, MediaPlayerBase player, String id, + MediaLibrarySession(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, SessionCallback callback) { super(context, player, id, volumeProvider, ratingType, sessionActivity, @@ -75,9 +75,10 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { } @Override - MediaSession2Provider createProvider(Context context, MediaPlayerBase player, String id, - VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, - Executor callbackExecutor, SessionCallback callback) { + MediaSession2Provider createProvider(Context context, MediaPlayerInterface player, + String id, VolumeProvider volumeProvider, int ratingType, + PendingIntent sessionActivity, Executor callbackExecutor, + SessionCallback callback) { return ApiLoader.getProvider(context) .createMediaLibraryService2MediaLibrarySession(context, this, player, id, volumeProvider, ratingType, sessionActivity, @@ -206,7 +207,7 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> { public MediaLibrarySessionBuilder( - @NonNull Context context, @NonNull MediaPlayerBase player, + @NonNull Context context, @NonNull MediaPlayerInterface player, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull MediaLibrarySessionCallback callback) { super(context, player); diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerInterface.java index 0efec901f6cd..51bcd9b8a56a 100644 --- a/media/java/android/media/MediaPlayerBase.java +++ b/media/java/android/media/MediaPlayerInterface.java @@ -25,32 +25,35 @@ import java.util.concurrent.Executor; * Base interfaces for all media players that want media session. * @hide */ -public abstract class MediaPlayerBase { +public interface MediaPlayerInterface { /** * Listens change in {@link PlaybackState2}. */ - public interface PlaybackListener { + interface PlaybackListener { /** * Called when {@link PlaybackState2} for this player is changed. */ void onPlaybackChanged(PlaybackState2 state); } - public abstract void play(); - public abstract void prepare(); - public abstract void pause(); - public abstract void stop(); - public abstract void skipToPrevious(); - public abstract void skipToNext(); - public abstract void seekTo(long pos); - public abstract void fastFoward(); - public abstract void rewind(); + // Transport controls that session will send command directly to this player. + void play(); + void prepare(); + void pause(); + void stop(); + void skipToPrevious(); + void skipToNext(); + void seekTo(long pos); + void fastForward(); + void rewind(); - public abstract PlaybackState2 getPlaybackState(); - public abstract AudioAttributes getAudioAttributes(); + PlaybackState2 getPlaybackState(); + AudioAttributes getAudioAttributes(); - public abstract void setPlaylist(List<MediaItem2> item, PlaylistParams param); - public abstract void setCurrentPlaylistItem(int index); + void setPlaylist(List<MediaItem2> item, PlaylistParams param); + void setCurrentPlaylistItem(int index); + void setPlaylistParams(PlaylistParams params); + PlaylistParams getPlaylistParams(); /** * Add a {@link PlaybackListener} to be invoked when the playback state is changed. @@ -58,12 +61,12 @@ public abstract class MediaPlayerBase { * @param executor the Handler that will receive the listener * @param listener the listener that will be run */ - public abstract void addPlaybackListener(Executor executor, PlaybackListener listener); + void addPlaybackListener(Executor executor, PlaybackListener listener); /** * Remove previously added {@link PlaybackListener}. * * @param listener the listener to be removed */ - public abstract void removePlaybackListener(PlaybackListener listener); + void removePlaybackListener(PlaybackListener listener); } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index cb4e46fe945a..c3090389aa37 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -158,6 +158,7 @@ public class MediaScanner implements AutoCloseable { public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild"; public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint"; private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio"; + private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio"; private static String sLastInternalScanFingerprint; private static final String[] ID3_GENRES = { @@ -1153,7 +1154,10 @@ public class MediaScanner implements AutoCloseable { private static boolean isSystemSoundWithMetadata(String path) { if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR) || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR) - || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)) { + || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR) + || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR) + || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR) + || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) { return true; } return false; diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java index 7dffd40d96ab..365c356e5e1d 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -24,7 +24,7 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.media.MediaPlayerBase.PlaybackListener; +import android.media.MediaPlayerInterface.PlaybackListener; import android.media.session.MediaSession; import android.media.session.MediaSession.Callback; import android.media.session.PlaybackState; @@ -67,7 +67,8 @@ import java.util.concurrent.Executor; * session. * <p> * When a session receive transport control commands, the session sends the commands directly to - * the the underlying media player set by {@link Builder} or {@link #setPlayer(MediaPlayerBase)}. + * the the underlying media player set by {@link Builder} or + * {@link #setPlayer(MediaPlayerInterface)}. * <p> * When an app is finished performing playback it must call {@link #close()} to clean up the session * and notify any controllers. @@ -229,6 +230,11 @@ public class MediaSession2 implements AutoCloseable { mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP)); mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM)); mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM)); + mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PREPARE)); + mCommands.add(new Command(COMMAND_CODE_PLAYBACK_FAST_FORWARD)); + mCommands.add(new Command(COMMAND_CODE_PLAYBACK_REWIND)); + mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SEEK_TO)); + mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM)); } public void removeCommand(Command command) { @@ -457,7 +463,7 @@ public class MediaSession2 implements AutoCloseable { static abstract class BuilderBase <T extends MediaSession2.BuilderBase<T, C>, C extends SessionCallback> { final Context mContext; - final MediaPlayerBase mPlayer; + final MediaPlayerInterface mPlayer; String mId; Executor mCallbackExecutor; C mCallback; @@ -474,7 +480,7 @@ public class MediaSession2 implements AutoCloseable { * {@link MediaSession2} or {@link MediaController2}. */ // TODO(jaewan): Also need executor - public BuilderBase(@NonNull Context context, @NonNull MediaPlayerBase player) { + public BuilderBase(@NonNull Context context, @NonNull MediaPlayerInterface player) { if (context == null) { throw new IllegalArgumentException("context shouldn't be null"); } @@ -589,12 +595,15 @@ public class MediaSession2 implements AutoCloseable { // TODO(jaewan): Add setRatingType() // TODO(jaewan): Add setSessionActivity() public static final class Builder extends BuilderBase<Builder, SessionCallback> { - public Builder(Context context, @NonNull MediaPlayerBase player) { + public Builder(Context context, @NonNull MediaPlayerInterface player) { super(context, player); } @Override public MediaSession2 build() { + if (mCallbackExecutor == null) { + mCallbackExecutor = mContext.getMainExecutor(); + } if (mCallback == null) { mCallback = new SessionCallback(); } @@ -845,7 +854,6 @@ public class MediaSession2 implements AutoCloseable { /** * Parameter for the playlist. */ - // TODO(jaewan): add fromBundle()/toBundle() public static class PlaylistParams { /** * @hide @@ -901,6 +909,16 @@ public class MediaSession2 implements AutoCloseable { */ public static final int SHUFFLE_MODE_GROUP = 2; + /** + * Keys used for converting a PlaylistParams object to a bundle object and vice versa. + */ + private static final String KEY_REPEAT_MODE = + "android.media.session2.playlistparams2.repeat_mode"; + private static final String KEY_SHUFFLE_MODE = + "android.media.session2.playlistparams2.shuffle_mode"; + private static final String KEY_MEDIA_METADATA2_BUNDLE = + "android.media.session2.playlistparams2.metadata2_bundle"; + private @RepeatMode int mRepeatMode; private @ShuffleMode int mShuffleMode; @@ -924,6 +942,47 @@ public class MediaSession2 implements AutoCloseable { public MediaMetadata2 getPlaylistMetadata() { return mPlaylistMetadata; } + + /** + * Returns this object as a bundle to share between processes. + * + * @hide + */ + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(KEY_REPEAT_MODE, mRepeatMode); + bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode); + if (mPlaylistMetadata != null) { + bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.getBundle()); + } + return bundle; + } + + /** + * Creates an instance from a bundle which is previously created by {@link #toBundle()}. + * + * @param bundle A bundle created by {@link #toBundle()}. + * @return A new {@link PlaylistParams} instance. Returns {@code null} if the given + * {@param bundle} is null, or if the {@param bundle} has no playlist parameters. + * @hide + */ + public static PlaylistParams fromBundle(Bundle bundle) { + if (bundle == null) { + return null; + } + if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) { + return null; + } + + Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE); + MediaMetadata2 metadata = + metadataBundle == null ? null : new MediaMetadata2(metadataBundle); + + return new PlaylistParams( + bundle.getInt(KEY_REPEAT_MODE), + bundle.getInt(KEY_SHUFFLE_MODE), + metadata); + } } /** @@ -941,16 +1000,16 @@ public class MediaSession2 implements AutoCloseable { * @hide */ - MediaSession2(Context context, MediaPlayerBase player, String id, VolumeProvider volumeProvider, - int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, - SessionCallback callback) { + MediaSession2(Context context, MediaPlayerInterface player, String id, + VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, + Executor callbackExecutor, SessionCallback callback) { super(); mProvider = createProvider(context, player, id, volumeProvider, ratingType, sessionActivity, callbackExecutor, callback ); } - MediaSession2Provider createProvider(Context context, MediaPlayerBase player, String id, + MediaSession2Provider createProvider(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, SessionCallback callback) { return ApiLoader.getProvider(context) @@ -964,8 +1023,8 @@ public class MediaSession2 implements AutoCloseable { } /** - * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event to. - * Events from the {@link MediaController2} will be sent directly to the underlying + * Set the underlying {@link MediaPlayerInterface} for this session to dispatch incoming event + * to. Events from the {@link MediaController2} will be sent directly to the underlying * player on the {@link Handler} where the session is created on. * <p> * If the new player is successfully set, {@link PlaybackListener} @@ -974,22 +1033,23 @@ public class MediaSession2 implements AutoCloseable { * You can also specify a volume provider. If so, playback in the player is considered as * remote playback. * - * @param player a {@link MediaPlayerBase} that handles actual media playback in your app. + * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app. * @throws IllegalArgumentException if the player is {@code null}. */ - public void setPlayer(@NonNull MediaPlayerBase player) { + public void setPlayer(@NonNull MediaPlayerInterface player) { mProvider.setPlayer_impl(player); } /** - * Set the underlying {@link MediaPlayerBase} with the volume provider for remote playback. + * Set the underlying {@link MediaPlayerInterface} with the volume provider for remote playback. * - * @param player a {@link MediaPlayerBase} that handles actual media playback in your app. + * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app. * @param volumeProvider a volume provider - * @see #setPlayer(MediaPlayerBase) + * @see #setPlayer(MediaPlayerInterface) * @see Builder#setVolumeProvider(VolumeProvider) */ - public void setPlayer(@NonNull MediaPlayerBase player, @NonNull VolumeProvider volumeProvider) { + public void setPlayer(@NonNull MediaPlayerInterface player, + @NonNull VolumeProvider volumeProvider) { mProvider.setPlayer_impl(player, volumeProvider); } @@ -1001,7 +1061,8 @@ public class MediaSession2 implements AutoCloseable { /** * @return player */ - public @Nullable MediaPlayerBase getPlayer() { + public @Nullable + MediaPlayerInterface getPlayer() { return mProvider.getPlayer_impl(); } @@ -1208,4 +1269,23 @@ public class MediaSession2 implements AutoCloseable { public void setPlaylist(@NonNull List<MediaItem2> playlist, @NonNull PlaylistParams param) { mProvider.setPlaylist_impl(playlist, param); } + + /** + * Sets the {@link PlaylistParams} for the current play list. Repeat/shuffle mode and metadata + * for the list can be set by calling this method. + * + * @param params A {@link PlaylistParams} object to set. + * @throws IllegalArgumentException if given {@param param} is null. + */ + public void setPlaylistParams(PlaylistParams params) { + mProvider.setPlaylistParams_impl(params); + } + + /** + * Returns the {@link PlaylistParams} for the current play list. + * Returns {@code null} if not set. + */ + public PlaylistParams getPlaylistParams() { + return mProvider.getPlaylistParams_impl(); + } } diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java index 04f211d002ff..7688fbc9bb41 100644 --- a/media/java/android/media/PlaybackState2.java +++ b/media/java/android/media/PlaybackState2.java @@ -23,7 +23,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Playback state for a {@link MediaPlayerBase}, to be shared between {@link MediaSession2} and + * Playback state for a {@link MediaPlayerInterface}, to be shared between {@link MediaSession2} and * {@link MediaController2}. This includes a playback state {@link #STATE_PLAYING}, * the current playback position and extra. * @hide @@ -31,8 +31,6 @@ import java.lang.annotation.RetentionPolicy; public final class PlaybackState2 { private static final String TAG = "PlaybackState2"; - private static final String KEY_STATE = "android.media.playbackstate2.state"; - // TODO(jaewan): Replace states from MediaPlayer2 /** * @hide @@ -90,13 +88,25 @@ public final class PlaybackState2 { */ public final static long PLAYBACK_POSITION_UNKNOWN = -1; + /** + * Keys used for converting a PlaybackState2 to a bundle object and vice versa. + */ + private static final String KEY_STATE = "android.media.playbackstate2.state"; + private static final String KEY_POSITION = "android.media.playbackstate2.position"; + private static final String KEY_BUFFERED_POSITION = + "android.media.playbackstate2.buffered_position"; + private static final String KEY_SPEED = "android.media.playbackstate2.speed"; + private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message"; + private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time"; + private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id"; + private final int mState; private final long mPosition; - private final long mBufferedPosition; - private final float mSpeed; - private final CharSequence mErrorMessage; private final long mUpdateTime; + private final float mSpeed; + private final long mBufferedPosition; private final long mActiveItemId; + private final CharSequence mErrorMessage; public PlaybackState2(int state, long position, long updateTime, float speed, long bufferedPosition, long activeItemId, CharSequence error) { @@ -194,22 +204,49 @@ public final class PlaybackState2 { } /** - * @return Bundle object for this to share between processes. + * Returns this object as a bundle to share between processes. */ public Bundle toBundle() { - // TODO(jaewan): Include other variables. Bundle bundle = new Bundle(); bundle.putInt(KEY_STATE, mState); + bundle.putLong(KEY_POSITION, mPosition); + bundle.putLong(KEY_UPDATE_TIME, mUpdateTime); + bundle.putFloat(KEY_SPEED, mSpeed); + bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition); + bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId); + bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage); return bundle; } /** - * @param bundle input - * @return + * Creates an instance from a bundle which is previously created by {@link #toBundle()}. + * + * @param bundle A bundle created by {@link #toBundle()}. + * @return A new {@link PlaybackState2} instance. Returns {@code null} if the given + * {@param bundle} is null, or if the {@param bundle} has no playback state parameters. */ public static PlaybackState2 fromBundle(Bundle bundle) { - // TODO(jaewan): Include other variables. - final int state = bundle.getInt(KEY_STATE); - return new PlaybackState2(state, 0, 0, 0, 0, 0, null); + if (bundle == null) { + return null; + } + + if (!bundle.containsKey(KEY_STATE) + || !bundle.containsKey(KEY_POSITION) + || !bundle.containsKey(KEY_UPDATE_TIME) + || !bundle.containsKey(KEY_SPEED) + || !bundle.containsKey(KEY_BUFFERED_POSITION) + || !bundle.containsKey(KEY_ACTIVE_ITEM_ID) + || !bundle.containsKey(KEY_ERROR_MESSAGE)) { + return null; + } + + return new PlaybackState2( + bundle.getInt(KEY_STATE), + bundle.getLong(KEY_POSITION), + bundle.getLong(KEY_UPDATE_TIME), + bundle.getFloat(KEY_SPEED), + bundle.getLong(KEY_BUFFERED_POSITION), + bundle.getLong(KEY_ACTIVE_ITEM_ID), + bundle.getCharSequence(KEY_ERROR_MESSAGE)); } }
\ No newline at end of file diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java index 8dfb892c7306..cf35358c7841 100644 --- a/media/java/android/media/update/MediaController2Provider.java +++ b/media/java/android/media/update/MediaController2Provider.java @@ -61,5 +61,6 @@ public interface MediaController2Provider extends TransportControlProvider { void addPlaylistItem_impl(int index, MediaItem2 item); PlaylistParams getPlaylistParam_impl(); + void setPlaylistParams_impl(PlaylistParams params); PlaybackState2 getPlaybackState_impl(); } diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java index d32b741550b3..801bdeb68eef 100644 --- a/media/java/android/media/update/MediaSession2Provider.java +++ b/media/java/android/media/update/MediaSession2Provider.java @@ -16,10 +16,9 @@ package android.media.update; -import android.annotation.SystemApi; import android.media.AudioAttributes; import android.media.MediaItem2; -import android.media.MediaPlayerBase; +import android.media.MediaPlayerInterface; import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; @@ -37,9 +36,9 @@ import java.util.List; */ public interface MediaSession2Provider extends TransportControlProvider { void close_impl(); - void setPlayer_impl(MediaPlayerBase player); - void setPlayer_impl(MediaPlayerBase player, VolumeProvider volumeProvider); - MediaPlayerBase getPlayer_impl(); + void setPlayer_impl(MediaPlayerInterface player); + void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider); + MediaPlayerInterface getPlayer_impl(); SessionToken2 getToken_impl(); List<ControllerInfo> getConnectedControllers_impl(); void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout); @@ -52,6 +51,8 @@ public interface MediaSession2Provider extends TransportControlProvider { ResultReceiver receiver); void sendCustomCommand_impl(Command command, Bundle args); void setPlaylist_impl(List<MediaItem2> playlist, PlaylistParams param); + void setPlaylistParams_impl(PlaylistParams params); + PlaylistParams getPlaylistParams_impl(); interface ControllerInfoProvider { String getPackageName_impl(); diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java index 3cd1a9976391..7faac951ce98 100644 --- a/media/java/android/media/update/StaticProvider.java +++ b/media/java/android/media/update/StaticProvider.java @@ -17,7 +17,6 @@ package android.media.update; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.app.PendingIntent; import android.content.Context; import android.media.MediaBrowser2; @@ -27,7 +26,7 @@ import android.media.MediaController2.ControllerCallback; import android.media.MediaLibraryService2; import android.media.MediaLibraryService2.MediaLibrarySession; import android.media.MediaLibraryService2.MediaLibrarySessionCallback; -import android.media.MediaPlayerBase; +import android.media.MediaPlayerInterface; import android.media.MediaSession2; import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; @@ -57,7 +56,7 @@ public interface StaticProvider { @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes); MediaSession2Provider createMediaSession2(Context context, MediaSession2 instance, - MediaPlayerBase player, String id, VolumeProvider volumeProvider, int ratingType, + MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor executor, SessionCallback callback); ControllerInfoProvider createMediaSession2ControllerInfoProvider(Context context, MediaSession2.ControllerInfo instance, int uid, int pid, @@ -69,7 +68,7 @@ public interface StaticProvider { MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance); MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance); MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(Context context, - MediaLibrarySession instance, MediaPlayerBase player, String id, + MediaLibrarySession instance, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor executor, MediaLibrarySessionCallback callback); } diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java index 5c05ce4f7aeb..322a4f6304d8 100644 --- a/media/java/android/media/update/VideoView2Provider.java +++ b/media/java/android/media/update/VideoView2Provider.java @@ -17,8 +17,9 @@ package android.media.update; import android.media.AudioAttributes; -import android.media.MediaPlayerBase; +import android.media.MediaPlayerInterface; import android.media.session.MediaController; +import android.media.session.PlaybackState; import android.net.Uri; import android.widget.MediaControlView2; import android.widget.VideoView2; @@ -44,7 +45,6 @@ public interface VideoView2Provider extends ViewProvider { void setMediaControlView2_impl(MediaControlView2 mediaControlView); MediaController getMediaController_impl(); MediaControlView2 getMediaControlView2_impl(); - int getAudioSessionId_impl(); void showSubtitle_impl(); void hideSubtitle_impl(); void setFullScreen_impl(boolean fullScreen); @@ -52,12 +52,14 @@ public interface VideoView2Provider extends ViewProvider { void setSpeed_impl(float speed); void setAudioFocusRequest_impl(int focusGain); void setAudioAttributes_impl(AudioAttributes attributes); - void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player); + void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player); void setVideoPath_impl(String path); - void setVideoURI_impl(Uri uri); - void setVideoURI_impl(Uri uri, Map<String, String> headers); + void setVideoUri_impl(Uri uri); + void setVideoUri_impl(Uri uri, Map<String, String> headers); void setViewType_impl(int viewType); int getViewType_impl(); + void setCustomActions_impl(List<PlaybackState.CustomAction> actionList, + VideoView2.OnCustomActionListener listener); void setOnPreparedListener_impl(VideoView2.OnPreparedListener l); void setOnCompletionListener_impl(VideoView2.OnCompletionListener l); void setOnErrorListener_impl(VideoView2.OnErrorListener l); diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java index 78c5b36f8e86..0dd8f388a8fe 100644 --- a/media/java/android/media/update/ViewProvider.java +++ b/media/java/android/media/update/ViewProvider.java @@ -17,8 +17,6 @@ package android.media.update; import android.annotation.SystemApi; -import android.graphics.Canvas; -import android.view.KeyEvent; import android.view.MotionEvent; /** @@ -42,8 +40,6 @@ public interface ViewProvider { CharSequence getAccessibilityClassName_impl(); boolean onTouchEvent_impl(MotionEvent ev); boolean onTrackballEvent_impl(MotionEvent ev); - boolean onKeyDown_impl(int keyCode, KeyEvent event); void onFinishInflate_impl(); - boolean dispatchKeyEvent_impl(KeyEvent event); void setEnabled_impl(boolean enabled); } diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index e70d5ea0d566..98e9a42d944d 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,11 +18,9 @@ #include <utils/Log.h> #include <android/asset_manager_jni.h> -#include <android_runtime/android_util_AssetManager.h> #include <androidfw/Asset.h> #include <androidfw/AssetDir.h> #include <androidfw/AssetManager.h> -#include <androidfw/AssetManager2.h> #include <utils/threads.h> #include "jni.h" @@ -37,20 +35,21 @@ using namespace android; // ----- struct AAssetDir { - std::unique_ptr<AssetDir> mAssetDir; + AssetDir* mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(std::unique_ptr<AssetDir> dir) : - mAssetDir(std::move(dir)), mCurFileIndex(0) { } + explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } + ~AAssetDir() { delete mAssetDir; } }; // ----- struct AAsset { - std::unique_ptr<Asset> mAsset; + Asset* mAsset; - explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { } + explicit AAsset(Asset* asset) : mAsset(asset) { } + ~AAsset() { delete mAsset; } }; // -------------------- Public native C API -------------------- @@ -105,18 +104,19 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode); - if (asset == nullptr) { - return nullptr; + AssetManager* mgr = static_cast<AssetManager*>(amgr); + Asset* asset = mgr->open(filename, amMode); + if (asset == NULL) { + return NULL; } - return new AAsset(std::move(asset)); + + return new AAsset(asset); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - return new AAssetDir(locked_mgr->OpenDir(dirName)); + AssetManager* mgr = static_cast<AssetManager*>(amgr); + return new AAssetDir(mgr->openDir(dirName)); } /** diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml index c0a59b3badbf..8d79f62f21d7 100644 --- a/packages/MtpDocumentsProvider/AndroidManifest.xml +++ b/packages/MtpDocumentsProvider/AndroidManifest.xml @@ -3,7 +3,6 @@ package="com.android.mtp" android:sharedUserId="android.media"> <uses-feature android:name="android.hardware.usb.host" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.MANAGE_USB" /> <application android:label="@string/app_label"> <provider diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 77df02bc5753..c926e1ff48a7 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -468,18 +468,6 @@ <item>show_deuteranomaly</item> </string-array> - <!-- Titles for debug renderer preference. [CHAR LIMIT=50] --> - <string-array name="debug_hw_renderer_entries"> - <item>OpenGL (Default)</item> - <item>OpenGL (Skia)</item> - </string-array> - - <!-- Values for debug renderer preference. --> - <string-array name="debug_hw_renderer_values" translatable="false" > - <item>opengl</item> - <item>skiagl</item> - </string-array> - <!-- Titles for app process limit preference. [CHAR LIMIT=35] --> <string-array name="app_process_limit_entries"> <item>Standard limit</item> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 4156653c493a..486a9bbed1f2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -679,9 +679,6 @@ <!-- UI debug setting: show the amount of overdraw in apps using the GPU [CHAR LIMIT=25] --> <string name="debug_hw_overdraw">Debug GPU overdraw</string> - <!-- UI debug setting: select the renderer to use by RenderThread [CHAR LIMIT=25] --> - <string name="debug_hw_renderer">Set GPU Renderer</string> - <!-- UI debug setting: disable use of overlays? [CHAR LIMIT=25] --> <string name="disable_overlays">Disable HW overlays</string> <!-- UI debug setting: disable use of overlays summary [CHAR LIMIT=50] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionController.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionController.java new file mode 100644 index 000000000000..f740f7c01ce1 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionController.java @@ -0,0 +1,174 @@ +/* + * 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.settingslib.suggestions; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.service.settings.suggestions.ISuggestionService; +import android.service.settings.suggestions.Suggestion; +import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; +import android.util.Log; + +import java.util.List; + +/** + * A controller class to access suggestion data. + */ +public class SuggestionController { + + /** + * Callback interface when service is connected/disconnected. + */ + public interface ServiceConnectionListener { + /** + * Called when service is connected. + */ + void onServiceConnected(); + + /** + * Called when service is disconnected. + */ + void onServiceDisconnected(); + } + + private static final String TAG = "SuggestionController"; + private static final boolean DEBUG = false; + + private final Context mContext; + private final Intent mServiceIntent; + + private ServiceConnection mServiceConnection; + private ISuggestionService mRemoteService; + private ServiceConnectionListener mConnectionListener; + + /** + * Create a new controller instance. + * + * @param context caller context + * @param service The component name for service. + * @param listener listener to receive service connected/disconnected event. + */ + public SuggestionController(Context context, ComponentName service, + ServiceConnectionListener listener) { + mContext = context.getApplicationContext(); + mConnectionListener = listener; + mServiceIntent = new Intent().setComponent(service); + mServiceConnection = createServiceConnection(); + } + + /** + * Start the controller. + */ + public void start() { + mContext.bindServiceAsUser(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE, + android.os.Process.myUserHandle()); + } + + /** + * Stop the controller. + */ + public void stop() { + if (mRemoteService != null) { + mRemoteService = null; + mContext.unbindService(mServiceConnection); + } + } + + /** + * Get setting suggestions. + */ + @Nullable + @WorkerThread + public List<Suggestion> getSuggestions() { + if (!isReady()) { + return null; + } + try { + return mRemoteService.getSuggestions(); + } catch (NullPointerException e) { + Log.w(TAG, "mRemote service detached before able to query", e); + return null; + } catch (RemoteException e) { + Log.w(TAG, "Error when calling getSuggestion()", e); + return null; + } + } + + public void dismissSuggestions(Suggestion suggestion) { + if (!isReady()) { + Log.w(TAG, "SuggestionController not ready, cannot dismiss " + suggestion.getId()); + return; + } + try { + mRemoteService.dismissSuggestion(suggestion); + } catch (RemoteException e) { + Log.w(TAG, "Error when calling dismissSuggestion()", e); + } + } + + public void launchSuggestion(Suggestion suggestion) { + if (!isReady()) { + Log.w(TAG, "SuggestionController not ready, cannot launch " + suggestion.getId()); + return; + } + + try { + mRemoteService.launchSuggestion(suggestion); + } catch (RemoteException e) { + Log.w(TAG, "Error when calling launchSuggestion()", e); + } + } + + /** + * Whether or not the manager is ready + */ + private boolean isReady() { + return mRemoteService != null; + } + + /** + * Create a new {@link ServiceConnection} object to handle service connect/disconnect event. + */ + private ServiceConnection createServiceConnection() { + return new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) { + Log.d(TAG, "Service is connected"); + } + mRemoteService = ISuggestionService.Stub.asInterface(service); + if (mConnectionListener != null) { + mConnectionListener.onServiceConnected(); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (mConnectionListener != null) { + mRemoteService = null; + mConnectionListener.onServiceDisconnected(); + } + } + }; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java new file mode 100644 index 000000000000..46fc32fa43cc --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java @@ -0,0 +1,140 @@ +/* + * 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.settingslib.suggestions; + +import android.app.LoaderManager; +import android.arch.lifecycle.OnLifecycleEvent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Loader; +import android.os.Bundle; +import android.service.settings.suggestions.Suggestion; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.android.settingslib.core.lifecycle.Lifecycle; + +import java.util.List; + +/** + * Manages IPC communication to SettingsIntelligence for suggestion related services. + */ +public class SuggestionControllerMixin implements SuggestionController.ServiceConnectionListener, + android.arch.lifecycle.LifecycleObserver, LoaderManager.LoaderCallbacks<List<Suggestion>> { + + public interface SuggestionControllerHost { + /** + * Called when suggestion data fetching is ready. + */ + void onSuggestionReady(List<Suggestion> data); + + /** + * Returns {@link LoaderManager} associated with the host. If host is not attached to + * activity then return null. + */ + @Nullable + LoaderManager getLoaderManager(); + } + + private static final String TAG = "SuggestionCtrlMixin"; + private static final boolean DEBUG = false; + + private final Context mContext; + private final SuggestionController mSuggestionController; + private final SuggestionControllerHost mHost; + + private boolean mSuggestionLoaded; + + public SuggestionControllerMixin(Context context, SuggestionControllerHost host, + Lifecycle lifecycle, ComponentName componentName) { + mContext = context.getApplicationContext(); + mHost = host; + mSuggestionController = new SuggestionController(mContext, componentName, + this /* serviceConnectionListener */); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + public void onStart() { + if (DEBUG) { + Log.d(TAG, "SuggestionController started"); + } + mSuggestionController.start(); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_STOP) + public void onStop() { + if (DEBUG) { + Log.d(TAG, "SuggestionController stopped."); + } + mSuggestionController.stop(); + } + + @Override + public void onServiceConnected() { + final LoaderManager loaderManager = mHost.getLoaderManager(); + if (loaderManager != null) { + loaderManager.restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS, + null /* args */, this /* callback */); + } + } + + @Override + public void onServiceDisconnected() { + if (DEBUG) { + Log.d(TAG, "SuggestionService disconnected"); + } + final LoaderManager loaderManager = mHost.getLoaderManager(); + if (loaderManager != null) { + loaderManager.destroyLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS); + } + } + + @Override + public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) { + if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) { + mSuggestionLoaded = false; + return new SuggestionLoader(mContext, mSuggestionController); + } + throw new IllegalArgumentException("This loader id is not supported " + id); + } + + @Override + public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) { + mSuggestionLoaded = true; + mHost.onSuggestionReady(data); + } + + @Override + public void onLoaderReset(Loader<List<Suggestion>> loader) { + mSuggestionLoaded = false; + } + + public boolean isSuggestionLoaded() { + return mSuggestionLoaded; + } + + public void dismissSuggestion(Suggestion suggestion) { + mSuggestionController.dismissSuggestions(suggestion); + } + + public void launchSuggestion(Suggestion suggestion) { + mSuggestionController.launchSuggestion(suggestion); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java new file mode 100644 index 000000000000..9c1af1edc778 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java @@ -0,0 +1,54 @@ +/* + * 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.settingslib.suggestions; + +import android.content.Context; +import android.service.settings.suggestions.Suggestion; +import android.util.Log; + +import com.android.settingslib.utils.AsyncLoader; + +import java.util.List; + +public class SuggestionLoader extends AsyncLoader<List<Suggestion>> { + + public static final int LOADER_ID_SUGGESTIONS = 42; + private static final String TAG = "SuggestionLoader"; + + private final SuggestionController mSuggestionController; + + public SuggestionLoader(Context context, SuggestionController controller) { + super(context); + mSuggestionController = controller; + } + + @Override + protected void onDiscardResult(List<Suggestion> result) { + + } + + @Override + public List<Suggestion> loadInBackground() { + final List<Suggestion> data = mSuggestionController.getSuggestions(); + if (data == null) { + Log.d(TAG, "data is null"); + } else { + Log.d(TAG, "data size " + data.size()); + } + return data; + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/ShadowSuggestionController.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/ShadowSuggestionController.java new file mode 100644 index 000000000000..61bc83b8f72e --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/ShadowSuggestionController.java @@ -0,0 +1,61 @@ +/* + * 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.settingslib.suggestions; + +import android.service.settings.suggestions.Suggestion; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.List; + +@Implements(SuggestionController.class) +public class ShadowSuggestionController { + + public static boolean sStartCalled; + public static boolean sStopCalled; + public static boolean sGetSuggestionCalled; + + public static List<Suggestion> sSuggestions; + + public static void reset() { + sStartCalled = false; + sStopCalled = false; + sGetSuggestionCalled = false; + sSuggestions = null; + } + + @Implementation + public void start() { + sStartCalled = true; + } + + @Implementation + public void stop() { + sStopCalled = true; + } + + public static void setSuggestion(List<Suggestion> suggestions) { + sSuggestions = suggestions; + } + + @Implementation + public List<Suggestion> getSuggestions() { + sGetSuggestionCalled = true; + return sSuggestions; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java new file mode 100644 index 000000000000..ed1c405f8a81 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.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.settingslib.suggestions; + +import static android.arch.lifecycle.Lifecycle.Event.ON_START; +import static android.arch.lifecycle.Lifecycle.Event.ON_STOP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.LoaderManager; +import android.arch.lifecycle.LifecycleOwner; +import android.content.ComponentName; +import android.content.Context; + +import com.android.settingslib.TestConfig; +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = { + ShadowSuggestionController.class + }) +public class SuggestionControllerMixinTest { + + @Mock + private SuggestionControllerMixin.SuggestionControllerHost mHost; + + private Context mContext; + private LifecycleOwner mLifecycleOwner; + private Lifecycle mLifecycle; + private SuggestionControllerMixin mMixin; + private ComponentName mComponentName; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + mComponentName = new ComponentName( + "com.android.settings.intelligence", + "com.android.settings.intelligence.suggestions.SuggestionService"); + } + + @After + public void tearDown() { + ShadowSuggestionController.reset(); + } + + @Test + public void goThroughLifecycle_onStartStop_shouldStartStopController() { + mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle, mComponentName); + + mLifecycle.handleLifecycleEvent(ON_START); + assertThat(ShadowSuggestionController.sStartCalled).isTrue(); + + mLifecycle.handleLifecycleEvent(ON_STOP); + assertThat(ShadowSuggestionController.sStopCalled).isTrue(); + } + + @Test + public void onServiceConnected_shouldGetSuggestion() { + final LoaderManager loaderManager = mock(LoaderManager.class); + when(mHost.getLoaderManager()).thenReturn(loaderManager); + + mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle, mComponentName); + mMixin.onServiceConnected(); + + verify(loaderManager).restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS, + null /* args */, mMixin /* callback */); + } + + @Test + public void onServiceConnected_hostNotAttached_shouldDoNothing() { + when(mHost.getLoaderManager()).thenReturn(null); + + mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle, mComponentName); + mMixin.onServiceConnected(); + + verify(mHost).getLoaderManager(); + } + + @Test + public void onServiceDisconnected_hostNotAttached_shouldDoNothing() { + when(mHost.getLoaderManager()).thenReturn(null); + + mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle, mComponentName); + mMixin.onServiceDisconnected(); + + verify(mHost).getLoaderManager(); + } + + @Test + public void doneLoadingg_shouldSetSuggestionLoaded() { + mMixin = new SuggestionControllerMixin(mContext, mHost, mLifecycle, mComponentName); + + mMixin.onLoadFinished(mock(SuggestionLoader.class), null); + + assertThat(mMixin.isSuggestionLoaded()).isTrue(); + + mMixin.onLoaderReset(mock(SuggestionLoader.class)); + + assertThat(mMixin.isSuggestionLoaded()).isFalse(); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6970c86aa5d7..b286f89d0049 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -103,21 +103,6 @@ class SettingsProtoDumpUtil { Settings.Global.THEATER_MODE_ON, GlobalSettingsProto.THEATER_MODE_ON); dumpSetting(s, p, - Settings.Global.RADIO_BLUETOOTH, - GlobalSettingsProto.RADIO_BLUETOOTH); - dumpSetting(s, p, - Settings.Global.RADIO_WIFI, - GlobalSettingsProto.RADIO_WIFI); - dumpSetting(s, p, - Settings.Global.RADIO_WIMAX, - GlobalSettingsProto.RADIO_WIMAX); - dumpSetting(s, p, - Settings.Global.RADIO_CELL, - GlobalSettingsProto.RADIO_CELL); - dumpSetting(s, p, - Settings.Global.RADIO_NFC, - GlobalSettingsProto.RADIO_NFC); - dumpSetting(s, p, Settings.Global.AIRPLANE_MODE_RADIOS, GlobalSettingsProto.AIRPLANE_MODE_RADIOS); dumpSetting(s, p, @@ -592,6 +577,9 @@ class SettingsProtoDumpUtil { Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, GlobalSettingsProto.WIFI_VERBOSE_LOGGING_ENABLED); dumpSetting(s, p, + Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, + GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED); + dumpSetting(s, p, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, GlobalSettingsProto.WIFI_MAX_DHCP_RETRY_COUNT); dumpSetting(s, p, @@ -1067,6 +1055,9 @@ class SettingsProtoDumpUtil { Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD, GlobalSettingsProto.STORAGE_SETTINGS_CLOBBER_THRESHOLD); dumpSetting(s, p, + Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, + GlobalSettingsProto.CHAINED_BATTERY_ATTRIBUTION_ENABLED); + dumpSetting(s, p, Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION); dumpSetting(s, p, @@ -1130,20 +1121,13 @@ class SettingsProtoDumpUtil { Settings.Global.SHOW_FIRST_CRASH_DIALOG, GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG); dumpSetting(s, p, - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED); - dumpSetting(s, p, Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, GlobalSettingsProto.SHOW_RESTART_IN_CRASH_DIALOG); dumpSetting(s, p, Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG, GlobalSettingsProto.SHOW_MUTE_IN_CRASH_DIALOG); - dumpSetting(s, p, - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED); - dumpSetting(s, p, - Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, - GlobalSettingsProto.CHAINED_BATTERY_ATTRIBUTION_ENABLED); + + // Please insert new settings using the same order as in Settings.Global. } /** Dump a single {@link SettingsState.Setting} to a proto buf */ @@ -1771,6 +1755,8 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING, SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING); + + // Please insert new settings using the same order as in Settings.Secure. } private static void dumpProtoSystemSettingsLocked( @@ -2029,5 +2015,7 @@ class SettingsProtoDumpUtil { SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS); // The rest of the settings were moved to Settings.Secure, and are thus excluded here since // they're deprecated from Settings.System. + + // Please insert new settings using the same order as in Settings.System. } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 79299aa6abcb..64b2ae6e23d3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -44,7 +44,6 @@ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.USE_RESERVED_DISK" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- System tool permissions granted to the shell. --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index 7b30d6ae8717..cd1be152b8d2 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -40,7 +40,7 @@ <LinearLayout android:id="@+id/system_icons_super_container" android:layout_width="wrap_content" - android:layout_height="@dimen/status_bar_header_height" + android:layout_height="@*android:dimen/quick_qs_total_height" android:layout_toStartOf="@id/multi_user_switch" android:layout_alignWithParentIfMissing="true" android:layout_marginStart="@dimen/system_icons_super_container_margin_start" diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 5bcb7fd2685e..1dab76183864 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -33,7 +33,7 @@ <View android:id="@+id/quick_settings_status_bar_background" android:layout_width="match_parent" - android:layout_height="@dimen/qs_header_system_icons_area_height" + android:layout_height="@*android:dimen/quick_qs_offset_height" android:clipToPadding="false" android:clipChildren="false" android:background="#ff000000" /> @@ -43,7 +43,7 @@ android:id="@+id/quick_settings_gradient_view" android:layout_width="match_parent" android:layout_height="126dp" - android:layout_marginTop="@dimen/qs_header_system_icons_area_height" + android:layout_marginTop="@*android:dimen/quick_qs_offset_height" android:clipToPadding="false" android:clipChildren="false" android:background="@drawable/qs_bg_gradient" /> @@ -51,7 +51,7 @@ <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" - android:layout_marginTop="@dimen/qs_header_system_icons_area_height" + android:layout_marginTop="@*android:dimen/quick_qs_offset_height" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="48dp" diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index dacc3f96220b..cc79d0d9b7dd 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="@dimen/status_bar_header_height" + android:layout_height="@*android:dimen/quick_qs_total_height" android:layout_gravity="@integer/notification_panel_layout_gravity" android:baselineAligned="false" android:clickable="false" diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml index 2c69501106a6..f38129f0dffa 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml @@ -19,7 +19,7 @@ xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/quick_status_bar_system_icons" android:layout_width="match_parent" - android:layout_height="@dimen/qs_header_system_icons_area_height" + android:layout_height="@*android:dimen/quick_qs_offset_height" android:layout_alignParentEnd="true" android:clipChildren="false" android:clipToPadding="false" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d58c725c6462..3db79d7556d5 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -214,9 +214,6 @@ <!-- Amount of close_handle that will NOT overlap the notification list --> <dimen name="close_handle_underlap">32dp</dimen> - <!-- Height of the status bar header bar --> - <dimen name="status_bar_header_height">178dp</dimen> - <!-- Height of the status bar header bar in the car setting. --> <dimen name="car_status_bar_header_height">128dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6d61a0c198f9..9b6af43a1920 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -771,8 +771,10 @@ <string name="quick_settings_tethering_label">Tethering</string> <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] --> <string name="quick_settings_hotspot_label">Hotspot</string> + <!-- QuickSettings: Hotspot. Secondary label shown when the hotspot is being enabled [CHAR LIMIT=NONE] --> + <string name="quick_settings_hotspot_secondary_label_transient">Turning on...</string> <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] --> - <plurals name="quick_settings_hotspot_num_devices"> + <plurals name="quick_settings_hotspot_secondary_label_num_devices"> <item quantity="one">%d device</item> <item quantity="other">%d devices</item> </plurals> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index cc4bc58fbf9d..da50776708b3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -29,4 +29,9 @@ interface ISystemUiProxy { */ GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean useIdentityTransform, int rotation); + + /** + * Called when the overview service has started the recents animation. + */ + void onRecentsAnimationStarted(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index c9a6ea9939f5..f9e1069cfe95 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -32,6 +32,7 @@ import android.app.AppGlobals; import android.app.IAssistDataReceiver; import android.app.WindowConfiguration.ActivityType; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -48,7 +49,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.IconDrawableFactory; import android.util.Log; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -243,10 +247,9 @@ public class ActivityManagerWrapper { /** * Starts the recents activity. The caller should manage the thread on which this is called. */ - public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options, - ActivityOptions opts, int userId, Consumer<Boolean> resultCallback, + public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver, + RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { - Bundle activityOptions = opts != null ? opts.toBundle() : null; try { IAssistDataReceiver receiver = null; if (assistDataReceiver != null) { @@ -259,8 +262,24 @@ public class ActivityManagerWrapper { } }; } - ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions, - userId); + IRecentsAnimationRunner runner = null; + if (animationHandler != null) { + runner = new IRecentsAnimationRunner.Stub() { + public void onAnimationStart(IRecentsAnimationController controller, + RemoteAnimationTarget[] apps) { + final RecentsAnimationControllerCompat controllerCompat = + new RecentsAnimationControllerCompat(controller); + final RemoteAnimationTargetCompat[] appsCompat = + RemoteAnimationTargetCompat.wrap(apps); + animationHandler.onAnimationStart(controllerCompat, appsCompat); + } + + public void onAnimationCanceled() { + animationHandler.onAnimationCanceled(); + } + }; + } + ActivityManager.getService().startRecentsActivity(intent, receiver, runner); if (resultCallback != null) { resultCallbackHandler.post(new Runnable() { @Override diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java index cd943f62ea9b..7cd6c512b660 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java @@ -22,7 +22,7 @@ import android.os.Bundle; /** * Abstract class for assist data receivers. */ -public abstract class AssistDataReceiverCompat { - public abstract void onHandleAssistData(Bundle resultData); - public abstract void onHandleAssistScreenshot(Bitmap screenshot); +public abstract class AssistDataReceiver { + public void onHandleAssistData(Bundle resultData) {} + public void onHandleAssistScreenshot(Bitmap screenshot) {} } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java index db4f988a9122..38b8ae8418af 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.android.systemui.pip.phone; +package com.android.systemui.shared.system; import static android.view.WindowManager.INPUT_CONSUMER_PIP; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import android.os.Binder; import android.os.IBinder; @@ -29,11 +30,12 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.IWindowManager; import android.view.MotionEvent; +import android.view.WindowManagerGlobal; import java.io.PrintWriter; /** - * Manages the input consumer that allows the SystemUI to control the PiP. + * Manages the input consumer that allows the SystemUI to directly receive touch input. */ public class InputConsumerController { @@ -55,12 +57,12 @@ public class InputConsumerController { } /** - * Input handler used for the PiP input consumer. Input events are batched and consumed with the + * Input handler used for the input consumer. Input events are batched and consumed with the * SurfaceFlinger vsync. */ - private final class PipInputEventReceiver extends BatchedInputEventReceiver { + private final class InputEventReceiver extends BatchedInputEventReceiver { - public PipInputEventReceiver(InputChannel inputChannel, Looper looper) { + public InputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper, Choreographer.getSfInstance()); } @@ -68,7 +70,6 @@ public class InputConsumerController { public void onInputEvent(InputEvent event, int displayId) { boolean handled = true; try { - // To be implemented for input handling over Pip windows if (mListener != null && event instanceof MotionEvent) { MotionEvent ev = (MotionEvent) event; handled = mListener.onTouchEvent(ev); @@ -81,15 +82,35 @@ public class InputConsumerController { private final IWindowManager mWindowManager; private final IBinder mToken; + private final String mName; - private PipInputEventReceiver mInputEventReceiver; + private InputEventReceiver mInputEventReceiver; private TouchListener mListener; private RegistrationListener mRegistrationListener; - public InputConsumerController(IWindowManager windowManager) { + /** + * @param name the name corresponding to the input consumer that is defined in the system. + */ + public InputConsumerController(IWindowManager windowManager, String name) { mWindowManager = windowManager; mToken = new Binder(); - registerInputConsumer(); + mName = name; + } + + /** + * @return A controller for the pip input consumer. + */ + public static InputConsumerController getPipInputConsumer() { + return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), + INPUT_CONSUMER_PIP); + } + + /** + * @return A controller for the recents animation input consumer. + */ + public static InputConsumerController getRecentsAnimationInputConsumer() { + return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), + INPUT_CONSUMER_RECENTS_ANIMATION); } /** @@ -125,12 +146,12 @@ public class InputConsumerController { if (mInputEventReceiver == null) { final InputChannel inputChannel = new InputChannel(); try { - mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP); - mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel); + mWindowManager.destroyInputConsumer(mName); + mWindowManager.createInputConsumer(mToken, mName, inputChannel); } catch (RemoteException e) { - Log.e(TAG, "Failed to create PIP input consumer", e); + Log.e(TAG, "Failed to create input consumer", e); } - mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper()); + mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper()); if (mRegistrationListener != null) { mRegistrationListener.onRegistrationChanged(true /* isRegistered */); } @@ -143,9 +164,9 @@ public class InputConsumerController { public void unregisterInputConsumer() { if (mInputEventReceiver != null) { try { - mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP); + mWindowManager.destroyInputConsumer(mName); } catch (RemoteException e) { - Log.e(TAG, "Failed to destroy PIP input consumer", e); + Log.e(TAG, "Failed to destroy input consumer", e); } mInputEventReceiver.dispose(); mInputEventReceiver = null; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java new file mode 100644 index 000000000000..9a7abf82c56c --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.app.ActivityManager.TaskSnapshot; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRecentsAnimationController; + +import com.android.systemui.shared.recents.model.ThumbnailData; + +public class RecentsAnimationControllerCompat { + + private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName(); + + private IRecentsAnimationController mAnimationController; + + public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) { + mAnimationController = animationController; + } + + public ThumbnailData screenshotTask(int taskId) { + try { + TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId); + return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to screenshot task", e); + return new ThumbnailData(); + } + } + + public void setInputConsumerEnabled(boolean enabled) { + try { + mAnimationController.setInputConsumerEnabled(enabled); + } catch (RemoteException e) { + Log.e(TAG, "Failed to set input consumer enabled state", e); + } + } + + public void finish(boolean toHome) { + try { + mAnimationController.finish(toHome); + } catch (RemoteException e) { + Log.e(TAG, "Failed to finish recents animation", e); + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java new file mode 100644 index 000000000000..bf6179d70a5e --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.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.systemui.shared.system; + +public interface RecentsAnimationListener { + + /** + * Called when the animation into Recents can start. This call is made on the binder thread. + */ + void onAnimationStart(RecentsAnimationControllerCompat controller, + RemoteAnimationTargetCompat[] apps); + + /** + * Called when the animation into Recents was canceled. This call is made on the binder thread. + */ + void onAnimationCanceled(); +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 244c1b990448..b6e49ae6cc2c 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -77,6 +77,15 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Binder.restoreCallingIdentity(token); } } + + public void onRecentsAnimationStarted() { + long token = Binder.clearCallingIdentity(); + try { + notifyRecentsAnimationStarted(); + } finally { + Binder.restoreCallingIdentity(token); + } + } }; private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() { @@ -214,6 +223,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + private void notifyRecentsAnimationStarted() { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).onRecentsAnimationStarted(); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(TAG_OPS + " state:"); @@ -224,6 +239,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } public interface OverviewProxyListener { - void onConnectionChanged(boolean isConnected); + default void onConnectionChanged(boolean isConnected) {} + default void onRecentsAnimationStarted() {} } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 36531bb727a4..24d0126a1494 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -16,12 +16,10 @@ package com.android.systemui.pip.phone; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.INPUT_CONSUMER_PIP; import android.app.ActivityManager; -import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; import android.content.ComponentName; import android.content.Context; @@ -43,6 +41,7 @@ import com.android.systemui.recents.events.component.ExpandPipEvent; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.InputConsumerController; import java.io.PrintWriter; @@ -174,7 +173,8 @@ public class PipManager implements BasePipManager { } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mInputConsumerController = new InputConsumerController(mWindowManager); + mInputConsumerController = InputConsumerController.getPipInputConsumer(); + mInputConsumerController.registerInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager); mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, mInputConsumerController); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 9fb201b82d8c..26fced307bac 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -23,7 +23,6 @@ import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.RemoteAction; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -43,6 +42,7 @@ import com.android.systemui.pip.phone.PipMediaController.ActionListener; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.component.HidePipMenuEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; +import com.android.systemui.shared.system.InputConsumerController; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index c0fed342ef44..b25351731a35 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -46,6 +46,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.PipSnapAlgorithm; import com.android.systemui.R; +import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 29a8f1641a85..669439d7f6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -285,17 +285,22 @@ public class QSFragment extends Fragment implements QS { int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom() + mFooter.getHeight(); mQSPanel.setTranslationY(translationScaleY * heightDiff); - mQSDetail.setFullyExpanded(expansion == 1); + boolean fullyExpanded = expansion == 1; + mQSDetail.setFullyExpanded(fullyExpanded); + if (fullyExpanded) { + // Always draw within the bounds of the view when fully expanded. + mQSPanel.setClipBounds(null); + } else { + // Set bounds on the QS panel so it doesn't run over the header when animating. + mQsBounds.top = (int) -mQSPanel.getTranslationY(); + mQsBounds.right = mQSPanel.getWidth(); + mQsBounds.bottom = mQSPanel.getHeight(); + mQSPanel.setClipBounds(mQsBounds); + } if (mQSAnimator != null) { mQSAnimator.setPosition(expansion); } - - // Set bounds on the QS panel so it doesn't run over the header. - mQsBounds.top = (int) -mQSPanel.getTranslationY(); - mQsBounds.right = mQSPanel.getWidth(); - mQsBounds.bottom = mQSPanel.getHeight(); - mQSPanel.setClipBounds(mQsBounds); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 17ede6586402..77768b1a4bd1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -166,7 +166,8 @@ public class QuickStatusBarHeader extends RelativeLayout if (disabled == mQsDisabled) return; mQsDisabled = disabled; mHeaderQsPanel.setDisabledByPolicy(disabled); - final int rawHeight = (int) getResources().getDimension(R.dimen.status_bar_header_height); + final int rawHeight = (int) getResources().getDimension( + com.android.internal.R.dimen.quick_qs_total_height); getLayoutParams().height = disabled ? (rawHeight - mHeaderQsPanel.getHeight()) : rawHeight; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index eb2e519fb560..aaf6ef52d761 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -76,9 +76,6 @@ public class DndTile extends QSTileImpl<BooleanState> { private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE"; private static final String EXTRA_VISIBLE = "visible"; - private static final QSTile.Icon TOTAL_SILENCE = - ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence); - private final ZenModeController mController; private final DndDetailAdapter mDetailAdapter; @@ -201,25 +198,22 @@ public class DndTile extends QSTileImpl<BooleanState> { state.slash.isSlashed = !state.value; state.label = getTileLabel(); state.secondaryLabel = getSecondaryLabel(zen != Global.ZEN_MODE_OFF); + state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME); switch (zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: - state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd_priority_on); break; case Global.ZEN_MODE_NO_INTERRUPTIONS: - state.icon = TOTAL_SILENCE; state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd_none_on); break; case ZEN_MODE_ALARMS: - state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd_alarms_on); break; default: - state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd); break; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index e1b58fe4c2d6..080e320802e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -113,11 +113,11 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { if (state.slash == null) { state.slash = new SlashState(); } - state.label = mContext.getString(R.string.quick_settings_hotspot_label); - - checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING); final int numConnectedDevices; + final boolean isTransient = mController.isHotspotTransient(); + + checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING); if (arg instanceof CallbackInfo) { CallbackInfo info = (CallbackInfo) arg; state.value = info.enabled; @@ -127,11 +127,11 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { numConnectedDevices = mController.getNumConnectedDevices(); } - state.secondaryLabel = getSecondaryLabel(state.value, numConnectedDevices); - state.icon = mEnabledStatic; + state.label = mContext.getString(R.string.quick_settings_hotspot_label); + state.secondaryLabel = getSecondaryLabel(state.value, isTransient, numConnectedDevices); state.isAirplaneMode = mAirplaneMode.getValue() != 0; - state.isTransient = mController.isHotspotTransient(); + state.isTransient = isTransient; state.slash.isSlashed = !state.value && !state.isTransient; if (state.isTransient) { state.icon = ResourceIcon.get(R.drawable.ic_hotspot_transient_animation); @@ -143,10 +143,13 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { } @Nullable - private String getSecondaryLabel(boolean enabled, int numConnectedDevices) { - if (numConnectedDevices > 0 && enabled) { + private String getSecondaryLabel( + boolean enabled, boolean isTransient, int numConnectedDevices) { + if (isTransient) { + return mContext.getString(R.string.quick_settings_hotspot_secondary_label_transient); + } else if (numConnectedDevices > 0 && enabled) { return mContext.getResources().getQuantityString( - R.plurals.quick_settings_hotspot_num_devices, + R.plurals.quick_settings_hotspot_secondary_label_num_devices, numConnectedDevices, numConnectedDevices); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 1056ecc9bf6a..b3f68d357083 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -1472,7 +1472,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getActiveRemoteInputText(); } - public void animateTranslateNotification(final float leftTarget) { if (mTranslateAnim != null) { mTranslateAnim.cancel(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 8336d29f2870..907af690a615 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.graphics.Matrix; import android.graphics.Rect; @@ -35,10 +36,11 @@ import android.view.ViewRootImpl; import com.android.systemui.Interpolators; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationListContainer; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowView; -import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.function.Consumer; @@ -57,16 +59,17 @@ public class ActivityLaunchAnimator { private final NotificationPanelView mNotificationPanel; private final NotificationListContainer mNotificationContainer; private final StatusBarWindowView mStatusBarWindow; - private final Consumer<Boolean> mPanelCollapser; + private final StatusBar mStatusBar; + private boolean mAnimationPending; public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow, - Consumer<Boolean> panelCollapser, + StatusBar statusBar, NotificationPanelView notificationPanel, NotificationListContainer container) { mNotificationPanel = notificationPanel; mNotificationContainer = container; mStatusBarWindow = statusBarWindow; - mPanelCollapser = panelCollapser; + mStatusBar = statusBar; } public ActivityOptions getLaunchAnimation( @@ -76,6 +79,21 @@ public class ActivityLaunchAnimator { new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */)); } + public boolean isAnimationPending() { + return mAnimationPending; + } + + public void setLaunchResult(int launchResult) { + setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT + || launchResult == ActivityManager.START_SUCCESS) + && mStatusBar.getBarState() == StatusBarState.SHADE); + } + + private void setAnimationPending(boolean pending) { + mAnimationPending = pending; + mStatusBarWindow.setExpandAnimationPending(pending); + } + class AnimationRunner extends IRemoteAnimationRunner.Stub { private final ExpandableNotificationRow mSourceNotification; @@ -94,7 +112,6 @@ public class ActivityLaunchAnimator { IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback) throws RemoteException { mSourceNotification.post(() -> { - boolean first = true; for (RemoteAnimationTarget app : remoteAnimationTargets) { if (app.mode == RemoteAnimationTarget.MODE_OPENING) { setExpandAnimationRunning(true); @@ -139,7 +156,7 @@ public class ActivityLaunchAnimator { public void onAnimationEnd(Animator animation) { setExpandAnimationRunning(false); if (mInstantCollapsePanel) { - mPanelCollapser.accept(false /* animate */); + mStatusBar.collapsePanel(false /* animate */); } try { iRemoteAnimationFinishedCallback.onAnimationFinished(); @@ -152,6 +169,7 @@ public class ActivityLaunchAnimator { break; } } + setAnimationPending(false); }); } @@ -198,6 +216,10 @@ public class ActivityLaunchAnimator { @Override public void onAnimationCancelled() throws RemoteException { + mSourceNotification.post(() -> { + setAnimationPending(false); + mStatusBar.onLaunchAnimationCancelled(); + }); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index dec5303ff7bb..918b6edc0c30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -137,8 +137,12 @@ public class TransformState { final View transformedView = mTransformedView; boolean transformX = (transformationFlags & TRANSFORM_X) != 0; boolean transformY = (transformationFlags & TRANSFORM_Y) != 0; - boolean differentHeight = otherState.getViewHeight() != getViewHeight(); - boolean differentWidth = otherState.getViewWidth() != getViewWidth(); + int viewHeight = getViewHeight(); + int otherHeight = otherState.getViewHeight(); + boolean differentHeight = otherHeight != viewHeight && otherHeight != 0 && viewHeight != 0; + int viewWidth = getViewWidth(); + int otherWidth = otherState.getViewWidth(); + boolean differentWidth = otherWidth != viewWidth && otherWidth != 0 && viewWidth != 0; boolean transformScale = transformScale(otherState) && (differentHeight || differentWidth); // lets animate the positions correctly if (transformationAmount == 0.0f @@ -165,15 +169,15 @@ public class TransformState { // we also want to animate the scale if we're the same View otherView = otherState.getTransformedView(); if (transformScale && differentWidth) { - setTransformationStartScaleX(otherState.getViewWidth() * otherView.getScaleX() - / (float) getViewWidth()); + setTransformationStartScaleX(otherWidth * otherView.getScaleX() + / (float) viewWidth); transformedView.setPivotX(0); } else { setTransformationStartScaleX(UNDEFINED); } if (transformScale && differentHeight) { - setTransformationStartScaleY(otherState.getViewHeight() * otherView.getScaleY() - / (float) getViewHeight()); + setTransformationStartScaleY(otherHeight * otherView.getScaleY() + / (float) viewHeight); transformedView.setPivotY(0); } else { setTransformationStartScaleY(UNDEFINED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index da809c12d16c..09acf3e4b53b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -115,7 +115,7 @@ public class KeyguardStatusBarView extends RelativeLayout lp = (MarginLayoutParams) mSystemIconsSuperContainer.getLayoutParams(); lp.height = getResources().getDimensionPixelSize( - R.dimen.status_bar_header_height); + com.android.internal.R.dimen.quick_qs_total_height); lp.setMarginStart(getResources().getDimensionPixelSize( R.dimen.system_icons_super_container_margin_start)); mSystemIconsSuperContainer.setLayoutParams(lp); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 61c8027eb800..5401e744f3ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -300,7 +300,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { boolean showImeSwitcher) { boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; int hints = mNavigationIconHints; - if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { + if (imeShown && backDisposition != InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS) { hints |= NAVIGATION_HINT_BACK_ALT; } else { hints &= ~NAVIGATION_HINT_BACK_ALT; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 0954fd054e2d..0d36efda9ece 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -308,8 +308,13 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture } public boolean onTouchEvent(MotionEvent event) { + // The same down event was just sent on intercept and therefore can be ignored here + boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN + && mOverviewEventSender.getProxy() != null; boolean result = mStatusBar.isPresenterFullyCollapsed() - && (mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event)); + && (mQuickScrubController.onTouchEvent(event) + || ignoreProxyDownEvent + || proxyMotionEvents(event)); if (mDockWindowEnabled) { result |= handleDockWindowEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 9f4d35e19332..b5fa52378660 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -104,6 +104,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private DeadZone mDeadZone; private final NavigationBarTransitions mBarTransitions; private final OverviewProxyService mOverviewProxyService; + private boolean mRecentsAnimationStarted; // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) final static boolean WORKAROUND_INVALID_LAYOUT = true; @@ -206,10 +207,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - private final OverviewProxyListener mOverviewProxyListener = isConnected -> { - updateSlippery(); - setDisabledFlags(mDisabledFlags, true); - setUpSwipeUpOnboarding(isConnected); + private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { + @Override + public void onConnectionChanged(boolean isConnected) { + updateSlippery(); + setDisabledFlags(mDisabledFlags, true); + setUpSwipeUpOnboarding(isConnected); + } + + @Override + public void onRecentsAnimationStarted() { + mRecentsAnimationStarted = true; + } }; public NavigationBarView(Context context, AttributeSet attrs) { @@ -273,12 +282,26 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav if (mGestureHelper.onTouchEvent(event)) { return true; } - return super.onTouchEvent(event); + return mRecentsAnimationStarted || super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { - return mGestureHelper.onInterceptTouchEvent(event); + int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + mRecentsAnimationStarted = false; + } else if (action == MotionEvent.ACTION_UP) { + // If the overview proxy service has not started the recents animation then clean up + // after it to ensure that the nav bar buttons still work + if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) { + try { + ActivityManager.getService().cancelRecentsAnimation(); + } catch (RemoteException e) { + Log.e(TAG, "Could not cancel recents animation"); + } + } + } + return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event); } public void abortCurrentGesture() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index a62a424caaf5..2b7e4747a837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -950,7 +950,7 @@ public abstract class PanelView extends FrameLayout { } public boolean isCollapsing() { - return mClosing; + return mClosing || mLaunchingNotification; } public boolean isTracking() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 7d8455002300..b51982415bbd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -100,7 +100,6 @@ import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; -import android.text.SpannedString; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; @@ -589,7 +588,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NavigationBarFragment mNavigationBar; private View mNavigationBarView; - private ActivityLaunchAnimator mActivityLaunchAnimator; + protected ActivityLaunchAnimator mActivityLaunchAnimator; @Override public void start() { @@ -760,7 +759,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel); mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow, - this::collapsePanel, + this, mNotificationPanel, mStackScroller); mGutsManager.setUpWithPresenter(this, mEntryManager, mStackScroller, mCheckSaveListener, @@ -2054,6 +2053,12 @@ public class StatusBar extends SystemUI implements DemoMode, } } + public void onLaunchAnimationCancelled() { + if (!isCollapsing()) { + onClosingFinished(); + } + } + /** * All changes to the status bar and notifications funnel through here and are batched. */ @@ -3418,7 +3423,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isCollapsing() { - return mNotificationPanel.isCollapsing(); + return mNotificationPanel.isCollapsing() || mActivityLaunchAnimator.isAnimationPending(); } public void addPostCollapseAction(Runnable r) { @@ -4959,6 +4964,7 @@ public class StatusBar extends SystemUI implements DemoMode, try { launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, getActivityOptions(row)); + mActivityLaunchAnimator.setLaunchResult(launchResult); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -4970,7 +4976,7 @@ public class StatusBar extends SystemUI implements DemoMode, mAssistManager.hideAssist(); } } - if (shouldCollapse(launchResult)) { + if (shouldCollapse()) { if (Looper.getMainLooper().isCurrentThread()) { collapsePanel(); } else { @@ -5003,17 +5009,8 @@ public class StatusBar extends SystemUI implements DemoMode, }, afterKeyguardGone); } - private boolean shouldCollapse(int launchResult) { - return mState != StatusBarState.SHADE - || (launchResult != ActivityManager.START_TASK_TO_FRONT - && launchResult != ActivityManager.START_SUCCESS); - } - - public void onExpandAnimationFinished() { - if (!isPresenterFullyCollapsed()) { - instantCollapseNotificationPanel(); - visibilityChanged(false); - } + private boolean shouldCollapse() { + return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending(); } public void collapsePanel(boolean animate) { @@ -5128,7 +5125,8 @@ public class StatusBar extends SystemUI implements DemoMode, .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions(row), new UserHandle(UserHandle.getUserId(appUid))); - if (shouldCollapse(launchResult)) { + mActivityLaunchAnimator.setLaunchResult(launchResult); + if (shouldCollapse()) { // Putting it back on the main thread, since we're touching views mStatusBarWindow.post(() -> animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index f7d0967c2378..e32914fa368b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -89,6 +89,7 @@ public class StatusBarWindowView extends FrameLayout { private boolean mTouchCancelled; private boolean mTouchActive; private boolean mExpandAnimationRunning; + private boolean mExpandAnimationPending; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); @@ -268,7 +269,7 @@ public class StatusBarWindowView extends FrameLayout { || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { setTouchActive(false); } - if (mTouchCancelled || mExpandAnimationRunning) { + if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { return false; } mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); @@ -393,6 +394,10 @@ public class StatusBarWindowView extends FrameLayout { mExpandAnimationRunning = expandAnimationRunning; } + public void setExpandAnimationPending(boolean pending) { + mExpandAnimationPending = pending; + } + public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 1e894ff4ee54..3febdfdfbe7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -119,7 +119,8 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio // for the current foreground user. LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - return locationManager.isLocationEnabledForUser(Process.myUserHandle()); + return locationManager.isLocationEnabledForUser( + UserHandle.of(ActivityManager.getCurrentUser())); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 56b7201ea274..3e13ddb1028a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -341,7 +341,8 @@ public class VolumeDialogImpl implements VolumeDialog { row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device); // forward events above the slider into the slider - row.view.setOnTouchListener(new OnTouchListener() { + row.view.findViewById(R.id.volume_row_slider_frame) + .setOnTouchListener(new OnTouchListener() { private final Rect mSliderHitRect = new Rect(); private boolean mDragging; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 99202f4ac10c..bdf9b1f6da9e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -188,7 +189,8 @@ public class StatusBarTest extends SysuiTestCase { mKeyguardIndicationController, mStackScroller, mHeadsUpManager, mPowerManager, mNotificationPanelView, mBarService, mNotificationListener, mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager, - mEntryManager, mScrimController, mFingerprintUnlockController); + mEntryManager, mScrimController, mFingerprintUnlockController, + mock(ActivityLaunchAnimator.class)); mStatusBar.mContext = mContext; mStatusBar.mComponents = mContext.getComponents(); mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager, @@ -593,7 +595,8 @@ public class StatusBarTest extends SysuiTestCase { VisualStabilityManager visualStabilityManager, NotificationViewHierarchyManager viewHierarchyManager, TestableNotificationEntryManager entryManager, ScrimController scrimController, - FingerprintUnlockController fingerprintUnlockController) { + FingerprintUnlockController fingerprintUnlockController, + ActivityLaunchAnimator launchAnimator) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; mKeyguardIndicationController = key; @@ -610,6 +613,7 @@ public class StatusBarTest extends SysuiTestCase { mEntryManager = entryManager; mScrimController = scrimController; mFingerprintUnlockController = fingerprintUnlockController; + mActivityLaunchAnimator = launchAnimator; } private WakefulnessLifecycle createAwakeWakefulnessLifecycle() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java index de99d71351c7..c18ed732f244 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -28,11 +28,12 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothProfile; import android.net.wifi.WifiManager; -import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.support.v7.media.MediaRouter; import android.telecom.TelecomManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.widget.TextView; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -49,7 +50,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class OutputChooserDialogTest extends SysuiTestCase { OutputChooserDialog mDialog; @@ -68,7 +70,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { @Before - @UiThreadTest public void setup() throws Exception { MockitoAnnotations.initMocks(this); @@ -82,8 +83,12 @@ public class OutputChooserDialogTest extends SysuiTestCase { mDialog = new OutputChooserDialog(getContext(), mRouter); } + @After + public void tearDown() throws Exception { + TestableLooper.get(this).processAllMessages(); + } + @Test - @UiThreadTest public void testClickMediaRouterItemConnectsMedia() { mDialog.show(); @@ -100,7 +105,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testClickBtItemConnectsBt() { mDialog.show(); @@ -116,7 +120,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testTitleNotInCall() { mDialog.show(); @@ -126,7 +129,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testTitleInCall() { mDialog.setIsInCall(true); mDialog.show(); @@ -137,7 +139,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testNoMediaScanIfInCall() { mDialog.setIsInCall(true); mDialog.onAttachedToWindow(); @@ -146,7 +147,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testMediaScanIfNotInCall() { mDialog.setIsInCall(false); mDialog.onAttachedToWindow(); @@ -155,7 +155,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testRegisterCallbacks() { mDialog.setIsInCall(false); mDialog.onAttachedToWindow(); @@ -166,7 +165,6 @@ public class OutputChooserDialogTest extends SysuiTestCase { } @Test - @UiThreadTest public void testUnregisterCallbacks() { mDialog.setIsInCall(false); mDialog.onDetachedFromWindow(); diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml index 43bde88dabdd..a584a7f3fb90 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml @@ -47,6 +47,10 @@ <!-- Height of the status bar --> <dimen name="status_bar_height">48dp</dimen> + <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) --> + <dimen name="quick_qs_offset_height">48dp</dimen> + <!-- Total height of QQS (quick_qs_offset_height + 128) --> + <dimen name="quick_qs_total_height">176dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml index 9cf48d9e15a8..915e16412155 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml @@ -47,6 +47,10 @@ <!-- Height of the status bar --> <dimen name="status_bar_height">48dp</dimen> + <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) --> + <dimen name="quick_qs_offset_height">48dp</dimen> + <!-- Total height of QQS (quick_qs_offset_height + 128) --> + <dimen name="quick_qs_total_height">176dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml index 1ce41f07691d..b8e29da8c8e7 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml @@ -47,6 +47,10 @@ <!-- Height of the status bar --> <dimen name="status_bar_height">48dp</dimen> + <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) --> + <dimen name="quick_qs_offset_height">48dp</dimen> + <!-- Total height of QQS (quick_qs_offset_height + 128) --> + <dimen name="quick_qs_total_height">176dp</dimen> </resources> diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 52d0e08e4e7f..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,9 +24,8 @@ #include <utils/misc.h> #include <inttypes.h> -#include <android-base/macros.h> #include <androidfw/Asset.h> -#include <androidfw/AssetManager2.h> +#include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> #include <android-base/macros.h> @@ -1665,22 +1664,18 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr<Asset> asset; - { - ScopedLock<AssetManager2> locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); return id; } @@ -1757,25 +1752,22 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr<Asset> asset; - { - ScopedLock<AssetManager2> locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); + delete asset; return id; } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index dc5f5a270748..ba9883b4de59 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -43,17 +43,17 @@ import android.hardware.health.V2_0.IHealthInfoCallback; import android.hardware.health.V2_0.IHealth; import android.hardware.health.V2_0.Result; import android.os.BatteryManager; -import android.os.BatteryManagerProto; import android.os.BatteryManagerInternal; import android.os.BatteryProperty; import android.os.Binder; +import android.os.DropBoxManager; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBatteryPropertiesListener; import android.os.IBatteryPropertiesRegistrar; import android.os.IBinder; -import android.os.DropBoxManager; +import android.os.OsProtoEnums; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -127,7 +127,7 @@ public final class BatteryService extends SystemService { private static final String DUMPSYS_DATA_PATH = "/data/system/"; // This should probably be exposed in the API, though it's not critical - private static final int BATTERY_PLUGGED_NONE = 0; + private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0 private final Context mContext; private final IBatteryStats mBatteryStats; @@ -921,13 +921,13 @@ public final class BatteryService extends SystemService { synchronized (mLock) { proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped); - int batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_NONE; + int batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_NONE; if (mHealthInfo.chargerAcOnline) { - batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_AC; + batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_AC; } else if (mHealthInfo.chargerUsbOnline) { - batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_USB; + batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_USB; } else if (mHealthInfo.chargerWirelessOnline) { - batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_WIRELESS; + batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS; } proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue); proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent); diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java index 257845e365be..7604044583db 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -22,6 +22,9 @@ import android.app.AppOpsManager; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; import android.app.IUidObserver; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManagerInternal; +import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -42,6 +45,7 @@ import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -50,6 +54,7 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage; import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages; import java.io.PrintWriter; @@ -74,7 +79,7 @@ import java.util.List; */ public class ForceAppStandbyTracker { private static final String TAG = "ForceAppStandbyTracker"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; @GuardedBy("ForceAppStandbyTracker.class") private static ForceAppStandbyTracker sInstance; @@ -89,6 +94,8 @@ public class ForceAppStandbyTracker { AppOpsManager mAppOpsManager; IAppOpsService mAppOpsService; PowerManagerInternal mPowerManagerInternal; + StandbyTracker mStandbyTracker; + UsageStatsManagerInternal mUsageStatsManagerInternal; private final MyHandler mHandler; @@ -113,6 +120,12 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") private int[] mTempWhitelistedAppIds = mPowerWhitelistedAllAppIds; + /** + * Per-user packages that are in the EXEMPT bucket. + */ + @GuardedBy("mLock") + private final SparseSetArray<String> mExemptedPackages = new SparseSetArray<>(); + @GuardedBy("mLock") final ArraySet<Listener> mListeners = new ArraySet<>(); @@ -146,6 +159,28 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") boolean mForcedAppStandbyEnabled; + interface Stats { + int UID_STATE_CHANGED = 0; + int RUN_ANY_CHANGED = 1; + int ALL_UNWHITELISTED = 2; + int ALL_WHITELIST_CHANGED = 3; + int TEMP_WHITELIST_CHANGED = 4; + int EXEMPT_CHANGED = 5; + int FORCE_ALL_CHANGED = 6; + int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7; + } + + private final StatLogger mStatLogger = new StatLogger(new String[] { + "UID_STATE_CHANGED", + "RUN_ANY_CHANGED", + "ALL_UNWHITELISTED", + "ALL_WHITELIST_CHANGED", + "TEMP_WHITELIST_CHANGED", + "EXEMPT_CHANGED", + "FORCE_ALL_CHANGED", + "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED", + }); + @VisibleForTesting class FeatureFlagsObserver extends ContentObserver { FeatureFlagsObserver() { @@ -162,12 +197,11 @@ public class ForceAppStandbyTracker { } boolean isForcedAppStandbyEnabled() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; + return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1; } boolean isForcedAppStandbyForSmallBatteryEnabled() { - return Settings.Global.getInt(mContext.getContentResolver(), + return injectGetGlobalSettingInt( Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1; } @@ -258,6 +292,17 @@ public class ForceAppStandbyTracker { // only for affected app-ids. updateAllJobs(); + + // Note when an app is just put in the temp whitelist, we do *not* drain pending alarms. + } + + /** + * This is called when the EXEMPT bucket is updated. + */ + private void onExemptChanged(ForceAppStandbyTracker sender) { + // This doesn't happen very often, so just re-evaluate all jobs / alarms. + updateAllJobs(); + unblockAllUnrestrictedAlarms(); } /** @@ -346,11 +391,16 @@ public class ForceAppStandbyTracker { mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager()); mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService()); mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal()); + mUsageStatsManagerInternal = Preconditions.checkNotNull( + injectUsageStatsManagerInternal()); + mFlagsObserver = new FeatureFlagsObserver(); mFlagsObserver.register(); mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled(); mForceAllAppStandbyForSmallBattery = mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled(); + mStandbyTracker = new StandbyTracker(); + mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker); try { mIActivityManager.registerUidObserver(new UidObserver(), @@ -408,10 +458,20 @@ public class ForceAppStandbyTracker { } @VisibleForTesting + UsageStatsManagerInternal injectUsageStatsManagerInternal() { + return LocalServices.getService(UsageStatsManagerInternal.class); + } + + @VisibleForTesting boolean isSmallBatteryDevice() { return ActivityManager.isSmallBatteryDevice(); } + @VisibleForTesting + int injectGetGlobalSettingInt(String key, int def) { + return Settings.Global.getInt(mContext.getContentResolver(), key, def); + } + /** * Update {@link #mRunAnyRestrictedPackages} with the current app ops state. */ @@ -604,6 +664,30 @@ public class ForceAppStandbyTracker { } } + final class StandbyTracker extends AppIdleStateChangeListener { + @Override + public void onAppIdleStateChanged(String packageName, int userId, boolean idle, + int bucket) { + if (DEBUG) { + Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId + + (idle ? " idle" : " active") + " " + bucket); + } + final boolean changed; + if (bucket == UsageStatsManager.STANDBY_BUCKET_EXEMPTED) { + changed = mExemptedPackages.add(userId, packageName); + } else { + changed = mExemptedPackages.remove(userId, packageName); + } + if (changed) { + mHandler.notifyExemptChanged(); + } + } + + @Override + public void onParoleStateChanged(boolean isParoleOn) { + } + } + private Listener[] cloneListeners() { synchronized (mLock) { return mListeners.toArray(new Listener[mListeners.size()]); @@ -619,6 +703,7 @@ public class ForceAppStandbyTracker { private static final int MSG_FORCE_ALL_CHANGED = 6; private static final int MSG_USER_REMOVED = 7; private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; + private static final int MSG_EXEMPT_CHANGED = 9; public MyHandler(Looper looper) { super(looper); @@ -652,6 +737,10 @@ public class ForceAppStandbyTracker { obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); } + public void notifyExemptChanged() { + obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget(); + } + public void doUserRemoved(int userId) { obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget(); } @@ -672,37 +761,57 @@ public class ForceAppStandbyTracker { } final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this; + long start = mStatLogger.getTime(); switch (msg.what) { case MSG_UID_STATE_CHANGED: for (Listener l : cloneListeners()) { l.onUidForegroundStateChanged(sender, msg.arg1); } + mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start); return; + case MSG_RUN_ANY_CHANGED: for (Listener l : cloneListeners()) { l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj); } + mStatLogger.logDurationStat(Stats.RUN_ANY_CHANGED, start); return; + case MSG_ALL_UNWHITELISTED: for (Listener l : cloneListeners()) { l.onPowerSaveUnwhitelisted(sender); } + mStatLogger.logDurationStat(Stats.ALL_UNWHITELISTED, start); return; + case MSG_ALL_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onPowerSaveWhitelistedChanged(sender); } + mStatLogger.logDurationStat(Stats.ALL_WHITELIST_CHANGED, start); return; + case MSG_TEMP_WHITELIST_CHANGED: for (Listener l : cloneListeners()) { l.onTempPowerSaveWhitelistChanged(sender); } + mStatLogger.logDurationStat(Stats.TEMP_WHITELIST_CHANGED, start); + return; + + case MSG_EXEMPT_CHANGED: + for (Listener l : cloneListeners()) { + l.onExemptChanged(sender); + } + mStatLogger.logDurationStat(Stats.EXEMPT_CHANGED, start); return; + case MSG_FORCE_ALL_CHANGED: for (Listener l : cloneListeners()) { l.onForceAllAppsStandbyChanged(sender); } + mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start); return; + case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: // Feature flag for forced app standby changed. final boolean unblockAlarms; @@ -715,7 +824,10 @@ public class ForceAppStandbyTracker { l.unblockAllUnrestrictedAlarms(); } } + mStatLogger.logDurationStat( + Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start); return; + case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); return; @@ -742,6 +854,7 @@ public class ForceAppStandbyTracker { mForegroundUids.removeAt(i); } } + mExemptedPackages.remove(removedUserId); } } @@ -860,6 +973,10 @@ public class ForceAppStandbyTracker { if (exemptOnBatterySaver) { return false; } + final int userId = UserHandle.getUserId(uid); + if (mExemptedPackages.contains(userId, packageName)) { + return false; + } return mForceAllAppsStandby; } } @@ -966,6 +1083,23 @@ public class ForceAppStandbyTracker { pw.println(Arrays.toString(mTempWhitelistedAppIds)); pw.print(indent); + pw.println("Exempted packages:"); + for (int i = 0; i < mExemptedPackages.size(); i++) { + pw.print(indent); + pw.print(" User "); + pw.print(mExemptedPackages.keyAt(i)); + pw.println(); + + for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { + pw.print(indent); + pw.print(" "); + pw.print(mExemptedPackages.valueAt(i, j)); + pw.println(); + } + } + pw.println(); + + pw.print(indent); pw.println("Restricted packages:"); for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { pw.print(indent); @@ -975,6 +1109,8 @@ public class ForceAppStandbyTracker { pw.print(uidAndPackage.second); pw.println(); } + + mStatLogger.dump(pw, indent); } } @@ -987,7 +1123,7 @@ public class ForceAppStandbyTracker { isSmallBatteryDevice()); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, mForceAllAppStandbyForSmallBattery); - proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn); + proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { @@ -1004,6 +1140,18 @@ public class ForceAppStandbyTracker { proto.write(ForceAppStandbyTrackerProto.TEMP_POWER_SAVE_WHITELIST_APP_IDS, appId); } + for (int i = 0; i < mExemptedPackages.size(); i++) { + for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { + final long token2 = proto.start( + ForceAppStandbyTrackerProto.EXEMPTED_PACKAGES); + + proto.write(ExemptedPackage.USER_ID, mExemptedPackages.keyAt(i)); + proto.write(ExemptedPackage.PACKAGE_NAME, mExemptedPackages.valueAt(i, j)); + + proto.end(token2); + } + } + for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) { final long token2 = proto.start( ForceAppStandbyTrackerProto.RUN_ANY_IN_BACKGROUND_RESTRICTED_PACKAGES); @@ -1012,6 +1160,9 @@ public class ForceAppStandbyTracker { uidAndPackage.second); proto.end(token2); } + + mStatLogger.dumpProto(proto, ForceAppStandbyTrackerProto.STATS); + proto.end(token); } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index bd93b1795ae3..1dd92f3130a7 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1577,13 +1577,22 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * Returns all providers by name, including passive, but excluding - * fused, also including ones that are not permitted to - * be accessed by the calling activity or are currently disabled. + * Returns all providers by name, including passive and the ones that are not permitted to + * be accessed by the calling activity or are currently disabled, but excluding fused. */ @Override public List<String> getAllProviders() { - List<String> out = getProviders(null /*criteria*/, false /*enabledOnly*/); + ArrayList<String> out; + synchronized (mLock) { + out = new ArrayList<>(mProviders.size()); + for (LocationProviderInterface provider : mProviders) { + String name = provider.getName(); + if (LocationManager.FUSED_PROVIDER.equals(name)) { + continue; + } + out.add(name); + } + } if (D) Log.d(TAG, "getAllProviders()=" + out); return out; } @@ -2586,9 +2595,10 @@ public class LocationManagerService extends ILocationManager.Stub { // Check INTERACT_ACROSS_USERS permission if userId is not current user id. checkInteractAcrossUsersPermission(userId); - // Enable or disable all location providers + // Enable or disable all location providers. Fused provider and passive provider are + // excluded. synchronized (mLock) { - for(String provider : getAllProviders()) { + for(String provider : getAllProvidersForLocationSettings()) { setProviderEnabledForUser(provider, enabled, userId); } } @@ -2605,9 +2615,10 @@ public class LocationManagerService extends ILocationManager.Stub { // Check INTERACT_ACROSS_USERS permission if userId is not current user id. checkInteractAcrossUsersPermission(userId); - // If at least one location provider is enabled, return true + // If at least one location provider is enabled, return true. Fused provider and passive + // provider are excluded. synchronized (mLock) { - for (String provider : getAllProviders()) { + for (String provider : getAllProvidersForLocationSettings()) { if (isProviderEnabledForUser(provider, userId)) { return true; } @@ -2691,6 +2702,26 @@ public class LocationManagerService extends ILocationManager.Stub { } /** + * Return all location providers except fused provider and passive provider. These two + * providers are not generating location by themselves, but only echo locations from other + * providers. + * + * @return All location providers except fused provider and passive provider, including + * providers that are not permitted to be accessed by the calling activity or are + * currently disabled. + */ + private List<String> getAllProvidersForLocationSettings() { + List<String> providersForSettings = new ArrayList<>(mProviders.size()); + for (String provider : getAllProviders()) { + if (provider.equals(LocationManager.PASSIVE_PROVIDER)) { + continue; + } + providersForSettings.add(provider); + } + return providersForSettings; + } + + /** * Read location provider status from Settings.Secure * * @param provider the location provider to query diff --git a/services/core/java/com/android/server/StatLogger.java b/services/core/java/com/android/server/StatLogger.java new file mode 100644 index 000000000000..f2117314df00 --- /dev/null +++ b/services/core/java/com/android/server/StatLogger.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.os.SystemClock; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.StatLoggerProto.Event; + +import java.io.PrintWriter; + +/** + * Simple class to keep track of the number of times certain events happened and their durations for + * benchmarking. + * + * TODO Update shortcut service to switch to it. + * + * @hide + */ +public class StatLogger { + private final Object mLock = new Object(); + + private final int SIZE; + + @GuardedBy("mLock") + private final int[] mCountStats; + + @GuardedBy("mLock") + private final long[] mDurationStats; + + private final String[] mLabels; + + public StatLogger(String[] eventLabels) { + SIZE = eventLabels.length; + mCountStats = new int[SIZE]; + mDurationStats = new long[SIZE]; + mLabels = eventLabels; + } + + /** + * Return the current time in the internal time unit. + * Call it before an event happens, and + * give it back to the {@link #logDurationStat(int, long)}} after the event. + */ + public long getTime() { + return SystemClock.elapsedRealtimeNanos() / 1000; + } + + /** + * @see {@link #getTime()} + */ + public void logDurationStat(int eventId, long start) { + synchronized (mLock) { + mCountStats[eventId]++; + mDurationStats[eventId] += (getTime() - start); + } + } + + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.print(prefix); + pw.println("Stats:"); + for (int i = 0; i < SIZE; i++) { + pw.print(prefix); + pw.print(" "); + final int count = mCountStats[i]; + final double durationMs = mDurationStats[i] / 1000.0; + pw.println(String.format("%s: count=%d, total=%.1fms, avg=%.3fms", + mLabels[i], count, durationMs, + (count == 0 ? 0 : ((double) durationMs) / count))); + } + } + } + + public void dumpProto(ProtoOutputStream proto, long fieldId) { + synchronized (mLock) { + final long outer = proto.start(fieldId); + + for (int i = 0; i < mLabels.length; i++) { + final long inner = proto.start(StatLoggerProto.EVENTS); + + proto.write(Event.EVENT_ID, i); + proto.write(Event.LABEL, mLabels[i]); + proto.write(Event.COUNT, mCountStats[i]); + proto.write(Event.TOTAL_DURATION_MICROS, mDurationStats[i]); + + proto.end(inner); + } + + proto.end(outer); + } + } +} diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index c1cda985a9ba..48b5a582f74e 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -73,6 +73,17 @@ public class VibratorService extends IVibratorService.Stub private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 }; + private static final float GAMMA_SCALE_FACTOR_MINIMUM = 2.0f; + private static final float GAMMA_SCALE_FACTOR_LOW = 1.5f; + private static final float GAMMA_SCALE_FACTOR_HIGH = 0.5f; + private static final float GAMMA_SCALE_FACTOR_NONE = 1.0f; + + private static final int MAX_AMPLITUDE_MINIMUM_INTENSITY = 168; // 2/3 * 255 + private static final int MAX_AMPLITUDE_LOW_INTENSITY = 192; // 3/4 * 255 + + // If a vibration is playing for longer than 5s, it's probably not haptic feedback. + private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000; + private final LinkedList<VibrationInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; @@ -89,6 +100,8 @@ public class VibratorService extends IVibratorService.Stub private final IBatteryStats mBatteryStatsService; private PowerManagerInternal mPowerManagerInternal; private InputManager mIm; + private Vibrator mVibrator; + private SettingsObserver mSettingObserver; private volatile VibrateThread mThread; @@ -101,7 +114,8 @@ public class VibratorService extends IVibratorService.Stub private Vibration mCurrentVibration; private int mCurVibUid = -1; private boolean mLowPowerMode; - private SettingsObserver mSettingObserver; + private int mHapticFeedbackIntensity; + private int mNotificationIntensity; native static boolean vibratorExists(); native static void vibratorInit(); @@ -112,27 +126,33 @@ public class VibratorService extends IVibratorService.Stub native static long vibratorPerformEffect(long effect, long strength); private class Vibration implements IBinder.DeathRecipient { - private final IBinder mToken; - private final VibrationEffect mEffect; + public final IBinder token; // Start time in CLOCK_BOOTTIME base. - private final long mStartTime; + public final long startTime; // Start time in unix epoch time. Only to be used for debugging purposes and to correlate - // with other system events, any duration calculations should be done use mStartTime so as + // with other system events, any duration calculations should be done use startTime so as // not to be affected by discontinuities created by RTC adjustments. - private final long mStartTimeDebug; - private final int mUsageHint; - private final int mUid; - private final String mOpPkg; + public final long startTimeDebug; + public final int usageHint; + public final int uid; + public final String opPkg; + + // The actual effect to be played. + public VibrationEffect effect; + // The original effect that was requested. This is non-null only when the original effect + // differs from the effect that's being played. Typically these two things differ because + // the effect was scaled based on the users vibration intensity settings. + public VibrationEffect originalEffect; private Vibration(IBinder token, VibrationEffect effect, int usageHint, int uid, String opPkg) { - mToken = token; - mEffect = effect; - mStartTime = SystemClock.elapsedRealtime(); - mStartTimeDebug = System.currentTimeMillis(); - mUsageHint = usageHint; - mUid = uid; - mOpPkg = opPkg; + this.token = token; + this.effect = effect; + this.startTime = SystemClock.elapsedRealtime(); + this.startTimeDebug = System.currentTimeMillis(); + this.usageHint = usageHint; + this.uid = uid; + this.opPkg = opPkg; } public void binderDied() { @@ -143,43 +163,68 @@ public class VibratorService extends IVibratorService.Stub } } - public boolean hasLongerTimeout(long millis) { - // If the current effect is a one shot vibration that will end after the given timeout - // for the new one shot vibration, then just let the current vibration finish. All - // other effect types will get pre-empted. - if (mEffect instanceof VibrationEffect.OneShot) { - VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect; - return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis; - } - return false; + public boolean hasTimeoutLongerThan(long millis) { + final long duration = effect.getDuration(); + return duration >= 0 && duration > millis; } - public boolean isSystemHapticFeedback() { - boolean repeating = false; - if (mEffect instanceof VibrationEffect.Waveform) { - VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect; - repeating = (waveform.getRepeatIndex() < 0); + public boolean isHapticFeedback() { + if (effect instanceof VibrationEffect.Prebaked) { + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; + switch (prebaked.getId()) { + case VibrationEffect.EFFECT_CLICK: + case VibrationEffect.EFFECT_DOUBLE_CLICK: + case VibrationEffect.EFFECT_TICK: + return true; + default: + Slog.w(TAG, "Unknown prebaked vibration effect, " + + "assuming it isn't haptic feedback."); + return false; + } + } + final long duration = effect.getDuration(); + return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION; + } + + public boolean isNotification() { + switch (usageHint) { + case AudioAttributes.USAGE_NOTIFICATION: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + return true; + default: + return false; } - return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg)) - && !repeating; + } + + public boolean isRingtone() { + return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE; + } + + public boolean isFromSystem() { + return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg); } public VibrationInfo toInfo() { - return new VibrationInfo(mStartTimeDebug, mEffect, mUsageHint, mUid, mOpPkg); + return new VibrationInfo( + startTimeDebug, effect, originalEffect, usageHint, uid, opPkg); } } private static class VibrationInfo { private final long mStartTimeDebug; private final VibrationEffect mEffect; + private final VibrationEffect mOriginalEffect; private final int mUsageHint; private final int mUid; private final String mOpPkg; public VibrationInfo(long startTimeDebug, VibrationEffect effect, - int usageHint, int uid, String opPkg) { + VibrationEffect originalEffect, int usageHint, int uid, String opPkg) { mStartTimeDebug = startTimeDebug; mEffect = effect; + mOriginalEffect = originalEffect; mUsageHint = usageHint; mUid = uid; mOpPkg = opPkg; @@ -192,6 +237,8 @@ public class VibratorService extends IVibratorService.Stub .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug))) .append(", effect: ") .append(mEffect) + .append(", originalEffect: ") + .append(mOriginalEffect) .append(", usageHint: ") .append(mUsageHint) .append(", uid: ") @@ -245,6 +292,7 @@ public class VibratorService extends IVibratorService.Stub VibrationEffect tickEffect = createEffect(tickEffectTimings); mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect }; + } private static VibrationEffect createEffect(long[] timings) { @@ -259,6 +307,7 @@ public class VibratorService extends IVibratorService.Stub public void systemReady() { mIm = mContext.getSystemService(InputManager.class); + mVibrator = mContext.getSystemService(Vibrator.class); mSettingObserver = new SettingsObserver(mH); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -279,6 +328,14 @@ public class VibratorService extends IVibratorService.Stub Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true, mSettingObserver, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY), + true, mSettingObserver, UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY), + true, mSettingObserver, UserHandle.USER_ALL); + mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -380,11 +437,11 @@ public class VibratorService extends IVibratorService.Stub // then just let the current one finish. if (effect instanceof VibrationEffect.OneShot && mCurrentVibration != null - && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) { + && mCurrentVibration.effect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect; VibrationEffect.OneShot currentOneShot = - (VibrationEffect.OneShot) mCurrentVibration.mEffect; - if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming()) + (VibrationEffect.OneShot) mCurrentVibration.effect; + if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration()) && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) { if (DEBUG) { Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration"); @@ -398,7 +455,7 @@ public class VibratorService extends IVibratorService.Stub // to grab the attention of the user, like ringtones and alarms, in favor of one-shot // vibrations that are likely quite short. if (!isRepeatingVibration(effect) - && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) { + && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) { if (DEBUG) { Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration"); } @@ -421,13 +478,7 @@ public class VibratorService extends IVibratorService.Stub } private static boolean isRepeatingVibration(VibrationEffect effect) { - if (effect instanceof VibrationEffect.Waveform) { - final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; - if (waveform.getRepeatIndex() >= 0) { - return true; - } - } - return false; + return effect.getDuration() == Long.MAX_VALUE; } private void addToPreviousVibrationsLocked(Vibration vib) { @@ -444,7 +495,7 @@ public class VibratorService extends IVibratorService.Stub "cancelVibrate"); synchronized (mLock) { - if (mCurrentVibration != null && mCurrentVibration.mToken == token) { + if (mCurrentVibration != null && mCurrentVibration.token == token) { if (DEBUG) { Slog.d(TAG, "Canceling vibration."); } @@ -488,15 +539,16 @@ public class VibratorService extends IVibratorService.Stub } private void startVibrationLocked(final Vibration vib) { - if (!isAllowedToVibrate(vib)) { - if (DEBUG) { - Slog.e(TAG, "Vibrate ignored, low power mode"); - } + if (!isAllowedToVibrateLocked(vib)) { + return; + } + + final int intensity = getCurrentIntensityLocked(vib); + if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { return; } - if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE && - !shouldVibrateForRingtone()) { + if (vib.isRingtone() && !shouldVibrateForRingtone()) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } @@ -508,26 +560,27 @@ public class VibratorService extends IVibratorService.Stub if (mode == AppOpsManager.MODE_ERRORED) { // We might be getting calls from within system_server, so we don't actually want // to throw a SecurityException here. - Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid); + Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid); } return; } + applyVibrationIntensityScalingLocked(vib, intensity); startVibrationInnerLocked(vib); } private void startVibrationInnerLocked(Vibration vib) { mCurrentVibration = vib; - if (vib.mEffect instanceof VibrationEffect.OneShot) { - VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect; - doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint); - mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming()); - } else if (vib.mEffect instanceof VibrationEffect.Waveform) { + if (vib.effect instanceof VibrationEffect.OneShot) { + VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; + doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint); + mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration()); + } else if (vib.effect instanceof VibrationEffect.Waveform) { // mThread better be null here. doCancelVibrate should always be // called before startNextVibrationLocked or startVibrationLocked. - VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect; - mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint); + VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; + mThread = new VibrateThread(waveform, vib.uid, vib.usageHint); mThread.start(); - } else if (vib.mEffect instanceof VibrationEffect.Prebaked) { + } else if (vib.effect instanceof VibrationEffect.Prebaked) { long timeout = doVibratorPrebakedEffectLocked(vib); if (timeout > 0) { mH.postDelayed(mVibrationEndRunnable, timeout); @@ -537,28 +590,91 @@ public class VibratorService extends IVibratorService.Stub } } - private boolean isAllowedToVibrate(Vibration vib) { + private boolean isAllowedToVibrateLocked(Vibration vib) { if (!mLowPowerMode) { return true; } - if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { + + if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { return true; } - if (!mAllowPriorityVibrationsInLowPowerMode) { - return false; - } - if (vib.mUsageHint == AudioAttributes.USAGE_ALARM || - vib.mUsageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY || - vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { + if (vib.usageHint == AudioAttributes.USAGE_ALARM || + vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY || + vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { return true; } return false; } + private int getCurrentIntensityLocked(Vibration vib) { + if (vib.isNotification() || vib.isRingtone()){ + return mNotificationIntensity; + } else if (vib.isHapticFeedback()) { + return mHapticFeedbackIntensity; + } else { + return Vibrator.VIBRATION_INTENSITY_MEDIUM; + } + } + + /** + * Scale the vibration effect by the intensity as appropriate based its intent. + */ + private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) { + if (vib.effect instanceof VibrationEffect.Prebaked) { + // Prebaked effects are always just a direct translation from intensity to + // EffectStrength. + VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect; + prebaked.setEffectStrength(intensityToEffectStrength(intensity)); + return; + } + + final float gamma; + final int maxAmplitude; + if (vib.isNotification() || vib.isRingtone()) { + if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) { + gamma = GAMMA_SCALE_FACTOR_MINIMUM; + maxAmplitude = MAX_AMPLITUDE_MINIMUM_INTENSITY; + } else if (intensity == Vibrator.VIBRATION_INTENSITY_MEDIUM) { + gamma = GAMMA_SCALE_FACTOR_LOW; + maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY; + } else { + gamma = GAMMA_SCALE_FACTOR_NONE; + maxAmplitude = VibrationEffect.MAX_AMPLITUDE; + } + } else { + if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) { + gamma = GAMMA_SCALE_FACTOR_LOW; + maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY; + } else if (intensity == Vibrator.VIBRATION_INTENSITY_HIGH) { + gamma = GAMMA_SCALE_FACTOR_HIGH; + maxAmplitude = VibrationEffect.MAX_AMPLITUDE; + } else { + gamma = GAMMA_SCALE_FACTOR_NONE; + maxAmplitude = VibrationEffect.MAX_AMPLITUDE; + } + } + + VibrationEffect scaledEffect = null; + if (vib.effect instanceof VibrationEffect.OneShot) { + VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; + scaledEffect = oneShot.scale(gamma, maxAmplitude); + } else if (vib.effect instanceof VibrationEffect.Waveform) { + VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; + scaledEffect = waveform.scale(gamma, maxAmplitude); + } else { + Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type"); + } + + if (scaledEffect != null) { + vib.originalEffect = vib.effect; + vib.effect = scaledEffect; + } + } + private boolean shouldVibrateForRingtone() { - AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + AudioManager audioManager = mContext.getSystemService(AudioManager.class); int ringerMode = audioManager.getRingerModeInternal(); // "Also vibrate for calls" Setting in Sound if (Settings.System.getInt( @@ -573,10 +689,10 @@ public class VibratorService extends IVibratorService.Stub int mode; try { mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, - vib.mUsageHint, vib.mUid, vib.mOpPkg); + vib.usageHint, vib.uid, vib.opPkg); if (mode == AppOpsManager.MODE_ALLOWED) { mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService), - AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg); + AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg); } } catch (RemoteException e) { Slog.e(TAG, "Failed to get appop mode for vibration!", e); @@ -589,8 +705,8 @@ public class VibratorService extends IVibratorService.Stub if (mCurrentVibration != null) { try { mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService), - AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid, - mCurrentVibration.mOpPkg); + AppOpsManager.OP_VIBRATE, mCurrentVibration.uid, + mCurrentVibration.opPkg); } catch (RemoteException e) { } unlinkVibration(mCurrentVibration); mCurrentVibration = null; @@ -600,9 +716,9 @@ public class VibratorService extends IVibratorService.Stub private void linkVibration(Vibration vib) { // Only link against waveforms since they potentially don't have a finish if // they're repeating. Let other effects just play out until they're done. - if (vib.mEffect instanceof VibrationEffect.Waveform) { + if (vib.effect instanceof VibrationEffect.Waveform) { try { - vib.mToken.linkToDeath(vib, 0); + vib.token.linkToDeath(vib, 0); } catch (RemoteException e) { return; } @@ -610,8 +726,8 @@ public class VibratorService extends IVibratorService.Stub } private void unlinkVibration(Vibration vib) { - if (vib.mEffect instanceof VibrationEffect.Waveform) { - vib.mToken.unlinkToDeath(vib, 0); + if (vib.effect instanceof VibrationEffect.Waveform) { + vib.token.unlinkToDeath(vib, 0); } } @@ -619,6 +735,7 @@ public class VibratorService extends IVibratorService.Stub synchronized (mLock) { boolean devicesUpdated = updateInputDeviceVibratorsLocked(); boolean lowPowerModeUpdated = updateLowPowerModeLocked(); + updateVibrationIntensityLocked(); if (devicesUpdated || lowPowerModeUpdated) { // If the state changes out from under us then just reset. @@ -678,6 +795,15 @@ public class VibratorService extends IVibratorService.Stub return false; } + private void updateVibrationIntensityLocked() { + mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.HAPTIC_FEEDBACK_INTENSITY, + mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT); + mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT); + } + @Override public void onInputDeviceAdded(int deviceId) { updateVibrators(); @@ -755,28 +881,32 @@ public class VibratorService extends IVibratorService.Stub } private long doVibratorPrebakedEffectLocked(Vibration vib) { + final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; + final boolean usingInputDeviceVibrators; synchronized (mInputDeviceVibrators) { - VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect; - // Input devices don't support prebaked effect, so skip trying it with them. - if (mInputDeviceVibrators.isEmpty()) { - long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM); - if (timeout > 0) { - noteVibratorOnLocked(vib.mUid, timeout); - return timeout; - } - } - if (!prebaked.shouldFallback()) { - return 0; - } - VibrationEffect effect = getFallbackEffect(prebaked.getId()); - if (effect == null) { - Slog.w(TAG, "Failed to play prebaked effect, no fallback"); - return 0; + usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty(); + } + // Input devices don't support prebaked effect, so skip trying it with them. + if (!usingInputDeviceVibrators) { + long timeout = vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength()); + if (timeout > 0) { + noteVibratorOnLocked(vib.uid, timeout); + return timeout; } - Vibration fallbackVib = - new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg); - startVibrationInnerLocked(fallbackVib); } + if (!prebaked.shouldFallback()) { + return 0; + } + VibrationEffect effect = getFallbackEffect(prebaked.getId()); + if (effect == null) { + Slog.w(TAG, "Failed to play prebaked effect, no fallback"); + return 0; + } + Vibration fallbackVib = + new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg); + final int intensity = getCurrentIntensityLocked(fallbackVib); + applyVibrationIntensityScalingLocked(fallbackVib, intensity); + startVibrationInnerLocked(fallbackVib); return 0; } @@ -787,6 +917,25 @@ public class VibratorService extends IVibratorService.Stub return mFallbackEffects[effectId]; } + /** + * Return the current desired effect strength. + * + * If the returned value is < 0 then the vibration shouldn't be played at all. + */ + private static int intensityToEffectStrength(int intensity) { + switch (intensity) { + case Vibrator.VIBRATION_INTENSITY_LOW: + return EffectStrength.LIGHT; + case Vibrator.VIBRATION_INTENSITY_MEDIUM: + return EffectStrength.MEDIUM; + case Vibrator.VIBRATION_INTENSITY_HIGH: + return EffectStrength.STRONG; + default: + Slog.w(TAG, "Got unexpected vibration intensity: " + intensity); + return EffectStrength.STRONG; + } + } + private void noteVibratorOnLocked(int uid, long millis) { try { mBatteryStatsService.noteVibratorOn(uid, millis); @@ -945,7 +1094,8 @@ public class VibratorService extends IVibratorService.Stub // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mCurrentVibration != null - && !mCurrentVibration.isSystemHapticFeedback()) { + && !(mCurrentVibration.isHapticFeedback() + && mCurrentVibration.isFromSystem())) { doCancelVibrateLocked(); } } @@ -957,10 +1107,21 @@ public class VibratorService extends IVibratorService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - pw.println("Previous vibrations:"); + pw.println("Vibrator Service:"); synchronized (mLock) { + pw.print(" mCurrentVibration="); + if (mCurrentVibration != null) { + pw.println(mCurrentVibration.toInfo().toString()); + } else { + pw.println("null"); + } + pw.println(" mLowPowerMode=" + mLowPowerMode); + pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity); + pw.println(" mNotificationIntensity=" + mNotificationIntensity); + pw.println(""); + pw.println(" Previous vibrations:"); for (VibrationInfo info : mPreviousVibrations) { - pw.print(" "); + pw.print(" "); pw.println(info.toString()); } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 30432df418ed..5215b6fbfd3d 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -87,6 +87,7 @@ public class Watchdog extends Thread { "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "com.android.bluetooth", // Bluetooth service + "statsd", // Stats daemon }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 266abf8c3f4c..2f7d4c1ec634 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1042,14 +1042,20 @@ public final class ActiveServices { throw new SecurityException("Instant app " + r.appInfo.packageName + " does not have permission to create foreground services"); default: - mAm.enforcePermission( - android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, - r.app.pid, r.appInfo.uid, "startForeground"); - } - } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) { - mAm.enforcePermission( - android.Manifest.permission.FOREGROUND_SERVICE, - r.app.pid, r.appInfo.uid, "startForeground"); + try { + if (AppGlobals.getPackageManager().checkPermission( + android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, + r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid)) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Instant app " + r.appInfo.packageName + + " does not have permission to create foreground" + + "services"); + } + } catch (RemoteException e) { + throw new SecurityException("Failed to check instant app permission." , + e); + } + } } if (r.fgRequired) { if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) { diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 427ccba94fd6..220014fff4ab 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -50,7 +51,9 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wm.ConfigurationContainer; +import com.android.server.wm.DisplayWindowController; +import com.android.server.wm.WindowContainerListener; import java.io.PrintWriter; import java.util.ArrayList; @@ -58,7 +61,8 @@ import java.util.ArrayList; * Exactly one of these classes per Display in the system. Capable of holding zero or more * attached {@link ActivityStack}s. */ -class ActivityDisplay extends ConfigurationContainer<ActivityStack> { +class ActivityDisplay extends ConfigurationContainer<ActivityStack> + implements WindowContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM; private static final String TAG_STACK = TAG + POSTFIX_STACK; @@ -100,6 +104,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { // Used in updating the display size private Point mTmpDisplaySize = new Point(); + private DisplayWindowController mWindowContainerController; + ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { mSupervisor = supervisor; mDisplayId = displayId; @@ -108,10 +114,15 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { throw new IllegalStateException("Display does not exist displayId=" + displayId); } mDisplay = display; + mWindowContainerController = createWindowContainerController(); updateBounds(); } + protected DisplayWindowController createWindowContainerController() { + return new DisplayWindowController(mDisplayId, this); + } + void updateBounds() { mDisplay.getSize(mTmpDisplaySize); setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y); @@ -148,7 +159,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { private void positionChildAt(ActivityStack stack, int position) { mStacks.remove(stack); - mStacks.add(getTopInsertPosition(stack, position), stack); + final int insertPosition = getTopInsertPosition(stack, position); + mStacks.add(insertPosition, stack); + mWindowContainerController.positionChildAt(stack.getWindowContainerController(), + insertPosition); } private int getTopInsertPosition(ActivityStack stack, int candidatePosition) { @@ -661,6 +675,64 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { && (mSupervisor.mService.mRunningVoice == null); } + /** + * @return the stack currently above the home stack. Can be null if there is no home stack, or + * the home stack is already on top. + */ + ActivityStack getStackAboveHome() { + if (mHomeStack == null) { + // Skip if there is no home stack + return null; + } + + final int stackIndex = mStacks.indexOf(mHomeStack) + 1; + return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null; + } + + /** + * Adjusts the home stack behind the last visible stack in the display if necessary. Generally + * used in conjunction with {@link #moveHomeStackBehindStack}. + */ + void moveHomeStackBehindBottomMostVisibleStack() { + if (mHomeStack == null) { + // Skip if there is no home stack + return; + } + + // Move the home stack to the bottom to not affect the following visibility checks + positionChildAtBottom(mHomeStack); + + // Find the next position where the homes stack should be placed + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack == mHomeStack) { + continue; + } + final int winMode = stack.getWindowingMode(); + final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN || + winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + if (stack.shouldBeVisible(null) && isValidWindowingMode) { + // Move the home stack to behind this stack + positionChildAt(mHomeStack, Math.max(0, stackNdx - 1)); + break; + } + } + } + + /** + * Moves the home stack behind the given {@param stack} if possible. If {@param stack} is not + * currently in the display, then then the home stack is moved to the back. Generally used in + * conjunction with {@link #moveHomeStackBehindBottomMostVisibleStack}. + */ + void moveHomeStackBehindStack(ActivityStack behindStack) { + if (behindStack == null) { + return; + } + + positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1)); + } + boolean isSleeping() { return mSleeping; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 168baf0fe5e2..f658b276d3ab 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -39,6 +39,7 @@ import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE; import static android.app.ActivityThread.PROC_START_SEQ_IDENT; import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; import static android.app.AppOpsManager.OP_NONE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -378,6 +379,7 @@ import android.util.Xml; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import android.view.Gravity; +import android.view.IRecentsAnimationRunner; import android.view.LayoutInflater; import android.view.RemoteAnimationDefinition; import android.view.View; @@ -451,6 +453,7 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.PinnedStackWindowController; +import com.android.server.wm.RecentsAnimationController; import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; @@ -5105,23 +5108,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle options, - Bundle activityOptions, int userId) { - if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) { - String msg = "Permission Denial: startRecentsActivity() from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " not recent tasks package"; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); - final int recentsUid = mRecentTasks.getRecentsComponentUid(); - final ComponentName recentsComponent = mRecentTasks.getRecentsComponent(); - final String recentsPackage = recentsComponent.getPackageName(); + public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver, + IRecentsAnimationRunner recentsAnimationRunner) { + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { + final int recentsUid = mRecentTasks.getRecentsComponentUid(); + final ComponentName recentsComponent = mRecentTasks.getRecentsComponent(); + final String recentsPackage = recentsComponent.getPackageName(); + // If provided, kick off the request for the assist data in the background before // starting the activity if (assistDataReceiver != null) { @@ -5138,17 +5134,24 @@ public class ActivityManagerService extends IActivityManager.Stub recentsUid, recentsPackage); } - final Intent intent = new Intent(); - intent.setFlags(FLAG_ACTIVITY_NEW_TASK); - intent.setComponent(recentsComponent); - intent.putExtras(options); + // Start a new recents animation + final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor, + mActivityStartController, mWindowManager, mUserController); + anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent, + recentsUid); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } - return mActivityStartController.obtainStarter(intent, "startRecentsActivity") - .setCallingUid(recentsUid) - .setCallingPackage(recentsPackage) - .setActivityOptions(safeOptions) - .setMayWait(userId) - .execute(); + @Override + public void cancelRecentsAnimation() { + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()"); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (this) { + mWindowManager.cancelRecentsAnimation(); } } finally { Binder.restoreCallingIdentity(origId); @@ -8914,20 +8917,6 @@ public class ActivityManagerService extends IActivityManager.Stub /** * This can be called with or without the global lock held. */ - void enforcePermission(String permission, int pid, int uid, String func) { - if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) { - return; - } - - String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid - + " requires " + permission; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - /** - * This can be called with or without the global lock held. - */ void enforceCallerIsRecentsOrHasPermission(String permission, String func) { if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) { enforceCallingPermission(permission, func); @@ -25623,11 +25612,14 @@ public class ActivityManagerService extends IActivityManager.Stub // "= 0" is needed because otherwise catch(RemoteException) would make it look like // packageUid may not be initialized. int packageUid = 0; + final long ident = Binder.clearCallingIdentity(); try { packageUid = AppGlobals.getPackageManager().getPackageUid( packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); } catch (RemoteException e) { // Shouldn't happen. + } finally { + Binder.restoreCallingIdentity(ident); } synchronized (ActivityManagerService.this) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9d06b0dbab64..ec8cf91a44cf 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -481,7 +481,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai @Override public void setWindowingMode(int windowingMode) { - setWindowingMode(windowingMode, false /* animate */, true /* showRecents */, + setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */); } @@ -642,7 +642,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: We should probably resolve the windowing mode for the stack on the new display here // so that it end up in a compatible mode in the new display. e.g. split-screen secondary. removeFromDisplay(); + // Reparent the window container before we try to update the position when adding it to + // the new display below mTmpRect2.setEmpty(); + mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); mStackSupervisor.resumeFocusedStackTopActivityLocked(); @@ -650,7 +653,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // windows that are no longer visible. mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); - mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop); } /** @@ -994,12 +996,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai insertTaskAtTop(task, null); return; } - - task = topTask(); - if (task != null) { - mWindowContainerController.positionChildAtTop(task.getWindowContainerController(), - true /* includingParents */); - } } /** @@ -1024,12 +1020,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (task != null) { insertTaskAtBottom(task); return; - } else { - task = bottomTask(); - if (task != null) { - mWindowContainerController.positionChildAtBottom( - task.getWindowContainerController(), true /* includingParents */); - } } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index bfb563fd93a8..510a3fa47ec5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -297,6 +297,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private RunningTasks mRunningTasks; final ActivityStackSupervisorHandler mHandler; + final Looper mLooper; /** Short cut */ WindowManagerService mWindowManager; @@ -581,6 +582,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D public ActivityStackSupervisor(ActivityManagerService service, Looper looper) { mService = service; + mLooper = looper; mHandler = new ActivityStackSupervisorHandler(looper); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 4dc30ddf4b5b..8fd754af1a0f 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -302,6 +302,7 @@ class ActivityStarter { SafeActivityOptions activityOptions; boolean ignoreTargetSecurity; boolean componentSpecified; + boolean avoidMoveToFront; ActivityRecord[] outActivity; TaskRecord inTask; String reason; @@ -356,6 +357,7 @@ class ActivityStarter { userId = 0; waitResult = null; mayWait = false; + avoidMoveToFront = false; } /** @@ -390,6 +392,7 @@ class ActivityStarter { userId = request.userId; waitResult = request.waitResult; mayWait = request.mayWait; + avoidMoveToFront = request.avoidMoveToFront; } } @@ -1485,19 +1488,23 @@ class ActivityStarter { mDoResume = false; } - if (mOptions != null && mOptions.getLaunchTaskId() != -1 - && mOptions.getTaskOverlay()) { - r.mTaskOverlay = true; - if (!mOptions.canTaskOverlayResume()) { - final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId()); - final ActivityRecord top = task != null ? task.getTopActivity() : null; - if (top != null && top.state != RESUMED) { - - // The caller specifies that we'd like to be avoided to be moved to the front, - // so be it! - mDoResume = false; - mAvoidMoveToFront = true; + if (mOptions != null) { + if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) { + r.mTaskOverlay = true; + if (!mOptions.canTaskOverlayResume()) { + final TaskRecord task = mSupervisor.anyTaskForIdLocked( + mOptions.getLaunchTaskId()); + final ActivityRecord top = task != null ? task.getTopActivity() : null; + if (top != null && top.state != RESUMED) { + + // The caller specifies that we'd like to be avoided to be moved to the + // front, so be it! + mDoResume = false; + mAvoidMoveToFront = true; + } } + } else if (mOptions.getAvoidMoveToFront()) { + mAvoidMoveToFront = true; } } @@ -1838,7 +1845,7 @@ class ActivityStarter { // Need to update mTargetStack because if task was moved out of it, the original stack may // be destroyed. mTargetStack = intentActivity.getStack(); - if (!mMovedToFront && mDoResume) { + if (!mAvoidMoveToFront && !mMovedToFront && mDoResume) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack + " from " + intentActivity); mTargetStack.moveToFront("intentActivityFound"); diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java new file mode 100644 index 000000000000..fe576fdaacbe --- /dev/null +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_NONE; +import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; + +import android.app.ActivityOptions; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Handler; +import android.view.IRecentsAnimationRunner; +import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; +import com.android.server.wm.WindowManagerService; + +/** + * Manages the recents animation, including the reordering of the stacks for the transition and + * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. + */ +class RecentsAnimation implements RecentsAnimationCallbacks { + private static final String TAG = RecentsAnimation.class.getSimpleName(); + + private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000; + + private final ActivityManagerService mService; + private final ActivityStackSupervisor mStackSupervisor; + private final ActivityStartController mActivityStartController; + private final WindowManagerService mWindowManager; + private final UserController mUserController; + private final Handler mHandler; + + private final Runnable mCancelAnimationRunnable; + + // The stack to restore the home stack behind when the animation is finished + private ActivityStack mRestoreHomeBehindStack; + + RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor, + ActivityStartController activityStartController, WindowManagerService wm, + UserController userController) { + mService = am; + mStackSupervisor = stackSupervisor; + mActivityStartController = activityStartController; + mHandler = new Handler(mStackSupervisor.mLooper); + mWindowManager = wm; + mUserController = userController; + mCancelAnimationRunnable = () -> { + // The caller has not finished the animation in a predefined amount of time, so + // force-cancel the animation + mWindowManager.cancelRecentsAnimation(); + }; + } + + void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner, + ComponentName recentsComponent, int recentsUid) { + + // Cancel the previous recents animation if necessary + mWindowManager.cancelRecentsAnimation(); + + final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null; + if (!hasExistingHomeActivity) { + // No home activity + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setLaunchActivityType(ACTIVITY_TYPE_HOME); + opts.setAvoidMoveToFront(); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); + + mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity") + .setCallingUid(recentsUid) + .setCallingPackage(recentsComponent.getPackageName()) + .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle())) + .setMayWait(mUserController.getCurrentUserId()) + .execute(); + mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + + // TODO: Maybe wait for app to draw in this particular case? + } + + final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); + final ActivityDisplay display = homeActivity.getDisplay(); + + // Save the initial position of the home activity stack to be restored to after the + // animation completes + mRestoreHomeBehindStack = hasExistingHomeActivity + ? display.getStackAboveHome() + : null; + + // Move the home activity into place for the animation + display.moveHomeStackBehindBottomMostVisibleStack(); + + // Mark the home activity as launch-behind to bump its visibility for the + // duration of the gesture that is driven by the recents component + homeActivity.mLaunchTaskBehind = true; + + // Fetch all the surface controls and pass them to the client to get the animation + // started + mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId); + + // If we updated the launch-behind state, update the visibility of the activities after we + // fetch the visible tasks to be controlled by the animation + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + + // Post a timeout for the animation + mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT); + } + + @Override + public void onAnimationFinished(boolean moveHomeToTop) { + mHandler.removeCallbacks(mCancelAnimationRunnable); + synchronized (mService) { + if (mWindowManager.getRecentsAnimationController() == null) return; + + mWindowManager.inSurfaceTransaction(() -> { + mWindowManager.cleanupRecentsAnimation(); + + // Move the home stack to the front + final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); + if (homeActivity == null) { + return; + } + + // Restore the launched-behind state + homeActivity.mLaunchTaskBehind = false; + + if (moveHomeToTop) { + // Bring the home stack to the front + final ActivityStack homeStack = homeActivity.getStack(); + homeStack.mNoAnimActivities.add(homeActivity); + homeStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + } else { + // Restore the home stack to its previous position + final ActivityDisplay display = homeActivity.getDisplay(); + display.moveHomeStackBehindStack(mRestoreHomeBehindStack); + } + + mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); + }); + } + } +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index bedf043147de..edeee3e590ea 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -133,6 +133,7 @@ import com.android.server.pm.UserManagerService; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -4767,6 +4768,16 @@ public class AudioService extends IAudioService.Stub Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode); } + private String getSoundEffectFilePath(int effectType) { + String filePath = Environment.getProductDirectory() + SOUND_EFFECTS_PATH + + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]); + if (!new File(filePath).isFile()) { + filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]); + } + return filePath; + } + private boolean onLoadSoundEffects() { int status; @@ -4836,9 +4847,7 @@ public class AudioService extends IAudioService.Stub continue; } if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { - String filePath = Environment.getRootDirectory() - + SOUND_EFFECTS_PATH - + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]); + String filePath = getSoundEffectFilePath(effect); int sampleId = mSoundPool.load(filePath, 0); if (sampleId <= 0) { Log.w(TAG, "Soundpool could not load file: "+filePath); @@ -4944,8 +4953,7 @@ public class AudioService extends IAudioService.Stub } else { MediaPlayer mediaPlayer = new MediaPlayer(); try { - String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + - SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]); + String filePath = getSoundEffectFilePath(effectType); mediaPlayer.setDataSource(filePath); mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); mediaPlayer.prepare(); diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java index 4e7490858fc8..08607bc8ef2c 100644 --- a/services/core/java/com/android/server/job/JobSchedulerInternal.java +++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java @@ -16,6 +16,7 @@ package com.android.server.job; +import android.annotation.UserIdInt; import android.app.job.JobInfo; import java.util.List; @@ -39,6 +40,14 @@ public interface JobSchedulerInternal { long nextHeartbeatForBucket(int bucket); /** + * Heartbeat ordinal for the given app. This is typically the heartbeat at which + * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run + * jobs in a long time is immediately runnable even if the app is bucketed into + * an infrequent time allocation. + */ + public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, int appBucket); + + /** * Returns a list of pending jobs scheduled by the system service. */ List<JobInfo> getSystemScheduledPendingJobs(); @@ -55,6 +64,14 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); + /** + * The user has started interacting with the app. Take any appropriate action. + */ + void reportAppUsage(String packageName, int userId); + + /** + * Report a snapshot of sync-related jobs back to the sync manager + */ JobStorePersistStats getPersistStats(); /** diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 5da470e68875..0a21f12c1f87 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -19,6 +19,7 @@ package com.android.server.job; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; +import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -39,6 +40,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.Intent.UriFlags; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -1069,6 +1071,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + void reportAppUsage(String packageName, int userId) { + // This app just transitioned into interactive use or near equivalent, so we should + // take a look at its job state for feedback purposes. + } + /** * Initializes the system service. * <p> @@ -2065,6 +2072,33 @@ public final class JobSchedulerService extends com.android.server.SystemService } /** + * Heartbeat ordinal for the given app. This is typically the heartbeat at which + * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run + * jobs in a long time is immediately runnable even if the app is bucketed into + * an infrequent time allocation. + */ + public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, + final int appStandbyBucket) { + if (appStandbyBucket == 0 || + appStandbyBucket >= mConstants.STANDBY_BEATS.length) { + // ACTIVE => everything can be run right away + // NEVER => we won't run them anyway, so let them go in the future + // as soon as the app enters normal use + return 0; + } + + final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun( + packageName, userId); + final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket]; + final long bucketsAgo = timeSinceLastJob / bucketLength; + + // If we haven't run any jobs for more than the app's current bucket period, just + // consider anything new to be immediately runnable. Otherwise, base it on the + // bucket at which we last ran jobs. + return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo); + } + + /** * Returns a list of all pending jobs. A running job is not considered pending. Periodic * jobs are always considered pending. */ @@ -2122,6 +2156,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } @Override + public void reportAppUsage(String packageName, int userId) { + JobSchedulerService.this.reportAppUsage(packageName, userId); + } + + @Override public JobStorePersistStats getPersistStats() { synchronized (mLock) { return new JobStorePersistStats(mJobs.getPersistStats()); @@ -2139,10 +2178,14 @@ public final class JobSchedulerService extends com.android.server.SystemService mUsageStats = usageStats; } + public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) { + return mUsageStats.getTimeSinceLastJobRun(packageName, userId); + } + // AppIdleStateChangeListener interface for live updates @Override - public void onAppIdleStateChanged(final String packageName, final int userId, + public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, boolean idle, int bucket) { final int uid = mLocalPM.getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); @@ -2179,6 +2222,40 @@ public final class JobSchedulerService extends com.android.server.SystemService } mInParole = isParoleOn; } + + @Override + public void onUserInteractionStarted(String packageName, int userId) { + final int uid = mLocalPM.getPackageUid(packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); + if (uid < 0) { + // Quietly ignore; the case is already logged elsewhere + return; + } + + final long sinceLast = sElapsedRealtimeClock.millis() - + mUsageStats.getTimeSinceLastJobRun(packageName, userId); + final DeferredJobCounter counter = new DeferredJobCounter(); + synchronized (mLock) { + mJobs.forEachJobForSourceUid(uid, counter); + } + + mUsageStats.reportAppJobState(packageName, userId, counter.numDeferred(), sinceLast); + } + } + + static class DeferredJobCounter implements JobStatusFunctor { + private int mDeferred = 0; + + public int numDeferred() { + return mDeferred; + } + + @Override + public void process(JobStatus job) { + if (job.getWhenStandbyDeferred() > 0) { + mDeferred++; + } + } } public static int standbyBucketToBucketIndex(int bucket) { diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index f23147b445c2..37b39907e976 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -24,6 +24,7 @@ import android.app.job.IJobService; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobWorkItem; +import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.server.EventLogTags; +import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; /** @@ -238,6 +240,11 @@ public final class JobServiceContext implements ServiceConnection { } } + UsageStatsManagerInternal usageStats = + LocalServices.getService(UsageStatsManagerInternal.class); + usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(), + mExecutionStartTimeElapsed); + // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. job.clearPersistedUtcTimes(); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index d9a5ff675475..08ff7bdb0eb8 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -414,8 +414,9 @@ public final class JobStatus { int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, sourceUserId, elapsedNow); JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); - long currentHeartbeat = js != null ? js.currentHeartbeat() : 0; - + long currentHeartbeat = js != null + ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket) + : 0; return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId, standbyBucket, currentHeartbeat, tag, 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4c9da8949cc9..5e3d77874e9b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3421,7 +3421,7 @@ public class NotificationManagerService extends SystemService { long zenLog = proto.start(NotificationServiceDumpProto.ZEN); mZenModeHelper.dump(proto); for (ComponentName suppressor : mEffectsSuppressors) { - proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString()); + suppressor.writeToProto(proto, ZenModeProto.SUPPRESSORS); } proto.end(zenLog); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 932e4f948a82..2859613c0047 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -553,16 +553,15 @@ public class ZenModeHelper { } void dump(ProtoOutputStream proto) { - proto.write(ZenModeProto.ZEN_MODE, mZenMode); synchronized (mConfig) { if (mConfig.manualRule != null) { - proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, mConfig.manualRule.toString()); + mConfig.manualRule.writeToProto(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS); } for (ZenRule rule : mConfig.automaticRules.values()) { if (rule.enabled && rule.condition.state == Condition.STATE_TRUE && !rule.snoozing) { - proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString()); + rule.writeToProto(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS); } } mConfig.toNotificationPolicy().writeToProto(proto, ZenModeProto.POLICY); diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 5bf38dc21305..10e05cf34955 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -362,9 +362,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub { continue; } - // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into - // /data/ota and moved into the dalvik-cache already. - if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) { + // If the path is in /system, /vendor or /product, ignore. It will have been + // ota-dexopted into /data/ota and moved into the dalvik-cache already. + if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor") + || pkg.codePath.startsWith("/product")) { continue; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 837a118384be..776794593512 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -466,6 +466,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int SCAN_AS_PRIVILEGED = 1<<18; static final int SCAN_AS_OEM = 1<<19; static final int SCAN_AS_VENDOR = 1<<20; + static final int SCAN_AS_PRODUCT = 1<<21; @IntDef(flag = true, prefix = { "SCAN_" }, value = { SCAN_NO_DEX, @@ -570,6 +571,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; + private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; + private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; /** Canonical intent used to identify what counts as a "web browser" app */ @@ -2552,7 +2555,7 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } - // Collect vendor overlay packages. (Do this before scanning any apps.) + // Collect vendor/product overlay packages. (Do this before scanning any apps.) // For security and version matching reason, only consider // overlay packages if they reside in the right directory. scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), @@ -2562,6 +2565,13 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_AS_SYSTEM | SCAN_AS_VENDOR, 0); + scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR), + mDefParseFlags + | PackageParser.PARSE_IS_SYSTEM_DIR, + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_PRODUCT, + 0); mParallelPackageParserCallback.findStaticOverlayPackages(); @@ -2595,8 +2605,7 @@ public class PackageManagerService extends IPackageManager.Stub 0); // Collected privileged vendor packages. - File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), - "priv-app"); + File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); try { privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile(); } catch (IOException e) { @@ -2636,6 +2645,37 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_AS_OEM, 0); + // Collected privileged product packages. + File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); + try { + privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile(); + } catch (IOException e) { + // failed to look up canonical path, continue with original one + } + scanDirTracedLI(privilegedProductAppDir, + mDefParseFlags + | PackageParser.PARSE_IS_SYSTEM_DIR, + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_PRODUCT + | SCAN_AS_PRIVILEGED, + 0); + + // Collect ordinary product packages. + File productAppDir = new File(Environment.getProductDirectory(), "app"); + try { + productAppDir = productAppDir.getCanonicalFile(); + } catch (IOException e) { + // failed to look up canonical path, continue with original one + } + scanDirTracedLI(productAppDir, + mDefParseFlags + | PackageParser.PARSE_IS_SYSTEM_DIR, + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_PRODUCT, + 0); + // Prune any system packages that no longer exist. final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>(); // Stub packages must either be replaced with full versions in the /data @@ -2842,6 +2882,23 @@ Slog.e("TODD", scanFlags | SCAN_AS_SYSTEM | SCAN_AS_OEM; + } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) { + reparseFlags = + mDefParseFlags | + PackageParser.PARSE_IS_SYSTEM_DIR; + rescanFlags = + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_PRODUCT + | SCAN_AS_PRIVILEGED; + } else if (FileUtils.contains(productAppDir, scanFile)) { + reparseFlags = + mDefParseFlags | + PackageParser.PARSE_IS_SYSTEM_DIR; + rescanFlags = + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_PRODUCT; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; @@ -9862,6 +9919,7 @@ Slog.e("TODD", * <li>{@link #SCAN_AS_PRIVILEGED}</li> * <li>{@link #SCAN_AS_OEM}</li> * <li>{@link #SCAN_AS_VENDOR}</li> + * <li>{@link #SCAN_AS_PRODUCT}</li> * <li>{@link #SCAN_AS_INSTANT_APP}</li> * <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li> * </ul> @@ -9884,6 +9942,10 @@ Slog.e("TODD", & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) { scanFlags |= SCAN_AS_VENDOR; } + if ((disabledPkgSetting.pkgPrivateFlags + & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) { + scanFlags |= SCAN_AS_PRODUCT; + } } if (pkgSetting != null) { final int userId = ((user == null) ? 0 : user.getIdentifier()); @@ -10662,6 +10724,10 @@ Slog.e("TODD", pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR; } + if ((scanFlags & SCAN_AS_PRODUCT) != 0) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT; + } + if (!isSystemApp(pkg)) { // Only system apps can use these features. pkg.mOriginalPackages = null; @@ -11708,6 +11774,8 @@ Slog.e("TODD", codeRoot = Environment.getOemDirectory(); } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { codeRoot = Environment.getVendorDirectory(); + } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { + codeRoot = Environment.getProductDirectory(); } else { // Unrecognized code path; take its top real segment as the apk root: // e.g. /something/app/blah.apk => /something @@ -16110,7 +16178,7 @@ Slog.e("TODD", boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { - // Set the system/privileged/oem/vendor flags as needed + // Set the system/privileged/oem/vendor/product flags as needed final boolean privileged = (oldPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; @@ -16120,12 +16188,16 @@ Slog.e("TODD", final boolean vendor = (oldPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; + final boolean product = + (oldPackage.applicationInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; final @ParseFlags int systemParseFlags = parseFlags; final @ScanFlags int systemScanFlags = scanFlags | SCAN_AS_SYSTEM | (privileged ? SCAN_AS_PRIVILEGED : 0) | (oem ? SCAN_AS_OEM : 0) - | (vendor ? SCAN_AS_VENDOR : 0); + | (vendor ? SCAN_AS_VENDOR : 0) + | (product ? SCAN_AS_PRODUCT : 0); replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags, user, allUsers, installerPackageName, res, installReason); @@ -17373,6 +17445,10 @@ Slog.e("TODD", return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } + private static boolean isProductApp(PackageParser.Package pkg) { + return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; + } + private static boolean hasDomainURLs(PackageParser.Package pkg) { return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0; } @@ -18112,8 +18188,10 @@ Slog.e("TODD", try { final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); + final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); return path.startsWith(privilegedAppDir.getCanonicalPath()) - || path.startsWith(privilegedVendorAppDir.getCanonicalPath()); + || path.startsWith(privilegedVendorAppDir.getCanonicalPath()) + || path.startsWith(privilegedProductAppDir.getCanonicalPath()); } catch (IOException e) { Slog.e(TAG, "Unable to access code path " + path); } @@ -18138,6 +18216,15 @@ Slog.e("TODD", return false; } + static boolean locationIsProduct(String path) { + try { + return path.startsWith(Environment.getProductDirectory().getCanonicalPath()); + } catch (IOException e) { + Slog.e(TAG, "Unable to access code path " + path); + } + return false; + } + /* * Tries to delete system package. */ @@ -18262,6 +18349,9 @@ Slog.e("TODD", if (locationIsVendor(codePathString)) { scanFlags |= SCAN_AS_VENDOR; } + if (locationIsProduct(codePathString)) { + scanFlags |= SCAN_AS_PRODUCT; + } final File codePath = new File(codePathString); final PackageParser.Package pkg = diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 47cd81326932..686c4a5eb321 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1555,6 +1555,15 @@ class PackageManagerShellCommand extends ShellCommand { } } + private boolean isProductApp(String pkg) { + try { + final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + return info != null && info.applicationInfo.isProduct(); + } catch (RemoteException e) { + return false; + } + } + private int runGetPrivappPermissions() { final String pkg = getNextArg(); if (pkg == null) { @@ -1562,9 +1571,14 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } - ArraySet<String> privAppPermissions = isVendorApp(pkg) ? - SystemConfig.getInstance().getVendorPrivAppPermissions(pkg) - : SystemConfig.getInstance().getPrivAppPermissions(pkg); + ArraySet<String> privAppPermissions = null; + if (isVendorApp(pkg)) { + privAppPermissions = SystemConfig.getInstance().getVendorPrivAppPermissions(pkg); + } else if (isProductApp(pkg)) { + privAppPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg); + } else { + privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg); + } getOutPrintWriter().println(privAppPermissions == null ? "{}" : privAppPermissions.toString()); @@ -1578,9 +1592,14 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } - ArraySet<String> privAppPermissions = isVendorApp(pkg) ? - SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg) - : SystemConfig.getInstance().getPrivAppDenyPermissions(pkg); + ArraySet<String> privAppPermissions = null; + if (isVendorApp(pkg)) { + privAppPermissions = SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg); + } else if (isProductApp(pkg)) { + privAppPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg); + } else { + privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg); + } getOutPrintWriter().println(privAppPermissions == null ? "{}" : privAppPermissions.toString()); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 2a2430c07980..3e2bd4a1b3ba 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -173,6 +173,10 @@ public final class PackageSetting extends PackageSettingBase { return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } + public boolean isProduct() { + return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; + } + public boolean isForwardLocked() { return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0; } diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 46ba0060d93e..7c92045c7c5e 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -62,6 +62,7 @@ abstract class SettingBase { & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED | ApplicationInfo.PRIVATE_FLAG_OEM | ApplicationInfo.PRIVATE_FLAG_VENDOR + | ApplicationInfo.PRIVATE_FLAG_PRODUCT | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 8ce412e5783a..5e9019dfea04 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -851,6 +851,8 @@ public final class Settings { pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM; pkgSetting.pkgPrivateFlags |= pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR; + pkgSetting.pkgPrivateFlags |= + pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT; pkgSetting.primaryCpuAbiString = primaryCpuAbi; pkgSetting.secondaryCpuAbiString = secondaryCpuAbi; if (childPkgNames != null) { @@ -4397,6 +4399,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER", ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY", ApplicationInfo.PRIVATE_FLAG_VENDOR, "VENDOR", + ApplicationInfo.PRIVATE_FLAG_PRODUCT, "PRODUCT", ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD", }; diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 6e07eaac9c44..e2123c25dd81 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1215,6 +1215,10 @@ public final class DefaultPermissionGrantPolicy { if (dir.isDirectory() && dir.canRead()) { Collections.addAll(ret, dir.listFiles()); } + dir = new File(Environment.getProductDirectory(), "etc/default-permissions"); + if (dir.isDirectory() && dir.canRead()) { + Collections.addAll(ret, dir.listFiles()); + } return ret.isEmpty() ? null : ret.toArray(new File[0]); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 786b998862de..cb3b1073a593 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -954,9 +954,16 @@ Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); * <p>This handles parent/child apps. */ private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) { - ArraySet<String> wlPermissions = pkg.isVendor() ? - SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName) - : SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName); + ArraySet<String> wlPermissions = null; + if (pkg.isVendor()) { + wlPermissions = + SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName); + } else if (pkg.isProduct()) { + wlPermissions = + SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName); + } else { + wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName); + } // Let's check if this package is whitelisted... boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm); // If it's not, we'll also tail-recurse to the parent. @@ -979,11 +986,17 @@ Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); // Only report violations for apps on system image if (!mSystemReady && !pkg.isUpdatedSystemApp()) { // it's only a reportable violation if the permission isn't explicitly denied - final ArraySet<String> deniedPermissions = pkg.isVendor() ? - SystemConfig.getInstance() - .getVendorPrivAppDenyPermissions(pkg.packageName) - : SystemConfig.getInstance() - .getPrivAppDenyPermissions(pkg.packageName); + ArraySet<String> deniedPermissions = null; + if (pkg.isVendor()) { + deniedPermissions = SystemConfig.getInstance() + .getVendorPrivAppDenyPermissions(pkg.packageName); + } else if (pkg.isProduct()) { + deniedPermissions = SystemConfig.getInstance() + .getProductPrivAppDenyPermissions(pkg.packageName); + } else { + deniedPermissions = SystemConfig.getInstance() + .getPrivAppDenyPermissions(pkg.packageName); + } final boolean permissionViolation = deniedPermissions == null || !deniedPermissions.contains(perm); if (permissionViolation) { diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index faafb39cd1f3..e3a6fa3bffc3 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -328,9 +328,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { * Skip immediately if intent is not relevant to device shutdown. */ if (!intent.getAction().equals(Intent.ACTION_REBOOT) - && !intent.getAction().equals(Intent.ACTION_SHUTDOWN)) { - return; + && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN) + && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) { + return; } + Slog.i(TAG, "StatsCompanionService noticed a shutdown."); synchronized (sStatsdLock) { if (sStatsd == null) { @@ -648,7 +650,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); mTelephony.requestModemActivityInfo(modemReceiver); final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 10); e.writeLong(modemInfo.getTimestamp()); e.writeLong(modemInfo.getSleepTimeMillis()); e.writeLong(modemInfo.getIdleTimeMillis()); @@ -706,7 +708,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { enforceCallingPermission(); if (DEBUG) Slog.d(TAG, "learned that statsdReady"); sayHiToStatsd(); // tell statsd that we're ready too and link to it - mContext.sendBroadcast(new Intent(StatsManager.ACTION_STATSD_STARTED), + mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED), + UserHandle.SYSTEM, android.Manifest.permission.DUMP); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6dc384a8831e..3f49f0cd5c15 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1508,6 +1508,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mTaskStackContainers.getTopStack(); } + ArrayList<Task> getVisibleTasks() { + return mTaskStackContainers.getVisibleTasks(); + } + void onStackWindowingModeChanged(TaskStack stack) { mTaskStackContainers.onStackWindowingModeChanged(stack); } @@ -1802,6 +1806,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo getParent().positionChildAt(position, this, includingParents); } + void positionStackAt(int position, TaskStack child) { + mTaskStackContainers.positionChildAt(position, child, false /* includingParents */); + layoutAndAssignWindowLayersIfNeeded(); + } + int taskIdFromPoint(int x, int y) { for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx); @@ -3255,6 +3264,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mSplitScreenPrimaryStack; } + ArrayList<Task> getVisibleTasks() { + final ArrayList<Task> visibleTasks = new ArrayList<>(); + forAllTasks(task -> { + if (task.isVisible()) { + visibleTasks.add(task); + } + }); + return visibleTasks; + } + /** * Adds the stack to this container. * @see DisplayContent#createStack(int, boolean, StackWindowController) diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java new file mode 100644 index 000000000000..ad4957e4fc6f --- /dev/null +++ b/services/core/java/com/android/server/wm/DisplayWindowController.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.content.res.Configuration; +import android.util.Slog; + +/** + * Controller for the display container. This is created by activity manager to link activity + * displays to the display content they use in window manager. + */ +public class DisplayWindowController + extends WindowContainerController<DisplayContent, WindowContainerListener> { + + private final int mDisplayId; + + public DisplayWindowController(int displayId, WindowContainerListener listener) { + super(listener, WindowManagerService.getInstance()); + mDisplayId = displayId; + + synchronized (mWindowMap) { + // TODO: Convert to setContainer() from DisplayContent once everything is hooked up. + // Currently we are not setup to register for config changes. + mContainer = mRoot.getDisplayContentOrCreate(displayId); + if (mContainer == null) { + throw new IllegalArgumentException("Trying to add displayId=" + displayId); + } + } + } + + @Override + public void removeContainer() { + // TODO: Pipe through from ActivityDisplay to remove the display + throw new UnsupportedOperationException("To be implemented"); + } + + @Override + public void onOverrideConfigurationChanged(Configuration overrideConfiguration) { + // TODO: Pipe through from ActivityDisplay to update the configuration for the display + throw new UnsupportedOperationException("To be implemented"); + } + + /** + * Positions the task stack at the given position in the task stack container. + */ + public void positionChildAt(StackWindowController child, int position) { + synchronized (mWindowMap) { + if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child + + " at " + position); + if (mContainer == null) { + if (DEBUG_STACK) Slog.i(TAG_WM, + "positionTaskStackAt: could not find display=" + mContainer); + return; + } + if (child.mContainer == null) { + if (DEBUG_STACK) Slog.i(TAG_WM, + "positionTaskStackAt: could not find stack=" + this); + return; + } + mContainer.positionStackAt(position, child.mContainer); + } + } + + @Override + public String toString() { + return "{DisplayWindowController displayId=" + mDisplayId + "}"; + } +} diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 88b7a11f02fd..281e0a8441e2 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.INPUT_CONSUMER_PIP; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; @@ -86,6 +87,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { private boolean mAddInputConsumerHandle; private boolean mAddPipInputConsumerHandle; private boolean mAddWallpaperInputConsumerHandle; + private boolean mAddRecentsAnimationInputConsumerHandle; private boolean mDisableWallpaperTouchEvents; private final Rect mTmpRect = new Rect(); private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer = @@ -612,7 +614,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { InputConsumerImpl navInputConsumer; InputConsumerImpl pipInputConsumer; InputConsumerImpl wallpaperInputConsumer; - Rect pipTouchableBounds; + InputConsumerImpl recentsAnimationInputConsumer; boolean inDrag; WallpaperController wallpaperController; @@ -622,11 +624,13 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY); pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY); wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER, DEFAULT_DISPLAY); + recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION, + DEFAULT_DISPLAY); mAddInputConsumerHandle = navInputConsumer != null; mAddPipInputConsumerHandle = pipInputConsumer != null; mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null; + mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null; mTmpRect.setEmpty(); - pipTouchableBounds = mAddPipInputConsumerHandle ? mTmpRect : null; mDisableWallpaperTouchEvents = false; this.inDrag = inDrag; wallpaperController = mService.mRoot.mWallpaperController; @@ -659,12 +663,28 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { final boolean hasFocus = w == mInputFocus; final boolean isVisible = w.isVisibleLw(); + if (mAddRecentsAnimationInputConsumerHandle) { + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + if (recentsAnimationController != null + && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) { + if (recentsAnimationController.updateInputConsumerForApp( + recentsAnimationInputConsumer, hasFocus)) { + addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle); + mAddRecentsAnimationInputConsumerHandle = false; + } + // Skip adding the window below regardless of whether there is an input consumer + // to handle it + return; + } + } + if (w.inPinnedWindowingMode()) { if (mAddPipInputConsumerHandle && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) { // Update the bounds of the Pip input consumer to match the window bounds. - w.getBounds(pipTouchableBounds); - pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds); + w.getBounds(mTmpRect); + pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); addInputWindowHandle(pipInputConsumer.mWindowHandle); mAddPipInputConsumerHandle = false; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java new file mode 100644 index 000000000000..c7d4b8ed0f16 --- /dev/null +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -0,0 +1,385 @@ +/* + * 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.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.app.ActivityManager; +import android.app.ActivityManager.TaskSnapshot; +import android.app.WindowConfiguration; +import android.graphics.GraphicBuffer; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Binder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; +import android.util.Slog; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Controls a single instance of the remote driven recents animation. In particular, this allows + * the calling SystemUI to animate the visible task windows as a part of the transition. The remote + * runner is provided an animation controller which allows it to take screenshots and to notify + * window manager when the animation is completed. In addition, window manager may also notify the + * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.) + */ +public class RecentsAnimationController { + private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM; + private static final boolean DEBUG = false; + + private final WindowManagerService mService; + private final IRecentsAnimationRunner mRunner; + private final RecentsAnimationCallbacks mCallbacks; + private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); + + // The recents component app token that is shown behind the visibile tasks + private AppWindowToken mHomeAppToken; + + // We start the RecentsAnimationController in a pending-start state since we need to wait for + // the wallpaper/activity to draw before we can give control to the handler to start animating + // the visible task surfaces + private boolean mPendingStart = true; + + // Set when the animation has been canceled + private boolean mCanceled = false; + + // Whether or not the input consumer is enabled. The input consumer must be both registered and + // enabled for it to start intercepting touch events. + private boolean mInputConsumerEnabled; + + private Rect mTmpRect = new Rect(); + + public interface RecentsAnimationCallbacks { + void onAnimationFinished(boolean moveHomeToTop); + } + + private final IRecentsAnimationController mController = + new IRecentsAnimationController.Stub() { + + @Override + public TaskSnapshot screenshotTask(int taskId) { + if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled); + long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + if (mCanceled) { + return null; + } + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final TaskAnimationAdapter adapter = mPendingAnimations.get(i); + final Task task = adapter.mTask; + if (task.mTaskId == taskId) { + // TODO: Save this screenshot as the task snapshot? + final Rect taskFrame = new Rect(); + task.getBounds(taskFrame); + final GraphicBuffer buffer = SurfaceControl.captureLayers( + task.getSurfaceControl().getHandle(), taskFrame, 1f); + final AppWindowToken topChild = task.getTopChild(); + final WindowState mainWindow = topChild.findMainWindow(); + return new TaskSnapshot(buffer, topChild.getConfiguration().orientation, + mainWindow.mStableInsets, + ActivityManager.isLowRamDeviceStatic() /* reduced */, + 1.0f /* scale */); + } + } + return null; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void finish(boolean moveHomeToTop) { + if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled); + long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + if (mCanceled) { + return; + } + } + + // Note, the callback will handle its own synchronization, do not lock on WM lock + // prior to calling the callback + mCallbacks.onAnimationFinished(moveHomeToTop); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setInputConsumerEnabled(boolean enabled) { + if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled=" + + mCanceled); + long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + if (mCanceled) { + return; + } + + mInputConsumerEnabled = enabled; + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + mService.scheduleAnimationLocked(); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + /** + * Initializes a new RecentsAnimationController. + * + * @param remoteAnimationRunner The remote runner which should be notified when the animation is + * ready to start or has been canceled + * @param callbacks Callbacks to be made when the animation finishes + * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the + * animation is complete. Will be passed to the callback. + */ + RecentsAnimationController(WindowManagerService service, + IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, + int displayId) { + mService = service; + mRunner = remoteAnimationRunner; + mCallbacks = callbacks; + + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + final ArrayList<Task> visibleTasks = dc.getVisibleTasks(); + if (visibleTasks.isEmpty()) { + cancelAnimation(); + return; + } + + // Make leashes for each of the visible tasks and add it to the recents animation to be + // started + final int taskCount = visibleTasks.size(); + for (int i = 0; i < taskCount; i++) { + final Task task = visibleTasks.get(i); + final WindowConfiguration config = task.getWindowConfiguration(); + if (config.tasksAreFloating() + || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || config.getActivityType() == ACTIVITY_TYPE_HOME) { + continue; + } + addAnimation(task); + } + + // Adjust the wallpaper visibility for the showing home activity + final AppWindowToken recentsComponentAppToken = + dc.getHomeStack().getTopChild().getTopFullscreenAppToken(); + if (recentsComponentAppToken != null) { + if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")"); + mHomeAppToken = recentsComponentAppToken; + final WallpaperController wc = dc.mWallpaperController; + if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) { + dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + dc.setLayoutNeeded(); + } + } + + mService.mWindowPlacerLocked.performSurfacePlacement(); + } + + private void addAnimation(Task task) { + if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")"); + final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */, + mService.mAnimator::addAfterPrepareSurfacesRunnable, mService); + final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task); + anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */); + task.commitPendingTransaction(); + mPendingAnimations.add(taskAdapter); + } + + void startAnimation() { + if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart); + if (!mPendingStart) { + return; + } + try { + final RemoteAnimationTarget[] appAnimations = + new RemoteAnimationTarget[mPendingAnimations.size()]; + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp(); + } + mPendingStart = false; + mRunner.onAnimationStart(mController, appAnimations); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to start recents animation", e); + } + } + + void cancelAnimation() { + if (DEBUG) Log.d(TAG, "cancelAnimation()"); + if (mCanceled) { + // We've already canceled the animation + return; + } + mCanceled = true; + try { + mRunner.onAnimationCanceled(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to cancel recents animation", e); + } + + // Clean up and return to the previous app + mCallbacks.onAnimationFinished(false /* moveHomeToTop */); + } + + void cleanupAnimation() { + if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations=" + + mPendingAnimations.size()); + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final TaskAnimationAdapter adapter = mPendingAnimations.get(i); + adapter.mCapturedFinishCallback.onAnimationFinished(adapter); + } + mPendingAnimations.clear(); + + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + mService.scheduleAnimationLocked(); + mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + } + + void checkAnimationReady(WallpaperController wallpaperController) { + if (mPendingStart) { + final boolean wallpaperReady = !isHomeAppOverWallpaper() + || (wallpaperController.getWallpaperTarget() != null + && wallpaperController.wallpaperTransitionReady()); + if (wallpaperReady) { + mService.getRecentsAnimationController().startAnimation(); + } + } + } + + boolean isWallpaperVisible(WindowState w) { + return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken + && isHomeAppOverWallpaper(); + } + + boolean hasInputConsumerForApp(AppWindowToken appToken) { + return mInputConsumerEnabled && isAnimatingApp(appToken); + } + + boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer, + boolean hasFocus) { + // Update the input consumer touchable region to match the home app main window + final WindowState homeAppMainWindow = mHomeAppToken != null + ? mHomeAppToken.findMainWindow() + : null; + if (homeAppMainWindow != null) { + homeAppMainWindow.getBounds(mTmpRect); + recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus; + recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); + return true; + } + return false; + } + + private boolean isHomeAppOverWallpaper() { + if (mHomeAppToken == null) { + return false; + } + return mHomeAppToken.windowsCanBeWallpaperTarget(); + } + + private boolean isAnimatingApp(AppWindowToken appToken) { + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + final Task task = mPendingAnimations.get(i).mTask; + for (int j = task.getChildCount() - 1; j >= 0; j--) { + final AppWindowToken app = task.getChildAt(j); + if (app == appToken) { + return true; + } + } + } + return false; + } + + private class TaskAnimationAdapter implements AnimationAdapter { + + private Task mTask; + private SurfaceControl mCapturedLeash; + private OnAnimationFinishedCallback mCapturedFinishCallback; + + TaskAnimationAdapter(Task task) { + mTask = task; + } + + RemoteAnimationTarget createRemoteAnimationApp() { + // TODO: Do we need position and stack bounds? + return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash, + !mTask.fillsParent(), + mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect, + mTask.getPrefixOrderIndex(), new Point(), new Rect(), + mTask.getWindowConfiguration()); + } + + @Override + public boolean getDetachWallpaper() { + return false; + } + + @Override + public int getBackgroundColor() { + return 0; + } + + @Override + public void startAnimation(SurfaceControl animationLeash, Transaction t, + OnAnimationFinishedCallback finishCallback) { + mCapturedLeash = animationLeash; + mCapturedFinishCallback = finishCallback; + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + cancelAnimation(); + } + + @Override + public long getDurationHint() { + return 0; + } + + @Override + public long getStatusBarTransitionsStartTime() { + return SystemClock.uptimeMillis(); + } + } + + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":"); + pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart); + pw.print(innerPrefix); pw.println("mHomeAppToken=" + mHomeAppToken); + } +} diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 8515dcb69970..7d4eafb07fe9 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -160,7 +160,8 @@ class RemoteAnimationController { return new RemoteAnimationTarget(task.mTaskId, getMode(), mCapturedLeash, !mAppWindowToken.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, - mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds); + mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds, + task.getWindowConfiguration()); } private int getMode() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2cc96c9ee7b6..deed7f17e4e6 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -623,6 +623,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { defaultDisplay.pendingLayoutChanges); } + // Defer starting the recents animation until the wallpaper has drawn + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + if (recentsAnimationController != null) { + recentsAnimationController.checkAnimationReady(mWallpaperController); + } + if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0 && !mService.mAppTransition.isReady()) { // At this point, there was a window with a wallpaper that was force hiding other diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 10f1c3a37dcf..0512a08c59db 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -62,7 +62,7 @@ class SurfaceAnimator { * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing * surfaces in WM. Can be implemented differently during testing. */ - SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback, + SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback, Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) { mAnimatable = animatable; mService = service; @@ -71,7 +71,8 @@ class SurfaceAnimator { addAfterPrepareSurfaces); } - private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback, + private OnAnimationFinishedCallback getFinishedCallback( + @Nullable Runnable animationFinishedCallback, Consumer<Runnable> addAfterPrepareSurfaces) { return anim -> { synchronized (mService.mWindowMap) { @@ -97,7 +98,9 @@ class SurfaceAnimator { SurfaceControl.openTransaction(); try { reset(t, true /* destroyLeash */); - animationFinishedCallback.run(); + if (animationFinishedCallback != null) { + animationFinishedCallback.run(); + } } finally { SurfaceControl.mergeToGlobalTransaction(t); SurfaceControl.closeTransaction(); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 1218d3bc1b9b..f2ad6fb7a888 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -149,8 +149,17 @@ class WallpaperController { mFindResults.setUseTopWallpaperAsTarget(true); } + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; - if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) { + final boolean isRecentsTransitionTarget = (recentsAnimationController != null + && recentsAnimationController.isWallpaperVisible(w)); + if (isRecentsTransitionTarget) { + if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); + mFindResults.setWallpaperTarget(w); + return true; + } else if (hasWallpaper && w.isOnScreen() + && (mWallpaperTarget == w || w.isDrawFinishedLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); mFindResults.setWallpaperTarget(w); if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) { @@ -199,15 +208,22 @@ class WallpaperController { } } - private boolean isWallpaperVisible(WindowState wallpaperTarget) { + private final boolean isWallpaperVisible(WindowState wallpaperTarget) { + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + boolean isAnimatingWithRecentsComponent = recentsAnimationController != null + && recentsAnimationController.isWallpaperVisible(wallpaperTarget); if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) ? wallpaperTarget.mAppToken.isSelfAnimating() : null) - + " prev=" + mPrevWallpaperTarget); + + " prev=" + mPrevWallpaperTarget + + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent); return (wallpaperTarget != null - && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null - && wallpaperTarget.mAppToken.isSelfAnimating()))) + && (!wallpaperTarget.mObscured + || isAnimatingWithRecentsComponent + || (wallpaperTarget.mAppToken != null + && wallpaperTarget.mAppToken.isSelfAnimating()))) || mPrevWallpaperTarget != null; } @@ -587,6 +603,11 @@ class WallpaperController { mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT; if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT"); + + // If there was a recents animation in progress, cancel that animation + if (mService.getRecentsAnimationController() != null) { + mService.getRecentsAnimationController().cancelAnimation(); + } return true; } return false; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 8a500b55b936..1f9255a2b20f 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -343,9 +343,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** Returns true if this window container has the input child. */ - boolean hasChild(WindowContainer child) { + boolean hasChild(E child) { for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer current = mChildren.get(i); + final E current = mChildren.get(i); if (current == child || current.hasChild(child)) { return true; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index de1e7ecb2bb9..4fb239085e5c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -24,6 +24,8 @@ import static android.Manifest.permission.RESTRICTED_VR_ACCESS; import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.StatusBarManager.DISABLE_MASK; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_USER_HANDLE; @@ -123,6 +125,7 @@ import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IAssistDataReceiver; +import android.app.WindowConfiguration; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -196,6 +199,7 @@ import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; +import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; @@ -528,6 +532,7 @@ public class WindowManagerService extends IWindowManager.Stub IInputMethodManager mInputMethodManager; AccessibilityController mAccessibilityController; + private RecentsAnimationController mRecentsAnimationController; Watermark mWatermark; StrictModeFlash mStrictModeFlash; @@ -2670,6 +2675,39 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void initializeRecentsAnimation( + IRecentsAnimationRunner recentsAnimationRunner, + RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) { + synchronized (mWindowMap) { + cancelRecentsAnimation(); + mRecentsAnimationController = new RecentsAnimationController(this, + recentsAnimationRunner, callbacks, displayId); + } + } + + public RecentsAnimationController getRecentsAnimationController() { + return mRecentsAnimationController; + } + + public void cancelRecentsAnimation() { + synchronized (mWindowMap) { + if (mRecentsAnimationController != null) { + // This call will call through to cleanupAnimation() below after the animation is + // canceled + mRecentsAnimationController.cancelAnimation(); + } + } + } + + public void cleanupRecentsAnimation() { + synchronized (mWindowMap) { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.cleanupAnimation(); + mRecentsAnimationController = null; + } + } + } + public void setAppFullscreen(IBinder token, boolean toOpaque) { synchronized (mWindowMap) { final AppWindowToken atoken = mRoot.getAppWindowToken(token); @@ -6327,6 +6365,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation); pw.println(" mLayoutToAnim:"); mAppTransition.dump(pw, " "); + if (mRecentsAnimationController != null) { + pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController); + mRecentsAnimationController.dump(pw, " "); + } } } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 67bad0fab7c5..8fd5be2a45ff 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -358,6 +358,7 @@ struct GnssCallback : public IGnssCallback { Return<void> gnssAcquireWakelockCb() override; Return<void> gnssReleaseWakelockCb() override; Return<void> gnssRequestTimeCb() override; + Return<void> gnssRequestLocationCb(const bool independentFromGnss) override; Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override; // New in 1.1 @@ -472,6 +473,11 @@ Return<void> GnssCallback::gnssRequestTimeCb() { return Void(); } +Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) { + // TODO(b/72405645): call into java implementation + return Void(); +} + Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) { ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 99712a5173db..8f6fac0566b9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -311,6 +311,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PRINTING_ENABLED = "printing-enabled"; + private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1); @@ -12624,6 +12626,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try (FileInputStream stream = new FileInputStream(bundleFile)) { XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, null); + parser.next(); return PersistableBundle.restoreFromXml(parser); } catch (IOException | XmlPullParserException | IllegalArgumentException e) { Slog.e(LOG_TAG, "Caught exception while trying to load the " @@ -12845,7 +12848,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final XmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(stream, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); + serializer.startTag(null, TAG_TRANSFER_OWNERSHIP_BUNDLE); bundle.saveToXml(serializer); + serializer.endTag(null, TAG_TRANSFER_OWNERSHIP_BUNDLE); + serializer.endDocument(); atomicFile.finishWrite(stream); } catch (IOException | XmlPullParserException e) { Slog.e(LOG_TAG, "Caught exception while trying to save the " diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 372b5be98a3b..5d8aca195ef8 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -59,7 +59,6 @@ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Uses API introduced in O (26) --> diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index b68bf2db1237..a16f118007da 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -15,8 +15,6 @@ */ package com.android.server; -import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY; - import static com.android.server.ForceAppStandbyTracker.TARGET_OP; import static org.junit.Assert.assertEquals; @@ -35,12 +33,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; import android.app.IUidObserver; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManagerInternal; +import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -76,6 +76,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; @@ -112,6 +113,18 @@ public class ForceAppStandbyTrackerTest { } @Override + UsageStatsManagerInternal injectUsageStatsManagerInternal() { + return mMockUsageStatsManagerInternal; + } + + @Override + int injectGetGlobalSettingInt(String key, int def) { + Integer val = mGlobalSettings.get(key); + + return (val == null) ? def : val; + } + + @Override boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; }; } @@ -143,19 +156,24 @@ public class ForceAppStandbyTrackerTest { @Mock private PowerManagerInternal mMockPowerManagerInternal; + @Mock + private UsageStatsManagerInternal mMockUsageStatsManagerInternal; + + private MockContentResolver mMockContentResolver; + private IUidObserver mIUidObserver; private IAppOpsCallback.Stub mAppOpsCallback; private Consumer<PowerSaveState> mPowerSaveObserver; private BroadcastReceiver mReceiver; - - private MockContentResolver mMockContentResolver; - private FakeSettingsProvider mFakeSettingsProvider; + private AppIdleStateChangeListener mAppIdleStateChangeListener; private boolean mPowerSaveMode; private boolean mIsSmallBatteryDevice; private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet(); + private final HashMap<String, Integer> mGlobalSettings = new HashMap<>(); + @Before public void setUp() { mMainHandler = new Handler(Looper.getMainLooper()); @@ -198,9 +216,7 @@ public class ForceAppStandbyTrackerTest { )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>()); mMockContentResolver = new MockContentResolver(); - mFakeSettingsProvider = new FakeSettingsProvider(); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); - mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider); // Call start. instance.start(); @@ -214,6 +230,8 @@ public class ForceAppStandbyTrackerTest { ArgumentCaptor.forClass(Consumer.class); ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); + ArgumentCaptor<AppIdleStateChangeListener> appIdleStateChangeListenerCaptor = + ArgumentCaptor.forClass(AppIdleStateChangeListener.class); verify(mMockIActivityManager).registerUidObserver( uidObserverArgumentCaptor.capture(), @@ -231,11 +249,14 @@ public class ForceAppStandbyTrackerTest { verify(mMockContext).registerReceiver( receiverCaptor.capture(), any(IntentFilter.class)); + verify(mMockUsageStatsManagerInternal).addAppIdleStateChangeListener( + appIdleStateChangeListenerCaptor.capture()); mIUidObserver = uidObserverArgumentCaptor.getValue(); mAppOpsCallback = appOpsCallbackCaptor.getValue(); mPowerSaveObserver = powerSaveObserverCaptor.getValue(); mReceiver = receiverCaptor.getValue(); + mAppIdleStateChangeListener = appIdleStateChangeListenerCaptor.getValue(); assertNotNull(mIUidObserver); assertNotNull(mAppOpsCallback); @@ -275,6 +296,12 @@ public class ForceAppStandbyTrackerTest { /*exemptFromBatterySaver=*/ false); } + private void areRestrictedWithExemption(ForceAppStandbyTrackerTestable instance, + int uid, String packageName, int restrictionTypes) { + areRestricted(instance, uid, packageName, restrictionTypes, + /*exemptFromBatterySaver=*/ true); + } + @Test public void testAll() throws Exception { final ForceAppStandbyTrackerTestable instance = newInstance(); @@ -285,6 +312,10 @@ public class ForceAppStandbyTrackerTest { areRestricted(instance, UID_2, PACKAGE_2, NONE); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); + areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); + areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); @@ -294,6 +325,10 @@ public class ForceAppStandbyTrackerTest { areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); + areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); + areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + // Toggle the foreground state. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); @@ -420,6 +455,73 @@ public class ForceAppStandbyTrackerTest { assertTrue(instance.isUidTempPowerSaveWhitelisted(UID_10_2)); } + @Test + public void testExempt() throws Exception { + final ForceAppStandbyTrackerTestable instance = newInstance(); + callStart(instance); + + assertFalse(instance.isForceAllAppsStandbyEnabled()); + areRestricted(instance, UID_1, PACKAGE_1, NONE); + areRestricted(instance, UID_2, PACKAGE_2, NONE); + areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + mPowerSaveMode = true; + mPowerSaveObserver.accept(getPowerSaveState()); + + assertTrue(instance.isForceAllAppsStandbyEnabled()); + + areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); + areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); + areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); + areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + // Exempt package 2 on user-10. + mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, + UsageStatsManager.STANDBY_BUCKET_EXEMPTED); + + areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); + areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); + areRestricted(instance, UID_10_2, PACKAGE_2, NONE); + + areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); + areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); + areRestrictedWithExemption(instance, UID_10_2, PACKAGE_2, NONE); + + // Exempt package 1 on user-0. + mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false, + UsageStatsManager.STANDBY_BUCKET_EXEMPTED); + + areRestricted(instance, UID_1, PACKAGE_1, NONE); + areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); + areRestricted(instance, UID_10_2, PACKAGE_2, NONE); + + // Unexempt package 2 on user-10. + mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, + UsageStatsManager.STANDBY_BUCKET_ACTIVE); + + areRestricted(instance, UID_1, PACKAGE_1, NONE); + areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); + areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); + + // Check force-app-standby. + // EXEMPT doesn't exempt from force-app-standby. + mPowerSaveMode = false; + mPowerSaveObserver.accept(getPowerSaveState()); + + mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false, + UsageStatsManager.STANDBY_BUCKET_EXEMPTED); + mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false, + UsageStatsManager.STANDBY_BUCKET_EXEMPTED); + + setAppOps(UID_1, PACKAGE_1, true); + + areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); + areRestricted(instance, UID_2, PACKAGE_2, NONE); + + areRestrictedWithExemption(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); + areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); + } + public void loadPersistedAppOps() throws Exception { final ForceAppStandbyTrackerTestable instance = newInstance(); @@ -661,6 +763,7 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); + waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); @@ -780,6 +883,7 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); + waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1)); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); @@ -878,7 +982,7 @@ public class ForceAppStandbyTrackerTest { assertFalse(instance.isForceAllAppsStandbyEnabled()); // Setting/experiment for all app standby for small battery is enabled - Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1); + mGlobalSettings.put(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1); instance.mFlagsObserver.onChange(true, Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED)); assertTrue(instance.isForceAllAppsStandbyEnabled()); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 96bf49b288c9..10253c570f3f 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; +import com.android.server.wm.DisplayWindowController; import org.mockito.invocation.InvocationOnMock; import android.app.IApplicationThread; @@ -345,7 +346,7 @@ public class ActivityTestsBase { } } - private static class TestActivityDisplay extends ActivityDisplay { + protected static class TestActivityDisplay extends ActivityDisplay { private final ActivityStackSupervisor mSupervisor; TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { @@ -374,6 +375,11 @@ public class ActivityTestsBase { this, stackId, mSupervisor, windowingMode, activityType, onTop); } } + + @Override + protected DisplayWindowController createWindowContainerController() { + return mock(DisplayWindowController.class); + } } private static WindowManagerService prepareMockWindowManager() { diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 5a2110258828..24566fcf8f0d 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -573,7 +573,8 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.getTaskDescription(0)); assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0)); assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null, - null, 0)); + null)); + assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation()); } private void testGetTasksApis(boolean expectCallable) { @@ -676,8 +677,8 @@ public class RecentTasksTest extends ActivityTestsBase { @Override public void initialize() { super.initialize(); - mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY); - mOtherDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY); + mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); + mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); attachDisplay(mOtherDisplay); attachDisplay(mDisplay); } diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java index fc7562869490..c6ce7e1188e8 100644 --- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java @@ -68,7 +68,7 @@ public class RunningTasksTest extends ActivityTestsBase { // Create a number of stacks with tasks (of incrementing active time) final ActivityStackSupervisor supervisor = mService.mStackSupervisor; final SparseArray<ActivityDisplay> displays = new SparseArray<>(); - final ActivityDisplay display = new ActivityDisplay(supervisor, DEFAULT_DISPLAY); + final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY); displays.put(DEFAULT_DISPLAY, display); final int numStacks = 2; diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java index c7fa62e56380..376db5bbad67 100644 --- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java +++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java @@ -388,7 +388,8 @@ public class ConnOnActivityStartTest { latch.countDown(); } }); - launchIntent.putExtras(extras); + launchIntent.putExtras(extras) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(launchIntent); if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { if (errors[0] != null) { diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index 0cbda2845e53..2becdf230dbc 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -36,6 +36,8 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerInternal; import libcore.io.IoUtils; @@ -202,27 +204,23 @@ public class AppIdleHistory { * that's in the future, then the usage event is temporary and keeps the app in the specified * bucket at least until the timeout is reached. This can be used to keep the app in an * elevated bucket for a while until some important task gets to run. - * @param packageName - * @param userId - * @param bucket the bucket to set the app to + * @param appUsageHistory the usage record for the app being updated + * @param packageName name of the app being updated, for logging purposes + * @param newBucket the bucket to set the app to * @param elapsedRealtime mark as used time if non-zero * @param timeout set the timeout of the specified bucket, if non-zero * @return */ - public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime, - long timeout) { - ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); - AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, - elapsedRealtime, true); - + public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, + int newBucket, long elapsedRealtime, long timeout) { if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); } - if (appUsageHistory.currentBucket > bucket) { - appUsageHistory.currentBucket = bucket; + if (appUsageHistory.currentBucket > newBucket) { + appUsageHistory.currentBucket = newBucket; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory .currentBucket @@ -235,7 +233,26 @@ public class AppIdleHistory { } appUsageHistory.bucketingReason = REASON_USAGE; - return appUsageHistory.currentBucket; + return appUsageHistory; + } + + /** + * Mark the app as used and update the bucket if necessary. If there is a timeout specified + * that's in the future, then the usage event is temporary and keeps the app in the specified + * bucket at least until the timeout is reached. This can be used to keep the app in an + * elevated bucket for a while until some important task gets to run. + * @param packageName + * @param userId + * @param newBucket the bucket to set the app to + * @param elapsedRealtime mark as used time if non-zero + * @param timeout set the timeout of the specified bucket, if non-zero + * @return + */ + public AppUsageHistory reportUsage(String packageName, int userId, int newBucket, + long nowElapsed, long timeout) { + ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); + AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true); + return reportUsage(history, packageName, newBucket, nowElapsed, timeout); } private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 6782188c0f34..8cb2eecc654a 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -27,7 +27,6 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; - import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; @@ -81,6 +80,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; +import com.android.server.usage.AppIdleHistory.AppUsageHistory; import java.io.File; import java.io.PrintWriter; @@ -198,6 +198,48 @@ public class AppStandbyController { private PackageManager mPackageManager; Injector mInjector; + static final ArrayList<StandbyUpdateRecord> sStandbyUpdatePool = new ArrayList<>(4); + + public static class StandbyUpdateRecord { + // Identity of the app whose standby state has changed + String packageName; + int userId; + + // What the standby bucket the app is now in + int bucket; + + // Whether the bucket change is because the user has started interacting with the app + boolean isUserInteraction; + + StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) { + this.packageName = pkgName; + this.userId = userId; + this.bucket = bucket; + this.isUserInteraction = isInteraction; + } + + public static StandbyUpdateRecord obtain(String pkgName, int userId, + int bucket, boolean isInteraction) { + synchronized (sStandbyUpdatePool) { + final int size = sStandbyUpdatePool.size(); + if (size < 1) { + return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction); + } + StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1); + r.packageName = pkgName; + r.userId = userId; + r.bucket = bucket; + r.isUserInteraction = isInteraction; + return r; + } + } + + public void recycle() { + synchronized (sStandbyUpdatePool) { + sStandbyUpdatePool.add(this); + } + } + } AppStandbyController(Context context, Looper looper) { this(new Injector(context, looper)); @@ -279,11 +321,11 @@ public class AppStandbyController { } if (!packageName.equals(providerPkgName)) { synchronized (mAppIdleLock) { - int newBucket = mAppIdleHistory.reportUsage(packageName, userId, + AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR); maybeInformListeners(packageName, userId, elapsedRealtime, - newBucket); + appUsage.currentBucket, false); } } } catch (PackageManager.NameNotFoundException e) { @@ -417,7 +459,7 @@ public class AppStandbyController { STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT); } maybeInformListeners(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED); + STANDBY_BUCKET_EXEMPTED, false); } else { synchronized (mAppIdleLock) { AppIdleHistory.AppUsageHistory app = @@ -446,7 +488,7 @@ public class AppStandbyController { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, REASON_TIMEOUT); maybeInformListeners(packageName, userId, elapsedRealtime, - newBucket); + newBucket, false); } } } @@ -474,13 +516,16 @@ public class AppStandbyController { } private void maybeInformListeners(String packageName, int userId, - long elapsedRealtime, int bucket) { + long elapsedRealtime, int bucket, boolean userStartedInteracting) { synchronized (mAppIdleLock) { // TODO: fold these into one call + lookup for efficiency if needed if (mAppIdleHistory.shouldInformListeners(packageName, userId, elapsedRealtime, bucket)) { + StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId, + bucket, userStartedInteracting); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, - userId, bucket, packageName)); + StandbyUpdateRecord.obtain(packageName, userId, + bucket, userStartedInteracting))); } } } @@ -566,19 +611,27 @@ public class AppStandbyController { || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) { - final int newBucket; + final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( + event.mPackage, userId, elapsedRealtime); + final int prevBucket = appHistory.currentBucket; + final String prevBucketReason = appHistory.bucketingReason; if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) { - newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId, + mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_WORKING_SET, elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR); } else { - newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId, + mAppIdleHistory.reportUsage(event.mPackage, userId, STANDBY_BUCKET_ACTIVE, elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR); } + final boolean userStartedInteracting = + appHistory.currentBucket == STANDBY_BUCKET_ACTIVE && + prevBucket != appHistory.currentBucket && + prevBucketReason != REASON_USAGE; maybeInformListeners(event.mPackage, userId, elapsedRealtime, - newBucket); + appHistory.currentBucket, userStartedInteracting); + if (previouslyIdle) { notifyBatteryStats(event.mPackage, userId, false); } @@ -611,7 +664,7 @@ public class AppStandbyController { userId, elapsedRealtime); // Inform listeners if necessary if (previouslyIdle != stillIdle) { - maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket); + maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false); if (!stillIdle) { notifyBatteryStats(packageName, userId, idle); } @@ -871,8 +924,7 @@ public class AppStandbyController { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason); } - maybeInformListeners(packageName, userId, elapsedRealtime, - newBucket); + maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false); } @VisibleForTesting @@ -972,11 +1024,14 @@ public class AppStandbyController { return packageName != null && packageName.equals(activeScorer); } - void informListeners(String packageName, int userId, int bucket) { + void informListeners(String packageName, int userId, int bucket, boolean userInteraction) { final boolean idle = bucket >= STANDBY_BUCKET_RARE; synchronized (mPackageAccessListeners) { for (AppIdleStateChangeListener listener : mPackageAccessListeners) { listener.onAppIdleStateChanged(packageName, userId, idle, bucket); + if (userInteraction) { + listener.onUserInteractionStarted(packageName, userId); + } } } } @@ -1230,7 +1285,9 @@ public class AppStandbyController { public void handleMessage(Message msg) { switch (msg.what) { case MSG_INFORM_LISTENERS: - informListeners((String) msg.obj, msg.arg1, msg.arg2); + StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj; + informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction); + r.recycle(); break; case MSG_FORCE_IDLE_STATE: diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 096fdcc24030..36a2a95dd1e1 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1043,6 +1043,12 @@ public class UsageStatsService extends SystemService implements return mAppStandby.getTimeSinceLastJobRun(packageName, userId); } + @Override + public void reportAppJobState(String packageName, int userId, + int numDeferredJobs, long timeSinceLastJobRun) { + } + + @Override public void onActiveAdminAdded(String packageName, int userId) { mAppStandby.addActiveDeviceAdmin(packageName, userId); } diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java index f71f58d01ea8..fc4e17aa6f00 100644 --- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java +++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java @@ -28,10 +28,14 @@ import android.os.Parcelable; public class DataConnectionRealTimeInfo implements Parcelable { private long mTime; // Time the info was collected since boot in nanos; - public static final int DC_POWER_STATE_LOW = 1; - public static final int DC_POWER_STATE_MEDIUM = 2; - public static final int DC_POWER_STATE_HIGH = 3; - public static final int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE; + public static final int DC_POWER_STATE_LOW + = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_LOW ; // = 1 + public static final int DC_POWER_STATE_MEDIUM + = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_MEDIUM; // = 2 + public static final int DC_POWER_STATE_HIGH + = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_HIGH; // = 3 + public static final int DC_POWER_STATE_UNKNOWN + = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_UNKNOWN; // = Integer.MAX_VALUE private int mDcPowerState; // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN] diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java new file mode 100644 index 000000000000..97e3037b3c90 --- /dev/null +++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java @@ -0,0 +1,72 @@ +package android.telephony; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Class that stores information specific to data network registration. + * @hide + */ +public class DataSpecificRegistrationStates implements Parcelable{ + /** + * The maximum number of simultaneous Data Calls that + * must be established using setupDataCall(). + */ + public final int maxDataCalls; + + DataSpecificRegistrationStates(int maxDataCalls) { + this.maxDataCalls = maxDataCalls; + } + + private DataSpecificRegistrationStates(Parcel source) { + maxDataCalls = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(maxDataCalls); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(maxDataCalls); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof DataSpecificRegistrationStates)) { + return false; + } + + DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o; + return this.maxDataCalls == other.maxDataCalls; + } + + public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR = + new Parcelable.Creator<DataSpecificRegistrationStates>() { + @Override + public DataSpecificRegistrationStates createFromParcel(Parcel source) { + return new DataSpecificRegistrationStates(source); + } + + @Override + public DataSpecificRegistrationStates[] newArray(int size) { + return new DataSpecificRegistrationStates[size]; + } + }; +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index e0510694d4ad..4f137bea69f7 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -105,6 +105,11 @@ public class NetworkRegistrationState implements Parcelable { @Nullable private final CellIdentity mCellIdentity; + @Nullable + private VoiceSpecificRegistrationStates mVoiceSpecificStates; + + @Nullable + private DataSpecificRegistrationStates mDataSpecificStates; /** * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType} @@ -128,6 +133,34 @@ public class NetworkRegistrationState implements Parcelable { mEmergencyOnly = emergencyOnly; } + /** + * Constructor for voice network registration states. + * @hide + */ + public NetworkRegistrationState(int transportType, int domain, int regState, + int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly, + int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported, + int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) { + this(transportType, domain, regState, accessNetworkTechnology, + reasonForDenial, emergencyOnly, availableServices, cellIdentity); + + mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator, + systemIsInPrl, defaultRoamingIndicator); + } + + /** + * Constructor for data network registration states. + * @hide + */ + public NetworkRegistrationState(int transportType, int domain, int regState, + int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly, + int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) { + this(transportType, domain, regState, accessNetworkTechnology, + reasonForDenial, emergencyOnly, availableServices, cellIdentity); + + mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls); + } + protected NetworkRegistrationState(Parcel source) { mTransportType = source.readInt(); mDomain = source.readInt(); @@ -137,6 +170,10 @@ public class NetworkRegistrationState implements Parcelable { mEmergencyOnly = source.readBoolean(); mAvailableServices = source.createIntArray(); mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader()); + mVoiceSpecificStates = source.readParcelable( + VoiceSpecificRegistrationStates.class.getClassLoader()); + mDataSpecificStates = source.readParcelable( + DataSpecificRegistrationStates.class.getClassLoader()); } /** @@ -173,6 +210,36 @@ public class NetworkRegistrationState implements Parcelable { return mAccessNetworkTechnology; } + /** + * @return Reason for denial from network. + */ + public int getReasonForDenial() { + return mReasonForDenial; + } + + /** + * @return The cell information. + */ + public CellIdentity getCellIdentity() { + return mCellIdentity; + } + + /** + * @hide + */ + @Nullable + public VoiceSpecificRegistrationStates getVoiceSpecificStates() { + return mVoiceSpecificStates; + } + + /** + * @hide + */ + @Nullable + public DataSpecificRegistrationStates getDataSpecificStates() { + return mDataSpecificStates; + } + @Override public int describeContents() { return 0; @@ -202,13 +269,16 @@ public class NetworkRegistrationState implements Parcelable { .append(" emergencyEnabled=").append(mEmergencyOnly) .append(" supportedServices=").append(mAvailableServices) .append(" cellIdentity=").append(mCellIdentity) + .append(" voiceSpecificStates=").append(mVoiceSpecificStates) + .append(" dataSpecificStates=").append(mDataSpecificStates) .append("}").toString(); } @Override public int hashCode() { return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology, - mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity); + mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity, + mVoiceSpecificStates, mDataSpecificStates); } @Override @@ -228,7 +298,9 @@ public class NetworkRegistrationState implements Parcelable { && mEmergencyOnly == other.mEmergencyOnly && (mAvailableServices == other.mAvailableServices || Arrays.equals(mAvailableServices, other.mAvailableServices)) - && mCellIdentity == other.mCellIdentity; + && mCellIdentity == other.mCellIdentity + && mVoiceSpecificStates == other.mVoiceSpecificStates + && mDataSpecificStates == other.mDataSpecificStates; } @Override @@ -241,6 +313,8 @@ public class NetworkRegistrationState implements Parcelable { dest.writeBoolean(mEmergencyOnly); dest.writeIntArray(mAvailableServices); dest.writeParcelable(mCellIdentity, 0); + dest.writeParcelable(mVoiceSpecificStates, 0); + dest.writeParcelable(mDataSpecificStates, 0); } public static final Parcelable.Creator<NetworkRegistrationState> CREATOR = diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index fc2ef2782b78..778ca77662ab 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -35,15 +35,20 @@ public class SignalStrength implements Parcelable { private static final boolean DBG = false; /** @hide */ - public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; + public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN + = TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0 /** @hide */ - public static final int SIGNAL_STRENGTH_POOR = 1; + public static final int SIGNAL_STRENGTH_POOR + = TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // = 1 /** @hide */ - public static final int SIGNAL_STRENGTH_MODERATE = 2; + public static final int SIGNAL_STRENGTH_MODERATE + = TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // = 2 /** @hide */ - public static final int SIGNAL_STRENGTH_GOOD = 3; + public static final int SIGNAL_STRENGTH_GOOD + = TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // = 3 /** @hide */ - public static final int SIGNAL_STRENGTH_GREAT = 4; + public static final int SIGNAL_STRENGTH_GREAT + = TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // = 4 /** @hide */ public static final int NUM_SIGNAL_STRENGTH_BINS = 5; /** @hide */ diff --git a/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java new file mode 100644 index 000000000000..871ee4d9f0a1 --- /dev/null +++ b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java @@ -0,0 +1,114 @@ +package android.telephony; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Class that stores information specific to voice network registration. + * @hide + */ +public class VoiceSpecificRegistrationStates implements Parcelable{ + /** + * oncurrent services support indicator. if + * registered on a CDMA system. + * false - Concurrent services not supported, + * true - Concurrent services supported + */ + public final boolean cssSupported; + + /** + * TSB-58 Roaming Indicator if registered + * on a CDMA or EVDO system or -1 if not. + * Valid values are 0-255. + */ + public final int roamingIndicator; + + /** + * indicates whether the current system is in the + * PRL if registered on a CDMA or EVDO system or -1 if + * not. 0=not in the PRL, 1=in the PRL + */ + public final int systemIsInPrl; + + /** + * default Roaming Indicator from the PRL, + * if registered on a CDMA or EVDO system or -1 if not. + * Valid values are 0-255. + */ + public final int defaultRoamingIndicator; + + VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl, + int defaultRoamingIndicator) { + this.cssSupported = cssSupported; + this.roamingIndicator = roamingIndicator; + this.systemIsInPrl = systemIsInPrl; + this.defaultRoamingIndicator = defaultRoamingIndicator; + } + + private VoiceSpecificRegistrationStates(Parcel source) { + this.cssSupported = source.readBoolean(); + this.roamingIndicator = source.readInt(); + this.systemIsInPrl = source.readInt(); + this.defaultRoamingIndicator = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(cssSupported); + dest.writeInt(roamingIndicator); + dest.writeInt(systemIsInPrl); + dest.writeInt(defaultRoamingIndicator); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "VoiceSpecificRegistrationStates {" + + " mCssSupported=" + cssSupported + + " mRoamingIndicator=" + roamingIndicator + + " mSystemIsInPrl=" + systemIsInPrl + + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl, + defaultRoamingIndicator); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof VoiceSpecificRegistrationStates)) { + return false; + } + + VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o; + return this.cssSupported == other.cssSupported + && this.roamingIndicator == other.roamingIndicator + && this.systemIsInPrl == other.systemIsInPrl + && this.defaultRoamingIndicator == other.defaultRoamingIndicator; + } + + + public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR = + new Parcelable.Creator<VoiceSpecificRegistrationStates>() { + @Override + public VoiceSpecificRegistrationStates createFromParcel(Parcel source) { + return new VoiceSpecificRegistrationStates(source); + } + + @Override + public VoiceSpecificRegistrationStates[] newArray(int size) { + return new VoiceSpecificRegistrationStates[size]; + } + }; +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index fa19ea069044..c4f9022c31ce 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -86,15 +86,17 @@ public abstract class DataService extends Service { /** The reason of the data request is IWLAN handover */ public static final int REQUEST_REASON_HANDOVER = 3; - private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1; - private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 2; - private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 3; - private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 4; - private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 5; - private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 6; - private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 7; - private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 8; - private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 9; + private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER = 1; + private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER = 2; + private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS = 3; + private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 4; + private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 5; + private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 6; + private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 7; + private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 8; + private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 9; + private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 10; + private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11; private final HandlerThread mHandlerThread; @@ -102,7 +104,7 @@ public abstract class DataService extends Service { private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>(); - private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>(); + private final IBinder mBinder = new IDataServiceWrapper(); /** * The abstract class of the actual data service implementation. The data service provider @@ -321,23 +323,34 @@ public abstract class DataService extends Service { public void handleMessage(Message message) { IDataServiceCallback callback; final int slotId = message.arg1; - DataServiceProvider service; - - synchronized (mServiceMap) { - service = mServiceMap.get(slotId); - } + DataServiceProvider serviceProvider = mServiceMap.get(slotId); switch (message.what) { - case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE: - service = createDataServiceProvider(message.arg1); - if (service != null) { - mServiceMap.put(slotId, service); + case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER: + serviceProvider = createDataServiceProvider(message.arg1); + if (serviceProvider != null) { + mServiceMap.put(slotId, serviceProvider); } break; + case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER: + if (serviceProvider != null) { + serviceProvider.onDestroy(); + mServiceMap.remove(slotId); + } + break; + case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS: + for (int i = 0; i < mServiceMap.size(); i++) { + serviceProvider = mServiceMap.get(i); + if (serviceProvider != null) { + serviceProvider.onDestroy(); + } + } + mServiceMap.clear(); + break; case DATA_SERVICE_REQUEST_SETUP_DATA_CALL: - if (service == null) break; + if (serviceProvider == null) break; SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj; - service.setupDataCall(setupDataCallRequest.accessNetworkType, + serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType, setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, setupDataCallRequest.allowRoaming, setupDataCallRequest.reason, setupDataCallRequest.linkProperties, @@ -345,46 +358,46 @@ public abstract class DataService extends Service { break; case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL: - if (service == null) break; + if (serviceProvider == null) break; DeactivateDataCallRequest deactivateDataCallRequest = (DeactivateDataCallRequest) message.obj; - service.deactivateDataCall(deactivateDataCallRequest.cid, + serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid, deactivateDataCallRequest.reason, new DataServiceCallback(deactivateDataCallRequest.callback)); break; case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN: - if (service == null) break; + if (serviceProvider == null) break; SetInitialAttachApnRequest setInitialAttachApnRequest = (SetInitialAttachApnRequest) message.obj; - service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile, + serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile, setInitialAttachApnRequest.isRoaming, new DataServiceCallback(setInitialAttachApnRequest.callback)); break; case DATA_SERVICE_REQUEST_SET_DATA_PROFILE: - if (service == null) break; + if (serviceProvider == null) break; SetDataProfileRequest setDataProfileRequest = (SetDataProfileRequest) message.obj; - service.setDataProfile(setDataProfileRequest.dps, + serviceProvider.setDataProfile(setDataProfileRequest.dps, setDataProfileRequest.isRoaming, new DataServiceCallback(setDataProfileRequest.callback)); break; case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST: - if (service == null) break; + if (serviceProvider == null) break; - service.getDataCallList(new DataServiceCallback( + serviceProvider.getDataCallList(new DataServiceCallback( (IDataServiceCallback) message.obj)); break; case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED: - if (service == null) break; - service.registerForDataCallListChanged((IDataServiceCallback) message.obj); + if (serviceProvider == null) break; + serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj); break; case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED: - if (service == null) break; + if (serviceProvider == null) break; callback = (IDataServiceCallback) message.obj; - service.unregisterForDataCallListChanged(callback); + serviceProvider.unregisterForDataCallListChanged(callback); break; case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED: - if (service == null) break; + if (serviceProvider == null) break; DataCallListChangedIndication indication = (DataCallListChangedIndication) message.obj; try { @@ -423,67 +436,19 @@ public abstract class DataService extends Service { loge("Unexpected intent " + intent); return null; } - - int slotId = intent.getIntExtra( - DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX); - - if (!SubscriptionManager.isValidSlotIndex(slotId)) { - loge("Invalid slot id " + slotId); - return null; - } - - log("onBind: slot id=" + slotId); - - IDataServiceWrapper binder = mBinderMap.get(slotId); - if (binder == null) { - Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE); - msg.arg1 = slotId; - msg.sendToTarget(); - - binder = new IDataServiceWrapper(slotId); - mBinderMap.put(slotId, binder); - } - - return binder; + return mBinder; } /** @hide */ @Override public boolean onUnbind(Intent intent) { - int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID, - SubscriptionManager.INVALID_SIM_SLOT_INDEX); - if (mBinderMap.get(slotId) != null) { - DataServiceProvider serviceImpl; - synchronized (mServiceMap) { - serviceImpl = mServiceMap.get(slotId); - } - if (serviceImpl != null) { - serviceImpl.onDestroy(); - } - mBinderMap.remove(slotId); - } - - // If all clients unbinds, quit the handler thread - if (mBinderMap.size() == 0) { - mHandlerThread.quit(); - } - + mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget(); return false; } /** @hide */ @Override public void onDestroy() { - synchronized (mServiceMap) { - for (int i = 0; i < mServiceMap.size(); i++) { - DataServiceProvider serviceImpl = mServiceMap.get(i); - if (serviceImpl != null) { - serviceImpl.onDestroy(); - } - } - mServiceMap.clear(); - } - mHandlerThread.quit(); } @@ -491,68 +456,74 @@ public abstract class DataService extends Service { * A wrapper around IDataService that forwards calls to implementations of {@link DataService}. */ private class IDataServiceWrapper extends IDataService.Stub { + @Override + public void createDataServiceProvider(int slotId) { + mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0) + .sendToTarget(); + } - private final int mSlotId; - - IDataServiceWrapper(int slotId) { - mSlotId = slotId; + @Override + public void removeDataServiceProvider(int slotId) { + mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0) + .sendToTarget(); } @Override - public void setupDataCall(int accessNetworkType, DataProfile dataProfile, + public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, IDataServiceCallback callback) { - mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0, + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0, new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, linkProperties, callback)) .sendToTarget(); } @Override - public void deactivateDataCall(int cid, int reason, IDataServiceCallback callback) { - mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0, + public void deactivateDataCall(int slotId, int cid, int reason, + IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0, new DeactivateDataCallRequest(cid, reason, callback)) .sendToTarget(); } @Override - public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, + public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback) { - mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0, + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0, new SetInitialAttachApnRequest(dataProfile, isRoaming, callback)) .sendToTarget(); } @Override - public void setDataProfile(List<DataProfile> dps, boolean isRoaming, + public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback) { - mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0, + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0, new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget(); } @Override - public void getDataCallList(IDataServiceCallback callback) { - mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0, + public void getDataCallList(int slotId, IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0, callback).sendToTarget(); } @Override - public void registerForDataCallListChanged(IDataServiceCallback callback) { + public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) { if (callback == null) { loge("Callback is null"); return; } - mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId, + mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId, 0, callback).sendToTarget(); } @Override - public void unregisterForDataCallListChanged(IDataServiceCallback callback) { + public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) { if (callback == null) { loge("Callback is null"); return; } - mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId, + mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId, 0, callback).sendToTarget(); } } diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 07720b69e45f..d4d9be85ffa8 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -25,14 +25,17 @@ import android.telephony.data.IDataServiceCallback; */ oneway interface IDataService { - void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming, + void createDataServiceProvider(int slotId); + void removeDataServiceProvider(int slotId); + void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, IDataServiceCallback callback); - void deactivateDataCall(int cid, int reason, IDataServiceCallback callback); - void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming, + void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); + void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback); - void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback); - void getDataCallList(IDataServiceCallback callback); - void registerForDataCallListChanged(IDataServiceCallback callback); - void unregisterForDataCallListChanged(IDataServiceCallback callback); + void setDataProfile(int slotId, in List<DataProfile> dps, boolean isRoaming, + IDataServiceCallback callback); + void getDataCallList(int slotId, IDataServiceCallback callback); + void registerForDataCallListChanged(int slotId, IDataServiceCallback callback); + void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback); } diff --git a/test-mock/Android.mk b/test-mock/Android.mk index 7926a77ccb01..5c586c7101a1 100644 --- a/test-mock/Android.mk +++ b/test-mock/Android.mk @@ -31,7 +31,7 @@ ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(android_test_mock_source_files) -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework +LOCAL_JAVA_LIBRARIES := core-oj core-libart framework conscrypt okhttp bouncycastle LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock @@ -116,7 +116,7 @@ update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(android_test_mock_source_files) -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework +LOCAL_JAVA_LIBRARIES := core-oj core-libart framework conscrypt okhttp bouncycastle LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml index d62ef9ec210c..2591aaf8f1a6 100644 --- a/tests/FrameworkPerf/AndroidManifest.xml +++ b/tests/FrameworkPerf/AndroidManifest.xml @@ -1,6 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworkperf"> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-sdk android:minSdkVersion="5" /> diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml index 8697f1b085bf..c6824ecea976 100644 --- a/tests/OneMedia/AndroidManifest.xml +++ b/tests/OneMedia/AndroidManifest.xml @@ -5,7 +5,6 @@ android:versionName="1.0" > <uses-sdk android:minSdkVersion="19"/> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> diff --git a/tests/libs-permissions/Android.mk b/tests/libs-permissions/Android.mk new file mode 100644 index 000000000000..eb3862390338 --- /dev/null +++ b/tests/libs-permissions/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := com.android.test.libs.product +LOCAL_PRODUCT_MODULE := true +LOCAL_SRC_FILES := $(call all-java-files-under, product/java) +LOCAL_REQUIRED_MODULES := com.android.test.libs.product.xml +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := com.android.test.libs.product.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions +LOCAL_SRC_FILES:= product/com.android.test.libs.product.xml +include $(BUILD_PREBUILT) diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/tests/libs-permissions/product/com.android.test.libs.product.xml index 045ede454bca..0a955e9df7fd 100644 --- a/libs/androidfw/tests/data/basic/res/layout/layout.xml +++ b/tests/libs-permissions/product/com.android.test.libs.product.xml @@ -4,22 +4,17 @@ 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. --> -<Button xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/ok" - android:layout_width="0sp" - android:layout_height="fill_parent" - android:layout_weight="1" - android:layout_marginStart="2dip" - android:layout_marginEnd="2dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textStyle="bold" - android:text="@android:string/ok" />
\ No newline at end of file + +<permissions> + <library name="com.android.test.libs.product" + file="/product/framework/com.android.test.libs.product.jar" /> +</permissions> diff --git a/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java new file mode 100644 index 000000000000..f49b46e72c25 --- /dev/null +++ b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.libs.product; + +/** + * Test class for product libs. + */ +public class LibsProductTest { + + /** + * Dummpy method for testing. + */ + public static void test() { + } +} diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk index b001c8c466a9..3c80ad8587af 100644 --- a/tests/privapp-permissions/Android.mk +++ b/tests/privapp-permissions/Android.mk @@ -29,3 +29,17 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/permissions LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest +LOCAL_PRIVILEGED_MODULE := true +LOCAL_MANIFEST_FILE := product/AndroidManifest.xml +LOCAL_PRODUCT_MODULE := true +LOCAL_REQUIRED_MODULES := productprivapp-permissions-test.xml +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) +LOCAL_MODULE := productprivapp-permissions-test.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions +LOCAL_SRC_FILES:= product/privapp-permissions-test.xml +include $(BUILD_PREBUILT) diff --git a/tests/privapp-permissions/product/AndroidManifest.xml b/tests/privapp-permissions/product/AndroidManifest.xml new file mode 100644 index 000000000000..3d9415c3df41 --- /dev/null +++ b/tests/privapp-permissions/product/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2018 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.permission.privapp.tests.product"> + + <!-- MANAGE_USB is signature|privileged --> + <uses-permission android:name="android.permission.MANAGE_USB"/> +</manifest> diff --git a/tests/privapp-permissions/product/privapp-permissions-test.xml b/tests/privapp-permissions/product/privapp-permissions-test.xml new file mode 100644 index 000000000000..f298f9da41b7 --- /dev/null +++ b/tests/privapp-permissions/product/privapp-permissions-test.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <privapp-permissions package="com.android.framework.permission.privapp.tests.product"> + <permission name="android.permission.MANAGE_USB"/> + </privapp-permissions> +</permissions> diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp index bd1b973c7bdf..302d7395f3e4 100644 --- a/tools/incident_report/main.cpp +++ b/tools/incident_report/main.cpp @@ -452,9 +452,10 @@ main(int argc, char** argv) bool adbIncidentWorkaround = true; pid_t childPid = -1; vector<string> sections; + const char* privacy = NULL; int opt; - while ((opt = getopt(argc, argv, "bhi:o:s:tw")) != -1) { + while ((opt = getopt(argc, argv, "bhi:o:s:twp:")) != -1) { switch (opt) { case 'b': outputFormat = OUTPUT_PROTO; @@ -477,6 +478,9 @@ main(int argc, char** argv) case 'w': adbIncidentWorkaround = false; break; + case 'p': + privacy = optarg; + break; default: usage(stderr); return 1; @@ -526,7 +530,7 @@ main(int argc, char** argv) } // TODO: This is what the real implementation will be... - char const** args = (char const**)malloc(sizeof(char*) * (6 + sections.size())); + char const** args = (char const**)malloc(sizeof(char*) * (8 + sections.size())); int argpos = 0; args[argpos++] = "adb"; if (adbSerial != NULL) { @@ -535,6 +539,10 @@ main(int argc, char** argv) } args[argpos++] = "shell"; args[argpos++] = "incident"; + if (privacy != NULL) { + args[argpos++] = "-p"; + args[argpos++] = privacy; + } for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) { args[argpos++] = it->c_str(); } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 101b3e20690f..2a2ff0cf3a6f 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -108,8 +108,6 @@ interface IWifiManager boolean isDualBandSupported(); - boolean saveConfiguration(String packageName); - DhcpInfo getDhcpInfo(); boolean isScanAlwaysAvailable(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 05dcb335618d..b4885c5dd25d 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1693,18 +1693,14 @@ public class WifiManager { * existing networks. You should assume the network IDs can be different * after calling this method. * - * @return {@code true} if the operation succeeded + * @return {@code false} Will always return true. * @deprecated There is no need to call this method - * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)} * and {@link #removeNetwork(int)} already persist the configurations automatically. */ @Deprecated public boolean saveConfiguration() { - try { - return mService.saveConfiguration(mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return true; } /** |