diff options
455 files changed, 11801 insertions, 4392 deletions
diff --git a/Android.bp b/Android.bp index 70b1fa0e793f..948b0a068215 100644 --- a/Android.bp +++ b/Android.bp @@ -545,17 +545,11 @@ java_library { "core/java/android/net/EventLogTags.logtags", "core/java/android/webkit/EventLogTags.logtags", "core/java/com/android/internal/logging/EventLogTags.logtags", + "core/java/com/android/server/DropboxLogTags.logtags", + "core/java/org/chromium/arc/EventLogTags.logtags", ":framework-statslog-gen", ], - logtags: [ - "core/java/android/app/admin/SecurityLogTags.logtags", - "core/java/android/content/EventLogTags.logtags", - "core/java/android/speech/tts/EventLogTags.logtags", - "core/java/android/net/EventLogTags.logtags", - "core/java/android/webkit/EventLogTags.logtags", - "core/java/com/android/internal/logging/EventLogTags.logtags", - ], aidl: { local_include_dirs: [ diff --git a/Android.mk b/Android.mk index 7ca8358c637a..9676958605dd 100644 --- a/Android.mk +++ b/Android.mk @@ -486,6 +486,10 @@ LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_F include $(BUILD_APIDIFF) +# Hack to get diffs included in docs output +out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip +$(out_zip): $(full_target) + # ==== System API diff =========================== include $(CLEAR_VARS) @@ -512,6 +516,10 @@ LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTE include $(BUILD_APIDIFF) +# Hack to get diffs included in docs output +out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip +$(out_zip): $(full_target) + # ==== the api stubs and current.xml =========================== include $(CLEAR_VARS) diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk index f08b4025f95e..75cb229e36d6 100644 --- a/apct-tests/perftests/core/Android.mk +++ b/apct-tests/perftests/core/Android.mk @@ -11,8 +11,9 @@ LOCAL_SRC_FILES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ apct-perftests-utils \ - guava \ - legacy-android-test + guava + +LOCAL_JAVA_LIBRARIES := android.test.base LOCAL_PACKAGE_NAME := CorePerfTests diff --git a/api/current.txt b/api/current.txt index f56f1a16b966..05b2a0c6a4fb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3952,6 +3952,7 @@ package android.app { field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR; field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190 field public static final int IMPORTANCE_CACHED = 400; // 0x190 + field public static final int IMPORTANCE_CANT_SAVE_STATE = 270; // 0x10e field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4 field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64 field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d @@ -6343,6 +6344,7 @@ package android.app.admin { method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String); method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); + method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); method public int getLockTaskFeatures(android.content.ComponentName); method public java.lang.String[] getLockTaskPackages(android.content.ComponentName); @@ -6383,6 +6385,7 @@ package android.app.admin { method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(android.content.ComponentName, int); method public boolean installCaCert(android.content.ComponentName, byte[]); + method public boolean installExistingPackage(android.content.ComponentName, java.lang.String); method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String); method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean); method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean); @@ -6393,7 +6396,7 @@ package android.app.admin { method public deprecated boolean isCallerApplicationRestrictionsManagingPackage(); method public boolean isDeviceOwnerApp(java.lang.String); method public boolean isLockTaskPermitted(java.lang.String); - method public boolean isLogoutButtonEnabled(); + method public boolean isLogoutEnabled(); method public boolean isManagedProfile(android.content.ComponentName); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isNetworkLoggingEnabled(android.content.ComponentName); @@ -6433,11 +6436,12 @@ package android.app.admin { method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>); method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); + method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); method public void setLockTaskFeatures(android.content.ComponentName, int); method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException; - method public void setLogoutButtonEnabled(android.content.ComponentName, boolean); + method public void setLogoutEnabled(android.content.ComponentName, boolean); method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence); method public void setMasterVolumeMuted(android.content.ComponentName, boolean); method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int); @@ -6502,6 +6506,8 @@ package android.app.admin { field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall"; field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install"; field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app"; + field public static final java.lang.String DELEGATION_INSTALL_EXISTING_PACKAGE = "delegation-install-existing-package"; + field public static final java.lang.String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages"; field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access"; field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant"; field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2 @@ -7005,11 +7011,14 @@ package android.app.slice { field public static final java.lang.String HINT_LARGE = "large"; field public static final java.lang.String HINT_LIST = "list"; field public static final java.lang.String HINT_LIST_ITEM = "list_item"; + field public static final java.lang.String HINT_MAX = "max"; field public static final java.lang.String HINT_NO_TINT = "no_tint"; field public static final java.lang.String HINT_PARTIAL = "partial"; field public static final java.lang.String HINT_SELECTED = "selected"; field public static final java.lang.String HINT_TITLE = "title"; + field public static final java.lang.String SUBTYPE_COLOR = "color"; field public static final java.lang.String SUBTYPE_MESSAGE = "message"; + field public static final java.lang.String SUBTYPE_SLIDER = "slider"; field public static final java.lang.String SUBTYPE_SOURCE = "source"; } @@ -7018,12 +7027,14 @@ package android.app.slice { ctor public Slice.Builder(android.app.slice.Slice.Builder); method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice); method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String); - method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...); - method public android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>); + method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...); + method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>); method public android.app.slice.Slice.Builder addHints(java.lang.String...); method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>); method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.lang.String...); + method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.util.List<java.lang.String>); method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>); method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice); @@ -7039,10 +7050,11 @@ package android.app.slice { public final class SliceItem implements android.os.Parcelable { method public int describeContents(); method public android.app.PendingIntent getAction(); - method public int getColor(); + method public deprecated int getColor(); method public java.lang.String getFormat(); method public java.util.List<java.lang.String> getHints(); method public android.graphics.drawable.Icon getIcon(); + method public int getInt(); method public android.app.RemoteInput getRemoteInput(); method public android.app.slice.Slice getSlice(); method public java.lang.String getSubType(); @@ -7052,8 +7064,9 @@ package android.app.slice { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR; field public static final java.lang.String FORMAT_ACTION = "action"; - field public static final java.lang.String FORMAT_COLOR = "color"; + field public static final deprecated java.lang.String FORMAT_COLOR = "color"; field public static final java.lang.String FORMAT_IMAGE = "image"; + field public static final java.lang.String FORMAT_INT = "int"; field public static final java.lang.String FORMAT_REMOTE_INPUT = "input"; field public static final java.lang.String FORMAT_SLICE = "slice"; field public static final java.lang.String FORMAT_TEXT = "text"; @@ -20558,6 +20571,7 @@ package android.inputmethodservice { method public int getMaxWidth(); method public java.lang.CharSequence getTextForImeAction(int); method public android.app.Dialog getWindow(); + method public void hideSoftInputFromInputMethod(int); method public void hideStatusIcon(); method public void hideWindow(); method public boolean isExtractViewShown(); @@ -20613,10 +20627,16 @@ package android.inputmethodservice { method public void setCandidatesViewShown(boolean); method public void setExtractView(android.view.View); method public void setExtractViewShown(boolean); + method public void setInputMethod(java.lang.String); + method public void setInputMethodAndSubtype(java.lang.String, android.view.inputmethod.InputMethodSubtype); method public void setInputView(android.view.View); + method public boolean shouldOfferSwitchingToNextInputMethod(); + method public void showSoftInputFromInputMethod(int); method public void showStatusIcon(int); method public void showWindow(boolean); method public void switchInputMethod(java.lang.String); + method public boolean switchToLastInputMethod(); + method public boolean switchToNextInputMethod(boolean); method public void updateFullscreenMode(); method public void updateInputViewShown(); field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0 @@ -49034,10 +49054,10 @@ package android.view.inputmethod { method public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodList(); method public android.view.inputmethod.InputMethodSubtype getLastInputMethodSubtype(); method public java.util.Map<android.view.inputmethod.InputMethodInfo, java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes(); - method public void hideSoftInputFromInputMethod(android.os.IBinder, int); + method public deprecated void hideSoftInputFromInputMethod(android.os.IBinder, int); method public boolean hideSoftInputFromWindow(android.os.IBinder, int); method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver); - method public void hideStatusIcon(android.os.IBinder); + method public deprecated void hideStatusIcon(android.os.IBinder); method public boolean isAcceptingText(); method public boolean isActive(android.view.View); method public boolean isActive(); @@ -49047,17 +49067,17 @@ package android.view.inputmethod { method public void sendAppPrivateCommand(android.view.View, java.lang.String, android.os.Bundle); method public void setAdditionalInputMethodSubtypes(java.lang.String, android.view.inputmethod.InputMethodSubtype[]); method public boolean setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype); - method public void setInputMethod(android.os.IBinder, java.lang.String); - method public void setInputMethodAndSubtype(android.os.IBinder, java.lang.String, android.view.inputmethod.InputMethodSubtype); - method public boolean shouldOfferSwitchingToNextInputMethod(android.os.IBinder); + method public deprecated void setInputMethod(android.os.IBinder, java.lang.String); + method public deprecated void setInputMethodAndSubtype(android.os.IBinder, java.lang.String, android.view.inputmethod.InputMethodSubtype); + method public deprecated boolean shouldOfferSwitchingToNextInputMethod(android.os.IBinder); method public void showInputMethodAndSubtypeEnabler(java.lang.String); method public void showInputMethodPicker(); method public boolean showSoftInput(android.view.View, int); method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver); - method public void showSoftInputFromInputMethod(android.os.IBinder, int); - method public void showStatusIcon(android.os.IBinder, java.lang.String, int); - method public boolean switchToLastInputMethod(android.os.IBinder); - method public boolean switchToNextInputMethod(android.os.IBinder, boolean); + method public deprecated void showSoftInputFromInputMethod(android.os.IBinder, int); + method public deprecated void showStatusIcon(android.os.IBinder, java.lang.String, int); + method public deprecated boolean switchToLastInputMethod(android.os.IBinder); + method public deprecated boolean switchToNextInputMethod(android.os.IBinder, boolean); method public void toggleSoftInput(int, int); method public void toggleSoftInputFromWindow(android.os.IBinder, int, int); method public deprecated void updateCursor(android.view.View, int, int, int, int); diff --git a/api/system-current.txt b/api/system-current.txt index 33fa246a9f51..15f070a5d5dd 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4136,6 +4136,7 @@ package android.telephony { method public int getCurrentPhoneType(int); method public deprecated boolean getDataEnabled(); method public deprecated boolean getDataEnabled(int); + method public boolean getEmergencyCallbackMode(); method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method public android.os.Bundle getVisualVoicemailSettings(); method public boolean handlePinMmi(java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 3c3521feff95..27e585d0a828 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -162,6 +162,10 @@ package android.content.pm { method public abstract boolean isPermissionReviewModeEnabled(); } + public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 + } + public final class ShortcutInfo implements android.os.Parcelable { method public boolean isVisibleToPublisher(); } diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index db4f586c7e31..4bf956a9a03d 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -297,3 +297,82 @@ Table::insertField(ProtoOutputStream* proto, const std::string& name, const std: } return true; } + +// ================================================================================ +Message::Message(Table* table) + :mTable(table), + mPreviousField(""), + mTokens(), + mSubMessages() +{ +} + +Message::~Message() +{ +} + +void +Message::addSubMessage(uint64_t fieldId, Message* fieldMsg) +{ + for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) { + if (iter->second == fieldId) { + mSubMessages[iter->first] = fieldMsg; + return; + } + } +} + +bool +Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value) +{ + // If the field name can be found, it means the name is a primitive field. + if (mTable->mFields.find(name) != mTable->mFields.end()) { + endSession(proto); + // The only edge case is for example ro.hardware itself is a message, so a field called "value" + // would be defined in proto Ro::Hardware and it must be the first field. + if (mSubMessages.find(name) != mSubMessages.end()) { + startSession(proto, name); + return mSubMessages[name]->insertField(proto, "value", value); + } else { + return mTable->insertField(proto, name, value); + } + } + + // Try to find the message field which is the prefix of name, so the value would be inserted + // recursively into the submessage. + string mutableName = name; + for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) { + string fieldName = iter->first; + string prefix = fieldName + "_"; // underscore is the delimiter in the name + if (stripPrefix(&mutableName, prefix.c_str())) { + if (mPreviousField != fieldName) { + endSession(proto); + startSession(proto, fieldName); + } + return mSubMessages[fieldName]->insertField(proto, mutableName, value); + } + } + // Can't find the name in proto definition, handle it separately. + return false; +} + +void +Message::startSession(ProtoOutputStream* proto, const string& name) +{ + uint64_t fieldId = mTable->mFields[name]; + long long token = proto->start(fieldId); + mPreviousField = name; + mTokens.push(token); +} + +void +Message::endSession(ProtoOutputStream* proto) +{ + if (mPreviousField == "") return; + if (mSubMessages.find(mPreviousField) != mSubMessages.end()) { + mSubMessages[mPreviousField]->endSession(proto); + } + proto->end(mTokens.top()); + mTokens.pop(); + mPreviousField = ""; +} diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index 4a5fe1dd7a42..58ef29044048 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -18,6 +18,7 @@ #define INCIDENT_HELPER_UTIL_H #include <map> +#include <stack> #include <string> #include <vector> @@ -34,7 +35,7 @@ const std::string DEFAULT_NEWLINE = "\r\n"; const std::string TAB_DELIMITER = "\t"; const std::string COMMA_DELIMITER = ","; -// returns true if c is a-zA-Z0-9 or underscore _ +// returns true if c is a-zA-Z0-9 or underscore bool isValidChar(char c); // trim the string with the given charset @@ -102,11 +103,20 @@ private: }; /** - * The class contains a mapping between table headers to its field ids. - * And allow users to insert the field values to proto based on its header name. + * The Table class is constructed from two arrays generated by the given message with + * option (stream_proto.stream_msg).enable_fields_mapping = true. + * The names are each field's names in the message and must corresponding to the header/name of + * the text to be parsed, and the ids are the streaming proto encoded field ids. + * + * This class then allows users to insert the table values to proto based on its header. + * + * Advance feature: if some fields in the message are enums, user must explicitly add the + * mapping from enum name string to its enum values. */ +class Message; class Table { +friend class Message; public: Table(const char* names[], const uint64_t ids[], const int count); ~Table(); @@ -114,9 +124,12 @@ public: // Add enum names to values for parsing purpose. void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize); - // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts + // Manually add enum names to values mapping, useful when an Enum type is used by + // a number of fields, there must not be any enum name conflicts. void addEnumNameToValue(const char* enumName, const int enumValue); + // Based on given name, find the right field id, parse the text value and insert to proto. + // Return false if the given name can't be found. bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); private: map<std::string, uint64_t> mFields; @@ -124,4 +137,47 @@ private: map<std::string, int> mEnumValuesByName; }; +/** + * Reconstructs a typical proto message given its message Table, adds submessage fields explicitly. + * It allows user to insert nested proto values purely by the names. See insertField for detail. + */ +class Message +{ +public: + Message(Table* table); + ~Message(); + + // Reconstructs the typical proto message by adding its message fields. + void addSubMessage(uint64_t fieldId, Message* fieldMsg); + + // Inserts value if the given name has the corresponding field in its message and return true. + // It will recursively search the name in submessages and find the correct field to insert. + // For example, when the name is dalvik_vm_heapsize, and the message's corresponding proto is: + // message Properties { + // message DalvikVm { + // int32 heapsize = 1; + // bool usejit = 2; + // } + // DalvikVm dalvik_vm = 1; + // string hack_in = 2; + // } + // The value will be inserted into field heapsize in dalvik_vm submessage. + // + // Also value belongs to same submessage MUST be inserted contiguously. + // For example, dalvik_vm_usejit must be inserted directly after dalvik_vm_heapsize, otherwise + // if hack_in attempts to be inserted before dalvik_vm_usejit, value of usejit isn't added as expected. + bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); + + // Starts a new message field proto session. + void startSession(ProtoOutputStream* proto, const string& name); + + // Ends the previous message field proto session. + void endSession(ProtoOutputStream* proto); +private: + Table* mTable; + std::string mPreviousField; + stack<long long> mTokens; + map<std::string, Message*> mSubMessages; +}; + #endif // INCIDENT_HELPER_UTIL_H diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp index ee5feb03242e..a41ed6ebaa22 100644 --- a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp @@ -45,11 +45,119 @@ SystemPropertiesParser::Parse(const int in, const int out) const string line; string name; // the name of the property string value; // the string value of the property - ProtoOutputStream proto; - Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT); - table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING); - table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED); + vector<pair<string, string>> extras; + + Table sysPropTable(SystemPropertiesProto::_FIELD_NAMES, + SystemPropertiesProto::_FIELD_IDS, + SystemPropertiesProto::_FIELD_COUNT); + Message sysProp(&sysPropTable); + + Table aaudioT(SystemPropertiesProto::Aaudio::_FIELD_NAMES, + SystemPropertiesProto::Aaudio::_FIELD_IDS, + SystemPropertiesProto::Aaudio::_FIELD_COUNT); + Message aaudio(&aaudioT); + sysProp.addSubMessage(SystemPropertiesProto::AAUDIO, &aaudio); + + Table cameraTable(SystemPropertiesProto::Camera::_FIELD_NAMES, + SystemPropertiesProto::Camera::_FIELD_IDS, + SystemPropertiesProto::Camera::_FIELD_COUNT); + Message camera(&cameraTable); + sysProp.addSubMessage(SystemPropertiesProto::CAMERA, &camera); + + Table dalvikVmTable(SystemPropertiesProto::DalvikVm::_FIELD_NAMES, + SystemPropertiesProto::DalvikVm::_FIELD_IDS, + SystemPropertiesProto::DalvikVm::_FIELD_COUNT); + Message dalvikVm(&dalvikVmTable); + sysProp.addSubMessage(SystemPropertiesProto::DALVIK_VM, &dalvikVm); + + Table initSvcTable(SystemPropertiesProto::InitSvc::_FIELD_NAMES, + SystemPropertiesProto::InitSvc::_FIELD_IDS, + SystemPropertiesProto::InitSvc::_FIELD_COUNT); + initSvcTable.addEnumNameToValue("running", SystemPropertiesProto::InitSvc::STATUS_RUNNING); + initSvcTable.addEnumNameToValue("stopped", SystemPropertiesProto::InitSvc::STATUS_STOPPED); + Message initSvc(&initSvcTable); + sysProp.addSubMessage(SystemPropertiesProto::INIT_SVC, &initSvc); + + Table logTable(SystemPropertiesProto::Log::_FIELD_NAMES, + SystemPropertiesProto::Log::_FIELD_IDS, + SystemPropertiesProto::Log::_FIELD_COUNT); + Message logMsg(&logTable); + sysProp.addSubMessage(SystemPropertiesProto::LOG, &logMsg); + + Table persistTable(SystemPropertiesProto::Persist::_FIELD_NAMES, + SystemPropertiesProto::Persist::_FIELD_IDS, + SystemPropertiesProto::Persist::_FIELD_COUNT); + Message persist(&persistTable); + sysProp.addSubMessage(SystemPropertiesProto::PERSIST, &persist); + + Table pmDexoptTable(SystemPropertiesProto::PmDexopt::_FIELD_NAMES, + SystemPropertiesProto::PmDexopt::_FIELD_IDS, + SystemPropertiesProto::PmDexopt::_FIELD_COUNT); + Message pmDexopt(&pmDexoptTable); + sysProp.addSubMessage(SystemPropertiesProto::PM_DEXOPT, &pmDexopt); + + Table roTable(SystemPropertiesProto::Ro::_FIELD_NAMES, + SystemPropertiesProto::Ro::_FIELD_IDS, + SystemPropertiesProto::Ro::_FIELD_COUNT); + Message ro(&roTable); + + Table bootTable(SystemPropertiesProto::Ro::Boot::_FIELD_NAMES, + SystemPropertiesProto::Ro::Boot::_FIELD_IDS, + SystemPropertiesProto::Ro::Boot::_FIELD_COUNT); + Message boot(&bootTable); + ro.addSubMessage(SystemPropertiesProto::Ro::BOOT, &boot); + + Table bootimageTable(SystemPropertiesProto::Ro::BootImage::_FIELD_NAMES, + SystemPropertiesProto::Ro::BootImage::_FIELD_IDS, + SystemPropertiesProto::Ro::BootImage::_FIELD_COUNT); + Message bootimage(&bootimageTable); + ro.addSubMessage(SystemPropertiesProto::Ro::BOOTIMAGE, &bootimage); + + Table buildTable(SystemPropertiesProto::Ro::Build::_FIELD_NAMES, + SystemPropertiesProto::Ro::Build::_FIELD_IDS, + SystemPropertiesProto::Ro::Build::_FIELD_COUNT); + Message build(&buildTable); + + Table versionTable(SystemPropertiesProto::Ro::Build::Version::_FIELD_NAMES, + SystemPropertiesProto::Ro::Build::Version::_FIELD_IDS, + SystemPropertiesProto::Ro::Build::Version::_FIELD_COUNT); + Message version(&versionTable); + build.addSubMessage(SystemPropertiesProto::Ro::Build::VERSION, &version); + ro.addSubMessage(SystemPropertiesProto::Ro::BUILD, &build); + + Table configTable(SystemPropertiesProto::Ro::Config::_FIELD_NAMES, + SystemPropertiesProto::Ro::Config::_FIELD_IDS, + SystemPropertiesProto::Ro::Config::_FIELD_COUNT); + Message config(&configTable); + ro.addSubMessage(SystemPropertiesProto::Ro::CONFIG, &config); + + Table hardwareTable(SystemPropertiesProto::Ro::Hardware::_FIELD_NAMES, + SystemPropertiesProto::Ro::Hardware::_FIELD_IDS, + SystemPropertiesProto::Ro::Hardware::_FIELD_COUNT); + Message hardware(&hardwareTable); + ro.addSubMessage(SystemPropertiesProto::Ro::HARDWARE, &hardware); + + Table productTable(SystemPropertiesProto::Ro::Product::_FIELD_NAMES, + SystemPropertiesProto::Ro::Product::_FIELD_IDS, + SystemPropertiesProto::Ro::Product::_FIELD_COUNT); + Message product(&productTable); + ro.addSubMessage(SystemPropertiesProto::Ro::PRODUCT, &product); + + sysProp.addSubMessage(SystemPropertiesProto::RO, &ro); + + Table sysTable(SystemPropertiesProto::Sys::_FIELD_NAMES, + SystemPropertiesProto::Sys::_FIELD_IDS, + SystemPropertiesProto::Sys::_FIELD_COUNT); + Message sys(&sysTable); + + Table usbTable(SystemPropertiesProto::Sys::Usb::_FIELD_NAMES, + SystemPropertiesProto::Sys::Usb::_FIELD_IDS, + SystemPropertiesProto::Sys::Usb::_FIELD_COUNT); + Message usb(&usbTable); + sys.addSubMessage(SystemPropertiesProto::Sys::USB, &usb); + + sysProp.addSubMessage(SystemPropertiesProto::SYS, &sys); // parse line by line while (reader.readLine(&line)) { @@ -67,13 +175,19 @@ SystemPropertiesParser::Parse(const int in, const int out) const // if the property name couldn't be found in proto definition or the value has mistype, // add to extra properties with its name and value - if (!table.insertField(&proto, convertToFieldName(name), value)) { - long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); - proto.write(SystemPropertiesProto::Property::NAME, name); - proto.write(SystemPropertiesProto::Property::VALUE, value); - proto.end(token); + if (!sysProp.insertField(&proto, convertToFieldName(name), value)) { + extras.push_back(make_pair(name, value)); } } + // end session for the last write. + sysProp.endSession(&proto); + + for (auto it = extras.begin(); it != extras.end(); it++) { + long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); + proto.write(SystemPropertiesProto::Property::NAME, it->first); + proto.write(SystemPropertiesProto::Property::VALUE, it->second); + proto.end(token); + } if (!reader.ok(&line)) { fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); diff --git a/cmds/incident_helper/testdata/system_properties.txt b/cmds/incident_helper/testdata/system_properties.txt index 57c07ee9d75e..51a2dfd96869 100644 --- a/cmds/incident_helper/testdata/system_properties.txt +++ b/cmds/incident_helper/testdata/system_properties.txt @@ -1,14 +1,13 @@ [aaudio.hw_burst_min_usec]: [2000] [aaudio.mmap_exclusive_policy]: [2] [dalvik.vm.appimageformat]: [lz4] -[gsm.operator.isroaming]: [false] -[init.svc.vendor.imsqmidaemon]: [running] -[init.svc.vendor.init-radio-sh]: [stopped] -[net.dns1]: [2001:4860:4860::8844] -[net.tcp.buffersize.wifi]: [524288,2097152,4194304,262144,524288,1048576] -[nfc.initialized]: [True] -[persist_radio_VT_ENABLE]: [1] +[drm_64bit_enabled]: [false] +[init.svc.adbd]: [running] +[init.svc.lmkd]: [stopped] +[media_mediadrmservice_enable]: [True] [ro.boot.boottime]: [1BLL:85,1BLE:898,2BLL:0,2BLE:862,SW:6739,KL:340] [ro.bootimage.build.date.utc]: [1509394807] [ro.bootimage.build.fingerprint]: [google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys] -[ro.wifi.channels]: []
\ No newline at end of file +[ro.hardware]: [marlin] +[ro.hardware.power]: [marlin-profile] +[ro.wifi.channels]: [] diff --git a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp index 23e292a512b9..98838e98d796 100644 --- a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp +++ b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp @@ -61,29 +61,38 @@ TEST_F(SystemPropertiesParserTest, HasSwapInfo) { SystemPropertiesParser parser; SystemPropertiesProto expected; - expected.set_aaudio_hw_burst_min_usec(2000); - expected.set_aaudio_mmap_exclusive_policy(2); - expected.set_dalvik_vm_appimageformat("lz4"); - expected.set_gsm_operator_isroaming(false); - expected.set_init_svc_vendor_imsqmidaemon(SystemPropertiesProto_Status_STATUS_RUNNING); - expected.set_init_svc_vendor_init_radio_sh(SystemPropertiesProto_Status_STATUS_STOPPED); - expected.set_net_dns1("2001:4860:4860::8844"); - expected.add_net_tcp_buffersize_wifi(524288); - expected.add_net_tcp_buffersize_wifi(2097152); - expected.add_net_tcp_buffersize_wifi(4194304); - expected.add_net_tcp_buffersize_wifi(262144); - expected.add_net_tcp_buffersize_wifi(524288); - expected.add_net_tcp_buffersize_wifi(1048576); - expected.set_nfc_initialized(true); - expected.set_persist_radio_vt_enable(1); - expected.add_ro_boot_boottime("1BLL:85"); - expected.add_ro_boot_boottime("1BLE:898"); - expected.add_ro_boot_boottime("2BLL:0"); - expected.add_ro_boot_boottime("2BLE:862"); - expected.add_ro_boot_boottime("SW:6739"); - expected.add_ro_boot_boottime("KL:340"); - expected.set_ro_bootimage_build_date_utc(1509394807LL); - expected.set_ro_bootimage_build_fingerprint("google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys"); + SystemPropertiesProto::Aaudio* aaudio = expected.mutable_aaudio(); + aaudio->set_hw_burst_min_usec(2000); + aaudio->set_mmap_exclusive_policy(2); + + SystemPropertiesProto::DalvikVm* dalvikVm = expected.mutable_dalvik_vm(); + dalvikVm->set_appimageformat("lz4"); + + expected.set_drm_64bit_enabled(false); + + SystemPropertiesProto::InitSvc* initSvc = expected.mutable_init_svc(); + initSvc->set_adbd(SystemPropertiesProto_InitSvc_Status_STATUS_RUNNING); + initSvc->set_lmkd(SystemPropertiesProto_InitSvc_Status_STATUS_STOPPED); + + expected.set_media_mediadrmservice_enable(true); + + SystemPropertiesProto::Ro* ro = expected.mutable_ro(); + + SystemPropertiesProto::Ro::Boot* boot = ro->mutable_boot(); + boot->add_boottime("1BLL:85"); + boot->add_boottime("1BLE:898"); + boot->add_boottime("2BLL:0"); + boot->add_boottime("2BLE:862"); + boot->add_boottime("SW:6739"); + boot->add_boottime("KL:340"); + + SystemPropertiesProto::Ro::BootImage* bootimage = ro->mutable_bootimage(); + bootimage->set_build_date_utc(1509394807LL); + bootimage->set_build_fingerprint("google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys"); + + SystemPropertiesProto::Ro::Hardware* hardware = ro->mutable_hardware(); + hardware->set_value("marlin"); + hardware->set_power("marlin-profile"); int fd = open(testFile.c_str(), O_RDONLY); ASSERT_TRUE(fd != -1); diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 87cde032ba79..337aeaaf37ab 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -161,6 +161,7 @@ LOCAL_SRC_FILES := \ tests/LogEntryMatcher_test.cpp \ tests/LogReader_test.cpp \ tests/MetricsManager_test.cpp \ + tests/StatsLogProcessor_test.cpp \ tests/UidMap_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ tests/condition/SimpleConditionTracker_test.cpp \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index c29164781e47..2df0c90e9baf 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -86,9 +86,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(msg); - // TODO: THIS CHECK FAILS BECAUSE ONCE UIDMAP SIZE EXCEEDS LIMIT, DROPPING METRICS DATA - // DOESN'T HELP. FIX THIS. - // flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second); + flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second)); } // Hard-coded logic to update the isolated uid's in the uid-map. @@ -217,23 +215,32 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mLastBroadcastTimes.erase(key); } -void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, - const ConfigKey& key, - const unique_ptr<MetricsManager>& metricsManager) { +void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, + MetricsManager& metricsManager) { std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); - size_t totalBytes = metricsManager->byteSize() + mUidMap->getBytesUsed(); - // TODO: Find a way to test that the dropping and broadcasts are sent when memory is exceeded. - if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data. + auto lastCheckTime = mLastByteSizeTimes.find(key); + if (lastCheckTime != mLastByteSizeTimes.end()) { + if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { + return; + } + } + + // We suspect that the byteSize() computation is expensive, so we set a rate limit. + size_t totalBytes = metricsManager.byteSize(); + mLastByteSizeTimes[key] = timestampNs; + if (totalBytes > + StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data. // We ignore the return value so we force each metric producer to clear its contents. - metricsManager->onDumpReport(); + metricsManager.onDumpReport(); StatsdStats::getInstance().noteDataDropped(key); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); - } else if (totalBytes > - .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data. - auto lastFlushNs = mLastBroadcastTimes.find(key); - if (lastFlushNs != mLastBroadcastTimes.end()) { - if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) { + } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) { + // Send broadcast so that receivers can pull data. + auto lastBroadcastTime = mLastBroadcastTimes.find(key); + if (lastBroadcastTime != mLastBroadcastTimes.end()) { + if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) { + VLOG("StatsD would've sent a broadcast but the rate limit stopped us."); return; } } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index e9ac01536b42..a4df23a16c0f 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -60,29 +60,25 @@ private: std::unordered_map<ConfigKey, long> mLastBroadcastTimes; + // Tracks when we last checked the bytes consumed for each config key. + std::unordered_map<ConfigKey, long> mLastByteSizeTimes; + sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. sp<AnomalyMonitor> mAnomalyMonitor; - /* Max *serialized* size of the logs kept in memory before flushing through binder call. - Proto lite does not implement the SpaceUsed() function which gives the in memory byte size. - So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size - is higher than its serialized size. - */ - static const size_t kMaxSerializedBytes = 16 * 1024; - /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ - void flushIfNecessary(uint64_t timestampNs, - const ConfigKey& key, - const unique_ptr<MetricsManager>& metricsManager); + void flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, + MetricsManager& metricsManager); // Function used to send a broadcast so that receiver for the config key can call getData // to retrieve the stored data. std::function<void(const ConfigKey& key)> mSendBroadcast; - /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */ - static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC; + FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); + FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); + FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index fa7fe0c0bae7..dc12efb2666e 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -72,7 +72,7 @@ void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) { // ====================================================================== StatsService::StatsService(const sp<Looper>& handlerLooper) - : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better + : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS)) { mUidMap = new UidMap(); mConfigManager = new ConfigManager(); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index bdae1ef62ff0..e434f65872a6 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -46,6 +46,10 @@ public: StatsService(const sp<Looper>& handlerLooper); virtual ~StatsService(); + /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */ + // TODO: Consider making this configurable. And choose a good number. + const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5; + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); virtual status_t dump(int fd, const Vector<String16>& args); virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args); diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp index 2b2bcfc18fbf..4912648b648d 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "anomaly/AnomalyMonitor.h" +#include "guardrail/StatsdStats.h" namespace android { namespace os { @@ -76,10 +77,7 @@ void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) { if (!wasPresent) return; if (mPq.empty()) { if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); - mRegisteredAlarmTimeSec = 0; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->cancelAnomalyAlarm(); - } + cancelRegisteredAlarmTime_l(); return; } uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec; @@ -106,10 +104,7 @@ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popS if (!oldAlarms.empty()) { if (mPq.empty()) { if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); - mRegisteredAlarmTimeSec = 0; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->cancelAnomalyAlarm(); - } + cancelRegisteredAlarmTime_l(); } else { // Always update the registered alarm in this case (unlike remove()). updateRegisteredAlarmTime_l(mPq.top()->timestampSec); @@ -123,6 +118,16 @@ void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { mRegisteredAlarmTimeSec = timestampSec; if (mStatsCompanionService != nullptr) { mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec)); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } +} + +void AnomalyMonitor::cancelRegisteredAlarmTime_l() { + if (DEBUG) ALOGD("Cancelling reg alarm."); + mRegisteredAlarmTimeSec = 0; + if (mStatsCompanionService != nullptr) { + mStatsCompanionService->cancelAnomalyAlarm(); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); } } diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h index e19c46929a19..7acc7904bb57 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h @@ -138,6 +138,12 @@ private: */ void updateRegisteredAlarmTime_l(uint32_t timestampSec); + /** + * Cancels the alarm registered with StatsCompanionService. + * Also correspondingly sets mRegisteredAlarmTimeSec to 0. + */ + void cancelRegisteredAlarmTime_l(); + /** Converts uint32 timestamp in seconds to a Java long in msec. */ int64_t secToMs(uint32_t timeSec); }; diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 7bacb441ee48..e8b408328181 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "guardrail/StatsdStats.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> @@ -31,8 +32,9 @@ namespace statsd { // TODO: Separate DurationAnomalyTracker as a separate subclass and let each MetricProducer // decide and let which one it wants. // TODO: Get rid of bucketNumbers, and return to the original circular array method. -AnomalyTracker::AnomalyTracker(const Alert& alert) +AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) : mAlert(alert), + mConfigKey(configKey), mNumOfPastBuckets(mAlert.number_of_buckets() - 1) { VLOG("AnomalyTracker() called"); if (mAlert.number_of_buckets() <= 0) { @@ -220,6 +222,8 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { } else { ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); } + + StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name()); } void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 49e83235f73b..874add2ba798 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -18,6 +18,7 @@ #include <gtest/gtest_prod.h> #include "AnomalyMonitor.h" +#include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include "stats_util.h" // HashableDimensionKey and DimToValMap @@ -35,7 +36,7 @@ using std::shared_ptr; // Does NOT allow negative values. class AnomalyTracker : public virtual RefBase { public: - AnomalyTracker(const Alert& alert); + AnomalyTracker(const Alert& alert, const ConfigKey& configKey); virtual ~AnomalyTracker(); @@ -107,9 +108,13 @@ public: protected: void flushPastBuckets(const int64_t& currBucketNum); + // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; + // A reference to the Alert's config key. + const ConfigKey& mConfigKey; + // Number of past buckets. One less than the total number of buckets needed // for the anomaly detection (since the current bucket is not in the past). int mNumOfPastBuckets; diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto index 18b21449ebc6..72bb38a394a7 100644 --- a/cmds/statsd/src/atoms_copy.proto +++ b/cmds/statsd/src/atoms_copy.proto @@ -48,12 +48,14 @@ message Atom { SyncStateChanged sync_state_changed = 7; ScheduledJobStateChanged scheduled_job_state_changed = 8; ScreenBrightnessChanged screen_brightness_changed = 9; - // 10-20 are temporarily reserved for wakelocks etc. WakelockStateChanged wakelock_state_changed = 10; - UidWakelockStateChanged uid_wakelock_state_changed = 11; - LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12; - BatterySaverModeStateChanged battery_saver_mode_state_changed = 21; - DeviceIdleModeStateChanged device_idle_mode_state_changed = 22; + LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11; + MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12; + WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13; + // TODO: 14-19 are blank, but need not be + BatterySaverModeStateChanged battery_saver_mode_state_changed = 20; + DeviceIdleModeStateChanged device_idle_mode_state_changed = 21; + DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22; AudioStateChanged audio_state_changed = 23; MediaCodecActivityChanged media_codec_activity_changed = 24; CameraStateChanged camera_state_changed = 25; @@ -75,6 +77,8 @@ message Atom { SettingChanged setting_changed = 41; ActivityForegroundStateChanged activity_foreground_state_changed = 42; IsolatedUidChanged isolated_uid_changed = 43; + PacketWakeupOccurred packet_wakeup_occurred = 44; + DropboxErrorChanged dropbox_error_changed = 45; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } @@ -91,6 +95,8 @@ message Atom { CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008; CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009; CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010; + WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011; + ModemActivityInfoPulled modem_activity_info_pulled = 1012; } } @@ -147,6 +153,7 @@ message ScreenStateChanged { STATE_DOZE = 3; STATE_DOZE_SUSPEND = 4; STATE_VR = 5; + STATE_ON_SUSPEND = 6; } // New screen state. optional State display_state = 1; @@ -179,13 +186,17 @@ message ProcessLifeCycleStateChanged { // TODO: What is this? optional string name = 2; - // The state. - // TODO: Use an enum. - optional int32 event = 3; + // What lifecycle state the process changed to. + // This enum is specific to atoms.proto. + enum Event { + PROCESS_FINISHED = 0; + PROCESS_STARTED = 1; + PROCESS_CRASHED = 2; + PROCESS_ANRED = 3; + } + optional Event event = 3; } - - /** * Logs when the ble scan state changes. * @@ -417,31 +428,6 @@ message WakelockStateChanged { } /** - * Logs when an app is holding a wakelock, regardless of the wakelock's name. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message UidWakelockStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Type of wakelock. - enum Type { - PARTIAL = 0; - FULL = 1; - WINDOW = 2; - } - optional Type type = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - -/** * Logs when a partial wakelock is considered 'long' (over 1 min). * * Logged from: @@ -482,11 +468,33 @@ message BatterySaverModeStateChanged { * Logs Doze mode state change. * * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message DeviceIdleModeStateChanged { // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_. - optional int32 state = 1; + enum State { + DEVICE_IDLE_MODE_OFF = 0; + DEVICE_IDLE_MODE_LIGHT = 1; + DEVICE_IDLE_MODE_DEEP = 2; + } + optional State state = 1; +} + + +/** + * Logs state change of Doze mode including maintenance windows. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message DeviceIdlingModeStateChanged { + // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_. + enum State { + DEVICE_IDLE_MODE_OFF = 0; + DEVICE_IDLE_MODE_LIGHT = 1; + DEVICE_IDLE_MODE_DEEP = 2; + } + optional State state = 1; } /** @@ -586,6 +594,49 @@ message DeviceOnStatusChanged { message WakeupAlarmOccurred { // TODO: Add attribution instead of uid? optional int32 uid = 1; + + // Name of the wakeup alarm. + optional string tag = 2; +} + +/** + * Logs when an an app causes the mobile radio to change state. + * Changing from LOW to MEDIUM or HIGH can be considered the app waking the mobile radio. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +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; +} + +/** + * Logs when an an app causes the wifi radio to change state. + * Changing from LOW to MEDIUM or HIGH can be considered the app waking the wifi radio. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +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; } /** @@ -705,7 +756,7 @@ message SettingChanged { optional int32 user = 7; } -/* +/** * Logs activity going to foreground or background * * Logged from: @@ -723,6 +774,34 @@ message ActivityForegroundStateChanged { } /** + * Logs when an error is written to dropbox. + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message DropboxErrorChanged { + // The uid if available. -1 means not available. + optional int32 uid = 1; + + // Tag used when recording this error to dropbox. Contains data_ or system_ prefix. + optional string tag = 2; + + // The name of the process. + optional string process_name = 3; + + // The pid if available. -1 means not available. + optional int32 pid = 4; + + // 1 indicates is instant app. -1 indicates Not applicable. + optional int32 is_instant_app = 5; + + // The activity name if available. + optional string activity_name = 6; + + // 1 indicates in foreground. -1 indicates not available. + optional int32 is_foreground = 7; +} + +/** * Pulls bytes transferred via wifi (Sum of foreground and background usage). * * Pulled from: @@ -817,7 +896,7 @@ message KernelWakelockPulled { optional int64 time = 4; } -/* +/** * Pulls PowerStatePlatformSleepState. * * Definition here: @@ -877,7 +956,7 @@ message IsolatedUidChanged { optional int32 is_create = 3; } -/* +/** * Pulls Cpu time per frequency. * Note: this should be pulled for gauge metric only, without condition. * The puller keeps internal state of last values. It should not be pulled by @@ -894,7 +973,7 @@ message CpuTimePerFreqPulled { optional uint64 time = 3; } -/* +/** * Pulls Cpu Time Per Uid. * Note that isolated process uid time should be attributed to host uids. */ @@ -913,4 +992,86 @@ message CpuTimePerUidFreqPulled { optional uint64 uid = 1; optional uint64 freq_idx = 2; optional uint64 time_ms = 3; +} + +/* + * Logs the reception of an incoming network packet causing the main system to wake up for + * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd + * and processed by WakeupController.cpp. + */ +message PacketWakeupOccurred { + // The uid owning the socket into which the packet was delivered, or -1 if the packet was + // delivered nowhere. + optional int32 uid = 1; + // The interface name on which the packet was received. + optional string iface = 2; + // The ethertype value of the packet. + optional int32 ethertype = 3; + // String representation of the destination MAC address of the packet. + optional string destination_hardware_address = 4; + // String representation of the source address of the packet if this was an IP packet. + optional string source_ip = 5; + // String representation of the destination address of the packet if this was an IP packet. + optional string destination_ip = 6; + // The value of the protocol field if this was an IPv4 packet or the value of the Next Header + // field if this was an IPv6 packet. The range of possible values is the same for both IP + // families. + optional int32 ip_next_header = 7; + // The source port if this was a TCP or UDP packet. + optional int32 source_port = 8; + // The destination port if this was a TCP or UDP packet. + optional int32 destination_port = 9; +} + +/** + * Pulls Wifi Controller Activity Energy Info + */ +message WifiActivityEnergyInfoPulled { + // timestamp(wall clock) of record creation + optional uint64 timestamp_ms = 1; + // stack reported state + // TODO: replace this with proto enum + optional int32 stack_state = 2; + // tx time in ms + optional uint64 controller_tx_time_ms = 3; + // rx time in ms + optional uint64 controller_rx_time_ms = 4; + // idle time in ms + optional uint64 controller_idle_time_ms = 5; + // product of current(mA), voltage(V) and time(ms) + optional uint64 controller_energy_used = 6; +} + +/** + * Pulls Modem Activity Energy Info + */ +message ModemActivityInfoPulled { + // timestamp(wall clock) of record creation + optional uint64 timestamp_ms = 1; + // sleep time in ms. + optional uint64 sleep_time_ms = 2; + // idle time in ms + optional uint64 controller_idle_time_ms = 3; + /** + * Tx power index + * index 0 = tx_power < 0dBm + * index 1 = 0dBm < tx_power < 5dBm + * index 2 = 5dBm < tx_power < 15dBm + * index 3 = 15dBm < tx_power < 20dBm + * index 4 = tx_power > 20dBm + */ + // tx time in ms at power level 0 + optional uint64 controller_tx_time_pl0_ms = 4; + // tx time in ms at power level 1 + optional uint64 controller_tx_time_pl1_ms = 5; + // tx time in ms at power level 2 + optional uint64 controller_tx_time_pl2_ms = 6; + // tx time in ms at power level 3 + optional uint64 controller_tx_time_pl3_ms = 7; + // tx time in ms at power level 4 + optional uint64 controller_tx_time_pl4_ms = 8; + // rx time in ms at power level 5 + optional uint64 controller_rx_time_ms = 9; + // product of current(mA), voltage(V) and time(ms) + optional uint64 energy_used = 10; }
\ No newline at end of file diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 29574579b2a0..b02b9daa4b2d 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -41,11 +41,9 @@ using std::vector; const int FIELD_ID_BEGIN_TIME = 1; const int FIELD_ID_END_TIME = 2; const int FIELD_ID_CONFIG_STATS = 3; -const int FIELD_ID_MATCHER_STATS = 4; -const int FIELD_ID_CONDITION_STATS = 5; -const int FIELD_ID_METRIC_STATS = 6; const int FIELD_ID_ATOM_STATS = 7; const int FIELD_ID_UIDMAP_STATS = 8; +const int FIELD_ID_ANOMALY_ALARM_STATS = 9; const int FIELD_ID_MATCHER_STATS_NAME = 1; const int FIELD_ID_MATCHER_STATS_COUNT = 2; @@ -59,6 +57,8 @@ const int FIELD_ID_METRIC_STATS_COUNT = 2; const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; +const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; + // TODO: add stats for pulled atoms. StatsdStats::StatsdStats() { mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1); @@ -101,11 +101,12 @@ void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { if (it != mConfigStats.end()) { int32_t nowTimeSec = time(nullptr); it->second.set_deletion_time_sec(nowTimeSec); - // Add condition stats, metrics stats, matcher stats - addSubStatsToConfig(key, it->second); + // Add condition stats, metrics stats, matcher stats, alert stats + addSubStatsToConfigLocked(key, it->second); // Remove them after they are added to the config stats. mMatcherStats.erase(key); mMetricsStats.erase(key); + mAlertStats.erase(key); mConditionStats.erase(key); mIceBox.push_back(it->second); mConfigStats.erase(it); @@ -219,6 +220,17 @@ void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) { matcherStats[name]++; } +void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const string& name) { + lock_guard<std::mutex> lock(mLock); + auto& alertStats = mAlertStats[key]; + alertStats[name]++; +} + +void StatsdStats::noteRegisteredAnomalyAlarmChanged() { + lock_guard<std::mutex> lock(mLock); + mAnomalyAlarmRegisteredStats++; +} + void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { lock_guard<std::mutex> lock(mLock); @@ -246,6 +258,8 @@ void StatsdStats::resetInternalLocked() { mConditionStats.clear(); mMetricsStats.clear(); std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); + mAlertStats.clear(); + mAnomalyAlarmRegisteredStats = 0; mMatcherStats.clear(); for (auto& config : mConfigStats) { config.second.clear_broadcast_sent_time_sec(); @@ -254,10 +268,11 @@ void StatsdStats::resetInternalLocked() { config.second.clear_matcher_stats(); config.second.clear_condition_stats(); config.second.clear_metric_stats(); + config.second.clear_alert_stats(); } } -void StatsdStats::addSubStatsToConfig(const ConfigKey& key, +void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats) { // Add matcher stats if (mMatcherStats.find(key) != mMatcherStats.end()) { @@ -289,6 +304,16 @@ void StatsdStats::addSubStatsToConfig(const ConfigKey& key, VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second); } } + // Add anomaly detection alert stats + if (mAlertStats.find(key) != mAlertStats.end()) { + const auto& alertStats = mAlertStats[key]; + for (const auto& stats : alertStats) { + auto output = configStats.add_alert_stats(); + output->set_name(stats.first); + output->set_declared_times(stats.second); + VLOG("alert %s declared %d times", stats.first.c_str(), stats.second); + } + } } void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { @@ -358,7 +383,7 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { } } - addSubStatsToConfig(pair.first, configStats); + addSubStatsToConfigLocked(pair.first, configStats); const int numBytes = configStats.ByteSize(); vector<char> buffer(numBytes); @@ -370,6 +395,7 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { configStats.clear_matcher_stats(); configStats.clear_condition_stats(); configStats.clear_metric_stats(); + configStats.clear_alert_stats(); } VLOG("********Atom stats***********"); @@ -386,6 +412,15 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { } } + if (mAnomalyAlarmRegisteredStats > 0) { + VLOG("********AnomalyAlarmStats stats***********"); + long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, + mAnomalyAlarmRegisteredStats); + proto.end(token); + VLOG("Anomaly alarm registrations: %d", mAnomalyAlarmRegisteredStats); + } + const int numBytes = mUidMapStats.ByteSize(); vector<char> buffer(numBytes); mUidMapStats.SerializeToArray(&buffer[0], numBytes); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index d6f6566cbc33..4cf168eb40bf 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -19,6 +19,7 @@ #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include <gtest/gtest_prod.h> +#include <log/log_time.h> #include <mutex> #include <string> #include <vector> @@ -45,10 +46,20 @@ public: const static int kMaxTimestampCount = 20; + // Max memory allowed for storing metrics per configuration. When this limit is approached, + // statsd will send a broadcast so that the client can fetch the data and clear this memory. + static const size_t kMaxMetricsBytesPerConfig = 128 * 1024; + // Cap the UID map's memory usage to this. This should be fairly high since the UID information // is critical for understanding the metrics. const static size_t kMaxBytesUsedUidMap = 50 * 1024; + /* Minimum period between two broadcasts in nanoseconds. */ + static const unsigned long long kMinBroadcastPeriodNs = 60 * NS_PER_SEC; + + /* Min period between two checks of byte size per config key in nanoseconds. */ + static const unsigned long long kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC; + /** * Report a new config has been received and report the static stats about the config. * @@ -112,11 +123,24 @@ public: void noteMatcherMatched(const ConfigKey& key, const std::string& name); /** + * Report that an anomaly detection alert has been declared. + * + * [key]: The config key that this alert belongs to. + * [name]: The name of the alert. + */ + void noteAnomalyDeclared(const ConfigKey& key, const std::string& name); + + /** * Report an atom event has been logged. */ void noteAtomLogged(int atomId, int32_t timeSec); /** + * Report that statsd modified the anomaly alarm registered with StatsCompanionService. + */ + void noteRegisteredAnomalyAlarmChanged(); + + /** * Records the number of snapshot and delta entries that are being dropped from the uid map. */ void noteUidMapDropped(int snapshots, int deltas); @@ -174,6 +198,14 @@ private: // This is a vector, not a map because it will be accessed A LOT -- for each stats log. std::vector<int> mPushedAtomStats; + // Stores the number of times statsd modified the anomaly alarm registered with + // StatsCompanionService. + int mAnomalyAlarmRegisteredStats = 0; + + // Stores the number of times an anomaly detection alert has been declared + // (per config, per alert name). + std::map<const ConfigKey, std::map<const std::string, int>> mAlertStats; + // Stores how many times a matcher have been matched. std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats; @@ -181,7 +213,8 @@ private: void resetInternalLocked(); - void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats); + void addSubStatsToConfigLocked(const ConfigKey& key, + StatsdStatsReport_ConfigStats& configStats); void noteDataDropped(const ConfigKey& key, int32_t timeSec); @@ -195,6 +228,7 @@ private: FRIEND_TEST(StatsdStatsTest, TestSubStats); FRIEND_TEST(StatsdStatsTest, TestAtomLog); FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold); + FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index fd484c25dbaa..cedea301de27 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -111,7 +111,7 @@ sp<AnomalyTracker> DurationMetricProducer::createAnomalyTracker(const Alert &ale return nullptr; } // TODO: return a DurationAnomalyTracker (which should sublclass AnomalyTracker) - return new AnomalyTracker(alert); + return new AnomalyTracker(alert, mConfigKey); } void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index e7e84ab1dcc8..adeb3cdf258a 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -94,7 +94,7 @@ public: } virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) { - return new AnomalyTracker(alert); + return new AnomalyTracker(alert, mConfigKey); } void addAnomalyTracker(sp<AnomalyTracker> tracker) { diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 4e6a0ce5439b..86c47333d7e6 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -36,7 +36,7 @@ class MetricsManager { public: MetricsManager(const ConfigKey& configKey, const StatsdConfig& config); - ~MetricsManager(); + virtual ~MetricsManager(); // Return whether the configuration is valid. bool isConfigValid() const; @@ -52,11 +52,11 @@ public: void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor); // Config source owner can call onDumpReport() to get all the metrics collected. - std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport(); + virtual std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport(); // Computes the total byte size of all metrics managed by a single config source. // Does not change the state. - size_t byteSize(); + virtual size_t byteSize(); private: const ConfigKey mConfigKey; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 60d9a3dbb9d2..f8b91fe0ef0a 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -193,6 +193,11 @@ message StatsdStatsReport { optional int32 max_tuple_counts = 2; } + message AlertStats { + optional string name = 1; + optional int32 declared_times = 2; + } + message ConfigStats { optional int32 uid = 1; optional string name = 2; @@ -210,6 +215,7 @@ message StatsdStatsReport { repeated MatcherStats matcher_stats = 13; repeated ConditionStats condition_stats = 14; repeated MetricStats metric_stats = 15; + repeated AlertStats alert_stats = 16; } repeated ConfigStats config_stats = 3; @@ -229,4 +235,9 @@ message StatsdStatsReport { optional int32 dropped_changes = 5; } optional UidMapStats uidmap_stats = 8; + + message AnomalyAlarmStats { + optional int32 alarms_registered = 1; + } + optional AnomalyAlarmStats anomaly_alarm_stats = 9; }
\ No newline at end of file diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp new file mode 100644 index 000000000000..ff04d955ecd0 --- /dev/null +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -0,0 +1,117 @@ +// 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 "StatsLogProcessor.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" +#include "packages/UidMap.h" +#include "statslog.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <stdio.h> + +using namespace android; +using namespace testing; + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +/** + * Mock MetricsManager (ByteSize() is called). + */ +class MockMetricsManager : public MetricsManager { +public: + MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig()) { + } + + MOCK_METHOD0(byteSize, size_t()); + MOCK_METHOD0(onDumpReport, std::vector<std::unique_ptr<std::vector<uint8_t>>>()); +}; + +TEST(StatsLogProcessorTest, TestRateLimitByteSize) { + sp<UidMap> m = new UidMap(); + sp<AnomalyMonitor> anomalyMonitor; + // Construct the processor with a dummy sendBroadcast function that does nothing. + StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {}); + + MockMetricsManager mockMetricsManager; + + ConfigKey key(100, "key"); + // Expect only the first flush to trigger a check for byte size since the last two are + // rate-limited. + EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); + p.flushIfNecessary(99, key, mockMetricsManager); + p.flushIfNecessary(100, key, mockMetricsManager); + p.flushIfNecessary(101, key, mockMetricsManager); +} + +TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { + sp<UidMap> m = new UidMap(); + sp<AnomalyMonitor> anomalyMonitor; + int broadcastCount = 0; + StatsLogProcessor p(m, anomalyMonitor, + [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); + + MockMetricsManager mockMetricsManager; + + ConfigKey key(100, "key"); + EXPECT_CALL(mockMetricsManager, byteSize()) + .Times(2) + .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); + + // Expect only one broadcast despite always returning a size that should trigger broadcast. + p.flushIfNecessary(1, key, mockMetricsManager); + EXPECT_EQ(1, broadcastCount); + + // This next call to flush should not trigger a broadcast. + p.mLastByteSizeTimes.clear(); // Force another check for byte size. + p.flushIfNecessary(2, key, mockMetricsManager); + EXPECT_EQ(1, broadcastCount); +} + +TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { + sp<UidMap> m = new UidMap(); + sp<AnomalyMonitor> anomalyMonitor; + int broadcastCount = 0; + StatsLogProcessor p(m, anomalyMonitor, + [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); + + MockMetricsManager mockMetricsManager; + + ConfigKey key(100, "key"); + EXPECT_CALL(mockMetricsManager, byteSize()) + .Times(1) + .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); + + EXPECT_CALL(mockMetricsManager, onDumpReport()).Times(1); + + // Expect to call the onDumpReport and skip the broadcast. + p.flushIfNecessary(1, key, mockMetricsManager); + EXPECT_EQ(0, broadcastCount); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 65c2a05543b9..f38576398f11 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -30,6 +30,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list, std::shared_ptr<DimToValMap> bucket) { for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { @@ -51,7 +53,7 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert); + AnomalyTracker anomalyTracker(alert, kConfigKey); std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); int64_t eventTimestamp0 = 10; @@ -168,7 +170,7 @@ TEST(AnomalyTrackerTest, TestSparseBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert); + AnomalyTracker anomalyTracker(alert, kConfigKey); std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}}); diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index a8193dd92e8c..9fed4f8b5e30 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -118,6 +118,10 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMetricDimensionSize(key, "metric1", 201); stats.noteMetricDimensionSize(key, "metric1", 202); + stats.noteAnomalyDeclared(key, "alert1"); + stats.noteAnomalyDeclared(key, "alert1"); + stats.noteAnomalyDeclared(key, "alert2"); + // broadcast-> 2 stats.noteBroadcastSent(key); stats.noteBroadcastSent(key); @@ -142,7 +146,6 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(3, configReport.dump_report_time_sec_size()); EXPECT_EQ(2, configReport.matcher_stats_size()); - // matcher1 is the first in the list if (!configReport.matcher_stats(0).name().compare("matcher1")) { EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); @@ -157,6 +160,13 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ("matcher1", configReport.matcher_stats(1).name()); } + EXPECT_EQ(2, configReport.alert_stats_size()); + bool alert1first = !configReport.alert_stats(0).name().compare("alert1"); + EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name()); + EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).declared_times()); + EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name()); + EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).declared_times()); + EXPECT_EQ(1, configReport.condition_stats_size()); EXPECT_EQ("condition1", configReport.condition_stats(0).name()); EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); @@ -169,6 +179,7 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMatcherMatched(key, "matcher99"); stats.noteConditionDimensionSize(key, "condition99", 300); stats.noteMetricDimensionSize(key, "metric99", 270); + stats.noteAnomalyDeclared(key, "alert99"); // now the config stats should only contain the stats about the new event. stats.dumpStats(&output, false); @@ -187,6 +198,10 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(1, configReport2.metric_stats_size()); EXPECT_EQ("metric99", configReport2.metric_stats(0).name()); EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); + + EXPECT_EQ(1, configReport2.alert_stats_size()); + EXPECT_EQ("alert99", configReport2.alert_stats(0).name()); + EXPECT_EQ(1, configReport2.alert_stats(0).declared_times()); } TEST(StatsdStatsTest, TestAtomLog) { @@ -224,6 +239,21 @@ TEST(StatsdStatsTest, TestAtomLog) { EXPECT_TRUE(sensorAtomGood); } + +TEST(StatsdStatsTest, TestAnomalyMonitor) { + StatsdStats stats; + stats.noteRegisteredAnomalyAlarmChanged(); + stats.noteRegisteredAnomalyAlarmChanged(); + + vector<uint8_t> output; + stats.dumpStats(&output, false); + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + + EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered()); +} + TEST(StatsdStatsTest, TestTimestampThreshold) { StatsdStats stats; vector<int32_t> timestamps; diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 9b94099625a5..d973ba1676a9 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -196,7 +196,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetection) { int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); CountMetric metric; metric.set_name("1"); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index ed13db25397a..59475d266217 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -33,6 +33,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(GaugeMetricProducerTest, TestWithCondition) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; @@ -148,7 +150,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_metric_name("1"); alert.set_trigger_if_sum_gt(25); alert.set_number_of_buckets(2); - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); gaugeProducer.addAnomalyTracker(anomalyTracker); std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 1adcc1146c42..4e5e0d6dbfb3 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -208,7 +208,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index fa7b9a734524..99d3e054d893 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -269,7 +269,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); @@ -331,7 +331,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/, bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java index 5e8f26d5adbc..735a32729a0f 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java @@ -110,113 +110,7 @@ public class DisplayProtoUtils { log.getEventMetrics(); for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { sb.append(getDateStr(event.getTimestampNanos())).append(": "); - switch (event.getAtom().getPushedCase()) { - case SETTING_CHANGED: - sb.append("SETTING_CHANGED\n"); - break; - case SYNC_STATE_CHANGED: - sb.append("SYNC_STATE_CHANGED\n"); - break; - case AUDIO_STATE_CHANGED: - sb.append("AUDIO_STATE_CHANGED\n"); - break; - case CAMERA_STATE_CHANGED: - sb.append("CAMERA_STATE_CHANGED\n"); - break; - case ISOLATED_UID_CHANGED: - sb.append("ISOLATED_UID_CHANGED\n"); - break; - case SCREEN_STATE_CHANGED: - sb.append("SCREEN_STATE_CHANGED\n"); - break; - case SENSOR_STATE_CHANGED: - sb.append("SENSOR_STATE_CHANGED\n"); - break; - case BATTERY_LEVEL_CHANGED: - sb.append("BATTERY_LEVEL_CHANGED\n"); - break; - case PLUGGED_STATE_CHANGED: - sb.append("PLUGGED_STATE_CHANGED\n"); - break; - case WAKEUP_ALARM_OCCURRED: - sb.append("WAKEUP_ALARM_OCCURRED\n"); - break; - case BLE_SCAN_STATE_CHANGED: - sb.append("BLE_SCAN_STATE_CHANGED\n"); - break; - case CHARGING_STATE_CHANGED: - sb.append("CHARGING_STATE_CHANGED\n"); - break; - case GPS_SCAN_STATE_CHANGED: - sb.append("GPS_SCAN_STATE_CHANGED\n"); - break; - case KERNEL_WAKEUP_REPORTED: - sb.append("KERNEL_WAKEUP_REPORTED\n"); - break; - case WAKELOCK_STATE_CHANGED: - sb.append("WAKELOCK_STATE_CHANGED\n"); - break; - case WIFI_LOCK_STATE_CHANGED: - sb.append("WIFI_LOCK_STATE_CHANGED\n"); - break; - case WIFI_SCAN_STATE_CHANGED: - sb.append("WIFI_SCAN_STATE_CHANGED\n"); - break; - case BLE_SCAN_RESULT_RECEIVED: - sb.append("BLE_SCAN_RESULT_RECEIVED\n"); - break; - case DEVICE_ON_STATUS_CHANGED: - sb.append("DEVICE_ON_STATUS_CHANGED\n"); - break; - case FLASHLIGHT_STATE_CHANGED: - sb.append("FLASHLIGHT_STATE_CHANGED\n"); - break; - case SCREEN_BRIGHTNESS_CHANGED: - sb.append("SCREEN_BRIGHTNESS_CHANGED\n"); - break; - case UID_PROCESS_STATE_CHANGED: - sb.append("UID_PROCESS_STATE_CHANGED\n"); - break; - case UID_WAKELOCK_STATE_CHANGED: - sb.append("UID_WAKELOCK_STATE_CHANGED\n"); - break; - case DEVICE_TEMPERATURE_REPORTED: - sb.append("DEVICE_TEMPERATURE_REPORTED\n"); - break; - case SCHEDULED_JOB_STATE_CHANGED: - sb.append("SCHEDULED_JOB_STATE_CHANGED\n"); - break; - case MEDIA_CODEC_ACTIVITY_CHANGED: - sb.append("MEDIA_CODEC_ACTIVITY_CHANGED\n"); - break; - case WIFI_SIGNAL_STRENGTH_CHANGED: - sb.append("WIFI_SIGNAL_STRENGTH_CHANGED\n"); - break; - case PHONE_SIGNAL_STRENGTH_CHANGED: - sb.append("PHONE_SIGNAL_STRENGTH_CHANGED\n"); - break; - case DEVICE_IDLE_MODE_STATE_CHANGED: - sb.append("DEVICE_IDLE_MODE_STATE_CHANGED\n"); - break; - case BATTERY_SAVER_MODE_STATE_CHANGED: - sb.append("BATTERY_SAVER_MODE_STATE_CHANGED\n"); - break; - case PROCESS_LIFE_CYCLE_STATE_CHANGED: - sb.append("PROCESS_LIFE_CYCLE_STATE_CHANGED\n"); - break; - case ACTIVITY_FOREGROUND_STATE_CHANGED: - sb.append("ACTIVITY_FOREGROUND_STATE_CHANGED\n"); - break; - case BLE_UNOPTIMIZED_SCAN_STATE_CHANGED: - sb.append("BLE_UNOPTIMIZED_SCAN_STATE_CHANGED\n"); - break; - case LONG_PARTIAL_WAKELOCK_STATE_CHANGED: - sb.append("LONG_PARTIAL_WAKELOCK_STATE_CHANGED\n"); - break; - case PUSHED_NOT_SET: - sb.append("PUSHED_NOT_SET\n"); - break; - } + sb.append(event.getAtom().getPushedCase().toString()).append("\n"); } } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 7a1931718888..037aeb058f15 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -35,23 +35,23 @@ interface IAccessibilityServiceConnection { void setServiceInfo(in AccessibilityServiceInfo info); - boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, in Bundle arguments); - boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, + String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, + String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, + String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, diff --git a/core/java/android/annotation/IntDef.java b/core/java/android/annotation/IntDef.java index 3f9064e4dd73..f84a67655dc3 100644 --- a/core/java/android/annotation/IntDef.java +++ b/core/java/android/annotation/IntDef.java @@ -52,10 +52,12 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; @Target({ANNOTATION_TYPE}) public @interface IntDef { /** Defines the constant prefix for this element */ - String[] prefix() default ""; + String[] prefix() default {}; + /** Defines the constant suffix for this element */ + String[] suffix() default {}; /** Defines the allowed constants for this element */ - long[] value() default {}; + int[] value() default {}; /** Defines whether the constants can be used as a flag, or just as an enum (the default) */ boolean flag() default false; diff --git a/core/java/android/annotation/LongDef.java b/core/java/android/annotation/LongDef.java new file mode 100644 index 000000000000..8723eef86328 --- /dev/null +++ b/core/java/android/annotation/LongDef.java @@ -0,0 +1,62 @@ +/* + * 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated long element represents + * a logical type and that its value should be one of the explicitly + * named constants. If the {@link #flag()} attribute is set to true, + * multiple constants can be combined. + * <p> + * <pre><code> + * @Retention(SOURCE) + * @LongDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS}) + * public @interface NavigationMode {} + * public static final long NAVIGATION_MODE_STANDARD = 0; + * public static final long NAVIGATION_MODE_LIST = 1; + * public static final long NAVIGATION_MODE_TABS = 2; + * ... + * public abstract void setNavigationMode(@NavigationMode long mode); + * @NavigationMode + * public abstract long getNavigationMode(); + * </code></pre> + * For a flag, set the flag attribute: + * <pre><code> + * @LongDef( + * flag = true, + * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS}) + * </code></pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({ANNOTATION_TYPE}) +public @interface LongDef { + /** Defines the constant prefix for this element */ + String[] prefix() default ""; + + /** Defines the allowed constants for this element */ + long[] value() default {}; + + /** Defines whether the constants can be used as a flag, or just as an enum (the default) */ + boolean flag() default false; +} diff --git a/core/java/android/annotation/StringDef.java b/core/java/android/annotation/StringDef.java index d5157c3a1562..a37535b9c98e 100644 --- a/core/java/android/annotation/StringDef.java +++ b/core/java/android/annotation/StringDef.java @@ -46,6 +46,11 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; @Retention(SOURCE) @Target({ANNOTATION_TYPE}) public @interface StringDef { + /** Defines the constant prefix for this element */ + String[] prefix() default {}; + /** Defines the constant suffix for this element */ + String[] suffix() default {}; + /** Defines the allowed constants for this element */ String[] value() default {}; } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 924137838ad4..e33b79e51fb6 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2613,7 +2613,7 @@ public class ActivityManager { Manifest.permission.ACCESS_INSTANT_APPS}) public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) { try { - return getService().clearApplicationUserData(packageName, + return getService().clearApplicationUserData(packageName, false, observer, UserHandle.myUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -2944,10 +2944,10 @@ public class ActivityManager { /** * Constant for {@link #importance}: This process is running an * application that can not save its state, and thus can't be killed - * while in the background. - * @hide + * while in the background. This will be used with apps that have + * {@link android.R.attr#cantSaveState} set on their application tag. */ - public static final int IMPORTANCE_CANT_SAVE_STATE= 270; + public static final int IMPORTANCE_CANT_SAVE_STATE = 270; /** * Constant for {@link #importance}: This process is contains services @@ -2995,7 +2995,7 @@ public class ActivityManager { return IMPORTANCE_CACHED; } else if (procState >= PROCESS_STATE_SERVICE) { return IMPORTANCE_SERVICE; - } else if (procState > PROCESS_STATE_HEAVY_WEIGHT) { + } else if (procState == PROCESS_STATE_HEAVY_WEIGHT) { return IMPORTANCE_CANT_SAVE_STATE; } else if (procState >= PROCESS_STATE_TRANSIENT_BACKGROUND) { return IMPORTANCE_PERCEPTIBLE; @@ -3051,7 +3051,7 @@ public class ActivityManager { return PROCESS_STATE_HOME; } else if (importance >= IMPORTANCE_SERVICE) { return PROCESS_STATE_SERVICE; - } else if (importance > IMPORTANCE_CANT_SAVE_STATE) { + } else if (importance == IMPORTANCE_CANT_SAVE_STATE) { return PROCESS_STATE_HEAVY_WEIGHT; } else if (importance >= IMPORTANCE_PERCEPTIBLE) { return PROCESS_STATE_TRANSIENT_BACKGROUND; diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d7efa91fb28b..a9c4d3705afc 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -299,4 +299,9 @@ public abstract class ActivityManagerInternal { * @return true if runtime was restarted, false if it's normal boot */ public abstract boolean isRuntimeRestarted(); + + /** + * Returns {@code true} if {@code uid} is running an activity from {@code packageName}. + */ + public abstract boolean hasRunningActivity(int uid, @Nullable String packageName); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 20213199f53b..5369adf4aad1 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2557,8 +2557,8 @@ public final class ActivityThread extends ClientTransactionHandler { + " req=" + requestCode + " res=" + resultCode + " data=" + data); ArrayList<ResultInfo> list = new ArrayList<ResultInfo>(); list.add(new ResultInfo(id, requestCode, resultCode, data)); - final ClientTransaction clientTransaction = new ClientTransaction(mAppThread, token); - clientTransaction.addCallback(new ActivityResultItem(list)); + final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread, token); + clientTransaction.addCallback(ActivityResultItem.obtain(list)); try { mAppThread.scheduleTransaction(clientTransaction); } catch (RemoteException e) { @@ -5605,7 +5605,16 @@ public final class ActivityThread extends ClientTransactionHandler { // Continue loading instrumentation. if (ii != null) { - final ApplicationInfo instrApp = new ApplicationInfo(); + ApplicationInfo instrApp; + try { + instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0, + UserHandle.myUserId()); + } catch (RemoteException e) { + instrApp = null; + } + if (instrApp == null) { + instrApp = new ApplicationInfo(); + } ii.copyTo(instrApp); instrApp.initForUser(UserHandle.myUserId()); final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 55f9e289f52d..382719b4a305 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -568,9 +568,21 @@ public class AlarmManager { } /** - * Schedule an alarm that represents an alarm clock. + * Schedule an alarm that represents an alarm clock, which will be used to notify the user + * when it goes off. The expectation is that when this alarm triggers, the application will + * further wake up the device to tell the user about the alarm -- turning on the screen, + * playing a sound, vibrating, etc. As such, the system will typically also use the + * information supplied here to tell the user about this upcoming alarm if appropriate. * - * The system may choose to display information about this alarm to the user. + * <p>Due to the nature of this kind of alarm, similar to {@link #setExactAndAllowWhileIdle}, + * these alarms will be allowed to trigger even if the system is in a low-power idle + * (a.k.a. doze) mode. The system may also do some prep-work when it sees that such an + * alarm coming up, to reduce the amount of background work that could happen if this + * causes the device to fully wake up -- this is to avoid situations such as a large number + * of devices having an alarm set at the same time in the morning, all waking up at that + * time and suddenly swamping the network with pending background work. As such, these + * types of alarms can be extremely expensive on battery use and should only be used for + * their intended purpose.</p> * * <p> * This method is like {@link #setExact(int, long, PendingIntent)}, but implies @@ -783,9 +795,9 @@ public class AlarmManager { /** * Like {@link #set(int, long, PendingIntent)}, but this alarm will be allowed to execute - * even when the system is in low-power idle modes. This type of alarm must <b>only</b> - * be used for situations where it is actually required that the alarm go off while in - * idle -- a reasonable example would be for a calendar notification that should make a + * even when the system is in low-power idle (a.k.a. doze) modes. This type of alarm must + * <b>only</b> be used for situations where it is actually required that the alarm go off while + * in idle -- a reasonable example would be for a calendar notification that should make a * sound so the user is aware of it. When the alarm is dispatched, the app will also be * added to the system's temporary whitelist for approximately 10 seconds to allow that * application to acquire further wake locks in which to complete its work.</p> diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 84c07ec7f45a..85bf6aa75f56 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -207,7 +207,7 @@ interface IActivityManager { boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); void getMemoryInfo(out ActivityManager.MemoryInfo outInfo); List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState(); - boolean clearApplicationUserData(in String packageName, + boolean clearApplicationUserData(in String packageName, boolean keepState, in IPackageDataObserver observer, int userId); void forceStopPackage(in String packageName, int userId); boolean killPids(in int[] pids, in String reason, boolean secure); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 42c1347e265d..fb9efe6f321d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1941,6 +1941,7 @@ public class Notification implements Parcelable mSortKey = parcel.readString(); extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null + fixDuplicateExtras(); actions = parcel.createTypedArray(Action.CREATOR); // may be null @@ -2389,6 +2390,33 @@ public class Notification implements Parcelable }; /** + * Parcelling creates multiple copies of objects in {@code extras}. Fix them. + * <p> + * For backwards compatibility {@code extras} holds some references to "real" member data such + * as {@link getLargeIcon()} which is mirrored by {@link #EXTRA_LARGE_ICON}. This is mostly + * fine as long as the object stays in one process. + * <p> + * However, once the notification goes into a parcel each reference gets marshalled separately, + * wasting memory. Especially with large images on Auto and TV, this is worth fixing. + */ + private void fixDuplicateExtras() { + if (extras != null) { + fixDuplicateExtra(mSmallIcon, EXTRA_SMALL_ICON); + fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON); + } + } + + /** + * If we find an extra that's exactly the same as one of the "real" fields but refers to a + * separate object, replace it with the field's version to avoid holding duplicate copies. + */ + private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) { + if (original != null && extras.getParcelable(extraName) != null) { + extras.putParcelable(extraName, original); + } + } + + /** * Sets the {@link #contentView} field to be a view with the standard "Latest Event" * layout. * diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index f831ae220703..495fd3c4682e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -618,12 +618,13 @@ final class SystemServiceRegistry { ConnectivityThread.getInstanceLooper()); }}); - registerService(Context.WIFI_RTT2_SERVICE, WifiRttManager.class, + registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class, new CachedServiceFetcher<WifiRttManager>() { @Override public WifiRttManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT2_SERVICE); + IBinder b = ServiceManager.getServiceOrThrow( + Context.WIFI_RTT_RANGING_SERVICE); IWifiRttManager service = IWifiRttManager.Stub.asInterface(b); return new WifiRttManager(ctx.getOuterContext(), service); }}); diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java index 392387a99897..61b90e1766e5 100644 --- a/core/java/android/app/VrManager.java +++ b/core/java/android/app/VrManager.java @@ -4,6 +4,7 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.os.Handler; @@ -214,4 +215,22 @@ public class VrManager { e.rethrowFromSystemServer(); } } + + /** + * Start VR Input method for the packageName in {@link ComponentName}. + * This method notifies InputMethodManagerService to use VR IME instead of + * regular phone IME. + * @param componentName ComponentName of a VR InputMethod that should be set as selected + * input by InputMethodManagerService. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) + public void setVrInputMethod(ComponentName componentName) { + try { + mService.setVrInputMethod(componentName); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 562b981941a7..34cd67e455ce 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1321,9 +1321,15 @@ public class DevicePolicyManager { public static final String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app"; /** + * Delegation for installing existing packages. This scope grants access to the + * {@link #installExistingPackage} API. + */ + public static final String DELEGATION_INSTALL_EXISTING_PACKAGE = + "delegation-install-existing-package"; + + /** * Delegation of management of uninstalled packages. This scope grants access to the * {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs. - * @hide */ public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages"; @@ -6085,7 +6091,6 @@ public class DevicePolicyManager { * @return List of package names to keep cached. * @see #setDelegatedScopes * @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES - * @hide */ public @Nullable List<String> getKeepUninstalledPackages(@Nullable ComponentName admin) { throwIfParentInstance("getKeepUninstalledPackages"); @@ -6113,7 +6118,6 @@ public class DevicePolicyManager { * @throws SecurityException if {@code admin} is not a device owner. * @see #setDelegatedScopes * @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES - * @hide */ public void setKeepUninstalledPackages(@Nullable ComponentName admin, @NonNull List<String> packageNames) { @@ -6591,6 +6595,37 @@ public class DevicePolicyManager { } /** + * Install an existing package that has been installed in another user, or has been kept after + * removal via {@link #setKeepUninstalledPackages}. + * This function can be called by a device owner, profile owner or a delegate given + * the {@link #DELEGATION_INSTALL_EXISTING_PACKAGE} scope via {@link #setDelegatedScopes}. + * When called in a secondary user or managed profile, the user/profile must be affiliated with + * the device owner. See {@link #setAffiliationIds}. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName The package to be installed in the calling profile. + * @return {@code true} if the app is installed; {@code false} otherwise. + * @throws SecurityException if {@code admin} is not the device owner, or the profile owner of + * an affiliated user or profile. + * @see #setKeepUninstalledPackages + * @see #setDelegatedScopes + * @see #setAffiliationIds + * @see #DELEGATION_PACKAGE_ACCESS + */ + public boolean installExistingPackage(@NonNull ComponentName admin, String packageName) { + throwIfParentInstance("installExistingPackage"); + if (mService != null) { + try { + return mService.installExistingPackage(admin, mContext.getPackageName(), + packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Called by a device owner or profile owner to disable account management for a specific type * of account. * <p> @@ -8585,32 +8620,31 @@ public class DevicePolicyManager { } /** - * Called by a device owner to specify whether a logout button is enabled for all secondary - * users. The system may show a logout button that stops the user and switches back to the - * primary user. + * Called by a device owner to specify whether logout is enabled for all secondary users. The + * system may show a logout button that stops the user and switches back to the primary user. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param enabled whether logout button should be enabled or not. + * @param enabled whether logout should be enabled or not. * @throws SecurityException if {@code admin} is not a device owner. */ - public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) { - throwIfParentInstance("setLogoutButtonEnabled"); + public void setLogoutEnabled(@NonNull ComponentName admin, boolean enabled) { + throwIfParentInstance("setLogoutEnabled"); try { - mService.setLogoutButtonEnabled(admin, enabled); + mService.setLogoutEnabled(admin, enabled); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** - * Returns whether logout button is enabled by a device owner. + * Returns whether logout is enabled by a device owner. * - * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise. + * @return {@code true} if logout is enabled by device owner, {@code false} otherwise. */ - public boolean isLogoutButtonEnabled() { - throwIfParentInstance("isLogoutButtonEnabled"); + public boolean isLogoutEnabled() { + throwIfParentInstance("isLogoutEnabled"); try { - return mService.isLogoutButtonEnabled(); + return mService.isLogoutEnabled(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index c525df725d47..81da197f2129 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -223,6 +223,7 @@ interface IDevicePolicyManager { void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName); int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent); + boolean installExistingPackage(in ComponentName admin, in String callerPackage, in String packageName); void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); String[] getAccountTypesWithManagementDisabled(); @@ -372,6 +373,6 @@ interface IDevicePolicyManager { boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback); - void setLogoutButtonEnabled(in ComponentName admin, boolean enabled); - boolean isLogoutButtonEnabled(); + void setLogoutEnabled(in ComponentName admin, boolean enabled); + boolean isLogoutEnabled(); } diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index a3fe686c4a08..a2b7d5809c4d 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -25,17 +25,15 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Trace; +import java.util.Objects; + /** * Activity configuration changed callback. * @hide */ public class ActivityConfigurationChangeItem extends ClientTransactionItem { - private final Configuration mConfiguration; - - public ActivityConfigurationChangeItem(Configuration configuration) { - mConfiguration = configuration; - } + private Configuration mConfiguration; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -47,6 +45,29 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private ActivityConfigurationChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ActivityConfigurationChangeItem obtain(Configuration config) { + ActivityConfigurationChangeItem instance = + ObjectPool.obtain(ActivityConfigurationChangeItem.class); + if (instance == null) { + instance = new ActivityConfigurationChangeItem(); + } + instance.mConfiguration = config; + + return instance; + } + + @Override + public void recycle() { + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -80,7 +101,7 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { return false; } final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o; - return mConfiguration.equals(other.mConfiguration); + return Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index 3a3d5b981fb4..73b5ec440441 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -27,6 +27,7 @@ import android.os.Parcelable; import android.os.Trace; import java.util.List; +import java.util.Objects; /** * Activity result delivery callback. @@ -34,11 +35,7 @@ import java.util.List; */ public class ActivityResultItem extends ClientTransactionItem { - private final List<ResultInfo> mResultInfoList; - - public ActivityResultItem(List<ResultInfo> resultInfos) { - mResultInfoList = resultInfos; - } + private List<ResultInfo> mResultInfoList; @Override public int getPreExecutionState() { @@ -54,6 +51,28 @@ public class ActivityResultItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private ActivityResultItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ActivityResultItem obtain(List<ResultInfo> resultInfoList) { + ActivityResultItem instance = ObjectPool.obtain(ActivityResultItem.class); + if (instance == null) { + instance = new ActivityResultItem(); + } + instance.mResultInfoList = resultInfoList; + + return instance; + } + + @Override + public void recycle() { + mResultInfoList = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -87,7 +106,7 @@ public class ActivityResultItem extends ClientTransactionItem { return false; } final ActivityResultItem other = (ActivityResultItem) o; - return mResultInfoList.equals(other.mResultInfoList); + return Objects.equals(mResultInfoList, other.mResultInfoList); } @Override diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java index e3473bfd0ae8..c91e0ca5ffc1 100644 --- a/core/java/android/app/servertransaction/BaseClientRequest.java +++ b/core/java/android/app/servertransaction/BaseClientRequest.java @@ -24,7 +24,7 @@ import android.os.IBinder; * Each of them can be prepared before scheduling and, eventually, executed. * @hide */ -public interface BaseClientRequest { +public interface BaseClientRequest extends ObjectPoolItem { /** * Prepare the client request before scheduling. diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 7a5896261aac..764ceede5d20 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -37,7 +37,7 @@ import java.util.Objects; * @see ActivityLifecycleItem * @hide */ -public class ClientTransaction implements Parcelable { +public class ClientTransaction implements Parcelable, ObjectPoolItem { /** A list of individual callbacks to a client. */ private List<ClientTransactionItem> mActivityCallbacks; @@ -54,11 +54,6 @@ public class ClientTransaction implements Parcelable { /** Target client activity. Might be null if the entire transaction is targeting an app. */ private IBinder mActivityToken; - public ClientTransaction(IApplicationThread client, IBinder activityToken) { - mClient = client; - mActivityToken = activityToken; - } - /** * Add a message to the end of the sequence of callbacks. * @param activityCallback A single message that can contain a lifecycle request/callback. @@ -127,6 +122,41 @@ public class ClientTransaction implements Parcelable { } + // ObjectPoolItem implementation + + private ClientTransaction() {} + + /** Obtain an instance initialized with provided params. */ + public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) { + ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class); + if (instance == null) { + instance = new ClientTransaction(); + } + instance.mClient = client; + instance.mActivityToken = activityToken; + + return instance; + } + + @Override + public void recycle() { + if (mActivityCallbacks != null) { + int size = mActivityCallbacks.size(); + for (int i = 0; i < size; i++) { + mActivityCallbacks.get(i).recycle(); + } + mActivityCallbacks.clear(); + } + if (mLifecycleStateRequest != null) { + mLifecycleStateRequest.recycle(); + mLifecycleStateRequest = null; + } + mClient = null; + mActivityToken = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java index ee1effa6c2ab..4ab7251e4d8a 100644 --- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java @@ -21,17 +21,15 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * App configuration change message. * @hide */ public class ConfigurationChangeItem extends ClientTransactionItem { - private final Configuration mConfiguration; - - public ConfigurationChangeItem(Configuration configuration) { - mConfiguration = new Configuration(configuration); - } + private Configuration mConfiguration; @Override public void preExecute(android.app.ClientTransactionHandler client, IBinder token) { @@ -44,6 +42,29 @@ public class ConfigurationChangeItem extends ClientTransactionItem { client.handleConfigurationChanged(mConfiguration); } + + // ObjectPoolItem implementation + + private ConfigurationChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ConfigurationChangeItem obtain(Configuration config) { + ConfigurationChangeItem instance = ObjectPool.obtain(ConfigurationChangeItem.class); + if (instance == null) { + instance = new ConfigurationChangeItem(); + } + instance.mConfiguration = config; + + return instance; + } + + @Override + public void recycle() { + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -77,7 +98,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem { return false; } final ConfigurationChangeItem other = (ConfigurationChangeItem) o; - return mConfiguration.equals(other.mConfiguration); + return Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index 3012a7a474e6..83da5f33c62a 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -29,13 +29,8 @@ import android.os.Trace; */ public class DestroyActivityItem extends ActivityLifecycleItem { - private final boolean mFinished; - private final int mConfigChanges; - - public DestroyActivityItem(boolean finished, int configChanges) { - mFinished = finished; - mConfigChanges = configChanges; - } + private boolean mFinished; + private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -52,6 +47,30 @@ public class DestroyActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private DestroyActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static DestroyActivityItem obtain(boolean finished, int configChanges) { + DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class); + if (instance == null) { + instance = new DestroyActivityItem(); + } + instance.mFinished = finished; + instance.mConfigChanges = configChanges; + + return instance; + } + + @Override + public void recycle() { + mFinished = false; + mConfigChanges = 0; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index e39042f43d51..7be82bf9f505 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -45,43 +45,21 @@ import java.util.Objects; */ public class LaunchActivityItem extends ClientTransactionItem { - private final Intent mIntent; - private final int mIdent; - private final ActivityInfo mInfo; - private final Configuration mCurConfig; - private final Configuration mOverrideConfig; - private final CompatibilityInfo mCompatInfo; - private final String mReferrer; - private final IVoiceInteractor mVoiceInteractor; - private final int mProcState; - private final Bundle mState; - private final PersistableBundle mPersistentState; - private final List<ResultInfo> mPendingResults; - private final List<ReferrerIntent> mPendingNewIntents; - private final boolean mIsForward; - private final ProfilerInfo mProfilerInfo; - - public LaunchActivityItem(Intent intent, int ident, ActivityInfo info, - Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, - String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, - PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) { - mIntent = intent; - mIdent = ident; - mInfo = info; - mCurConfig = curConfig; - mOverrideConfig = overrideConfig; - mCompatInfo = compatInfo; - mReferrer = referrer; - mVoiceInteractor = voiceInteractor; - mProcState = procState; - mState = state; - mPersistentState = persistentState; - mPendingResults = pendingResults; - mPendingNewIntents = pendingNewIntents; - mIsForward = isForward; - mProfilerInfo = profilerInfo; - } + private Intent mIntent; + private int mIdent; + private ActivityInfo mInfo; + private Configuration mCurConfig; + private Configuration mOverrideConfig; + private CompatibilityInfo mCompatInfo; + private String mReferrer; + private IVoiceInteractor mVoiceInteractor; + private int mProcState; + private Bundle mState; + private PersistableBundle mPersistentState; + private List<ResultInfo> mPendingResults; + private List<ReferrerIntent> mPendingNewIntents; + private boolean mIsForward; + private ProfilerInfo mProfilerInfo; @Override public void preExecute(ClientTransactionHandler client, IBinder token) { @@ -102,6 +80,35 @@ public class LaunchActivityItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private LaunchActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info, + Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, + List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) { + LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); + if (instance == null) { + instance = new LaunchActivityItem(); + } + setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer, + voiceInteractor, procState, state, persistentState, pendingResults, + pendingNewIntents, isForward, profilerInfo); + + return instance; + } + + @Override + public void recycle() { + setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, + false, null); + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write from Parcel. */ @@ -114,7 +121,7 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeTypedObject(mOverrideConfig, flags); dest.writeTypedObject(mCompatInfo, flags); dest.writeString(mReferrer); - dest.writeStrongBinder(mVoiceInteractor != null ? mVoiceInteractor.asBinder() : null); + dest.writeStrongInterface(mVoiceInteractor); dest.writeInt(mProcState); dest.writeBundle(mState); dest.writePersistableBundle(mPersistentState); @@ -126,21 +133,16 @@ public class LaunchActivityItem extends ClientTransactionItem { /** Read from Parcel. */ private LaunchActivityItem(Parcel in) { - mIntent = in.readTypedObject(Intent.CREATOR); - mIdent = in.readInt(); - mInfo = in.readTypedObject(ActivityInfo.CREATOR); - mCurConfig = in.readTypedObject(Configuration.CREATOR); - mOverrideConfig = in.readTypedObject(Configuration.CREATOR); - mCompatInfo = in.readTypedObject(CompatibilityInfo.CREATOR); - mReferrer = in.readString(); - mVoiceInteractor = (IVoiceInteractor) in.readStrongBinder(); - mProcState = in.readInt(); - mState = in.readBundle(getClass().getClassLoader()); - mPersistentState = in.readPersistableBundle(getClass().getClassLoader()); - mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR); - mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); - mIsForward = in.readBoolean(); - mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR); + setValues(this, in.readTypedObject(Intent.CREATOR), in.readInt(), + in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR), + in.readTypedObject(Configuration.CREATOR), + in.readTypedObject(CompatibilityInfo.CREATOR), in.readString(), + IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(), + in.readBundle(getClass().getClassLoader()), + in.readPersistableBundle(getClass().getClassLoader()), + in.createTypedArrayList(ResultInfo.CREATOR), + in.createTypedArrayList(ReferrerIntent.CREATOR), in.readBoolean(), + in.readTypedObject(ProfilerInfo.CREATOR)); } public static final Creator<LaunchActivityItem> CREATOR = @@ -163,7 +165,9 @@ public class LaunchActivityItem extends ClientTransactionItem { return false; } final LaunchActivityItem other = (LaunchActivityItem) o; - return mIntent.filterEquals(other.mIntent) && mIdent == other.mIdent + final boolean intentsEqual = (mIntent == null && other.mIntent == null) + || (mIntent != null && mIntent.filterEquals(other.mIntent)); + return intentsEqual && mIdent == other.mIdent && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig) && Objects.equals(mOverrideConfig, other.mOverrideConfig) && Objects.equals(mCompatInfo, other.mCompatInfo) @@ -196,7 +200,11 @@ public class LaunchActivityItem extends ClientTransactionItem { } private boolean activityInfoEqual(ActivityInfo other) { - return mInfo.flags == other.flags && mInfo.maxAspectRatio == other.maxAspectRatio + if (mInfo == null) { + return other == null; + } + return other != null && mInfo.flags == other.flags + && mInfo.maxAspectRatio == other.maxAspectRatio && Objects.equals(mInfo.launchToken, other.launchToken) && Objects.equals(mInfo.getComponentName(), other.getComponentName()); } @@ -231,4 +239,28 @@ public class LaunchActivityItem extends ClientTransactionItem { + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo + "}"; } + + // Using the same method to set and clear values to make sure we don't forget anything + private static void setValues(LaunchActivityItem instance, Intent intent, int ident, + ActivityInfo info, Configuration curConfig, Configuration overrideConfig, + CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + int procState, Bundle state, PersistableBundle persistentState, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + boolean isForward, ProfilerInfo profilerInfo) { + instance.mIntent = intent; + instance.mIdent = ident; + instance.mInfo = info; + instance.mCurConfig = curConfig; + instance.mOverrideConfig = overrideConfig; + instance.mCompatInfo = compatInfo; + instance.mReferrer = referrer; + instance.mVoiceInteractor = voiceInteractor; + instance.mProcState = procState; + instance.mState = state; + instance.mPersistentState = persistentState; + instance.mPendingResults = pendingResults; + instance.mPendingNewIntents = pendingNewIntents; + instance.mIsForward = isForward; + instance.mProfilerInfo = profilerInfo; + } } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index ee87261217fa..b3dddfb37eaa 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -24,19 +24,16 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Trace; +import java.util.Objects; + /** * Activity move to a different display message. * @hide */ public class MoveToDisplayItem extends ClientTransactionItem { - private final int mTargetDisplayId; - private final Configuration mConfiguration; - - public MoveToDisplayItem(int targetDisplayId, Configuration configuration) { - mTargetDisplayId = targetDisplayId; - mConfiguration = configuration; - } + private int mTargetDisplayId; + private Configuration mConfiguration; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -47,6 +44,30 @@ public class MoveToDisplayItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private MoveToDisplayItem() {} + + /** Obtain an instance initialized with provided params. */ + public static MoveToDisplayItem obtain(int targetDisplayId, Configuration configuration) { + MoveToDisplayItem instance = ObjectPool.obtain(MoveToDisplayItem.class); + if (instance == null) { + instance = new MoveToDisplayItem(); + } + instance.mTargetDisplayId = targetDisplayId; + instance.mConfiguration = configuration; + + return instance; + } + + @Override + public void recycle() { + mTargetDisplayId = 0; + mConfiguration = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -82,7 +103,7 @@ public class MoveToDisplayItem extends ClientTransactionItem { } final MoveToDisplayItem other = (MoveToDisplayItem) o; return mTargetDisplayId == other.mTargetDisplayId - && mConfiguration.equals(other.mConfiguration); + && Objects.equals(mConfiguration, other.mConfiguration); } @Override diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java index 04ddb5e1378c..c3022d6facc7 100644 --- a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java +++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java @@ -21,6 +21,8 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * Multi-window mode change message. * @hide @@ -29,14 +31,8 @@ import android.os.Parcel; // communicate multi-window mode change with WindowConfiguration. public class MultiWindowModeChangeItem extends ClientTransactionItem { - private final boolean mIsInMultiWindowMode; - private final Configuration mOverrideConfig; - - public MultiWindowModeChangeItem(boolean isInMultiWindowMode, - Configuration overrideConfig) { - mIsInMultiWindowMode = isInMultiWindowMode; - mOverrideConfig = overrideConfig; - } + private boolean mIsInMultiWindowMode; + private Configuration mOverrideConfig; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -45,6 +41,31 @@ public class MultiWindowModeChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private MultiWindowModeChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static MultiWindowModeChangeItem obtain(boolean isInMultiWindowMode, + Configuration overrideConfig) { + MultiWindowModeChangeItem instance = ObjectPool.obtain(MultiWindowModeChangeItem.class); + if (instance == null) { + instance = new MultiWindowModeChangeItem(); + } + instance.mIsInMultiWindowMode = isInMultiWindowMode; + instance.mOverrideConfig = overrideConfig; + + return instance; + } + + @Override + public void recycle() { + mIsInMultiWindowMode = false; + mOverrideConfig = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -81,7 +102,7 @@ public class MultiWindowModeChangeItem extends ClientTransactionItem { } final MultiWindowModeChangeItem other = (MultiWindowModeChangeItem) o; return mIsInMultiWindowMode == other.mIsInMultiWindowMode - && mOverrideConfig.equals(other.mOverrideConfig); + && Objects.equals(mOverrideConfig, other.mOverrideConfig); } @Override diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index d01b455e5390..7dfde73c0534 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -25,6 +25,7 @@ import android.os.Trace; import com.android.internal.content.ReferrerIntent; import java.util.List; +import java.util.Objects; /** * New intent message. @@ -32,13 +33,8 @@ import java.util.List; */ public class NewIntentItem extends ClientTransactionItem { - private final List<ReferrerIntent> mIntents; - private final boolean mPause; - - public NewIntentItem(List<ReferrerIntent> intents, boolean pause) { - mIntents = intents; - mPause = pause; - } + private List<ReferrerIntent> mIntents; + private boolean mPause; // TODO(lifecycler): Switch new intent handling to this scheme. /*@Override @@ -60,6 +56,30 @@ public class NewIntentItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private NewIntentItem() {} + + /** Obtain an instance initialized with provided params. */ + public static NewIntentItem obtain(List<ReferrerIntent> intents, boolean pause) { + NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class); + if (instance == null) { + instance = new NewIntentItem(); + } + instance.mIntents = intents; + instance.mPause = pause; + + return instance; + } + + @Override + public void recycle() { + mIntents = null; + mPause = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -95,7 +115,7 @@ public class NewIntentItem extends ClientTransactionItem { return false; } final NewIntentItem other = (NewIntentItem) o; - return mPause == other.mPause && mIntents.equals(other.mIntents); + return mPause == other.mPause && Objects.equals(mIntents, other.mIntents); } @Override diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java new file mode 100644 index 000000000000..98121253f486 --- /dev/null +++ b/core/java/android/app/servertransaction/ObjectPool.java @@ -0,0 +1,73 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.servertransaction; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * An object pool that can provide reused objects if available. + * @hide + */ +class ObjectPool { + + private static final Object sPoolSync = new Object(); + private static final Map<Class, LinkedList<? extends ObjectPoolItem>> sPoolMap = + new HashMap<>(); + + private static final int MAX_POOL_SIZE = 50; + + /** + * Obtain an instance of a specific class from the pool + * @param itemClass The class of the object we're looking for. + * @return An instance or null if there is none. + */ + public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) { + synchronized (sPoolSync) { + @SuppressWarnings("unchecked") + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(itemClass); + if (itemPool != null && !itemPool.isEmpty()) { + return itemPool.poll(); + } + return null; + } + } + + /** + * Recycle the object to the pool. The object should be properly cleared before this. + * @param item The object to recycle. + * @see ObjectPoolItem#recycle() + */ + public static <T extends ObjectPoolItem> void recycle(T item) { + synchronized (sPoolSync) { + @SuppressWarnings("unchecked") + LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(item.getClass()); + if (itemPool == null) { + itemPool = new LinkedList<>(); + sPoolMap.put(item.getClass(), itemPool); + } + if (itemPool.contains(item)) { + throw new IllegalStateException("Trying to recycle already recycled item"); + } + + if (itemPool.size() < MAX_POOL_SIZE) { + itemPool.add(item); + } + } + } +} diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java new file mode 100644 index 000000000000..17bd4f30640f --- /dev/null +++ b/core/java/android/app/servertransaction/ObjectPoolItem.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.servertransaction; + +/** + * Base interface for all lifecycle items that can be put in object pool. + * @hide + */ +public interface ObjectPoolItem { + /** + * Clear the contents of the item and putting it to a pool. The implementation should call + * {@link ObjectPool#recycle(ObjectPoolItem)} passing itself. + */ + void recycle(); +} diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 634da04a09bf..880fef73c6f2 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -33,23 +33,10 @@ public class PauseActivityItem extends ActivityLifecycleItem { private static final String TAG = "PauseActivityItem"; - private final boolean mFinished; - private final boolean mUserLeaving; - private final int mConfigChanges; - private final boolean mDontReport; - - public PauseActivityItem() { - this(false /* finished */, false /* userLeaving */, 0 /* configChanges */, - true /* dontReport */); - } - - public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges, - boolean dontReport) { - mFinished = finished; - mUserLeaving = userLeaving; - mConfigChanges = configChanges; - mDontReport = dontReport; - } + private boolean mFinished; + private boolean mUserLeaving; + private int mConfigChanges; + private boolean mDontReport; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -79,6 +66,49 @@ public class PauseActivityItem extends ActivityLifecycleItem { } } + + // ObjectPoolItem implementation + + private PauseActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static PauseActivityItem obtain(boolean finished, boolean userLeaving, int configChanges, + boolean dontReport) { + PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); + if (instance == null) { + instance = new PauseActivityItem(); + } + instance.mFinished = finished; + instance.mUserLeaving = userLeaving; + instance.mConfigChanges = configChanges; + instance.mDontReport = dontReport; + + return instance; + } + + /** Obtain an instance initialized with default params. */ + public static PauseActivityItem obtain() { + PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); + if (instance == null) { + instance = new PauseActivityItem(); + } + instance.mFinished = false; + instance.mUserLeaving = false; + instance.mConfigChanges = 0; + instance.mDontReport = true; + + return instance; + } + + @Override + public void recycle() { + mFinished = false; + mUserLeaving = false; + mConfigChanges = 0; + mDontReport = false; + ObjectPool.recycle(this); + } + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java index 7c74e6f76656..b999cd7e295e 100644 --- a/core/java/android/app/servertransaction/PipModeChangeItem.java +++ b/core/java/android/app/servertransaction/PipModeChangeItem.java @@ -21,6 +21,8 @@ import android.content.res.Configuration; import android.os.IBinder; import android.os.Parcel; +import java.util.Objects; + /** * Picture in picture mode change message. * @hide @@ -29,13 +31,8 @@ import android.os.Parcel; // communicate multi-window mode change with WindowConfiguration. public class PipModeChangeItem extends ClientTransactionItem { - private final boolean mIsInPipMode; - private final Configuration mOverrideConfig; - - public PipModeChangeItem(boolean isInPipMode, Configuration overrideConfig) { - mIsInPipMode = isInPipMode; - mOverrideConfig = overrideConfig; - } + private boolean mIsInPipMode; + private Configuration mOverrideConfig; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -44,6 +41,30 @@ public class PipModeChangeItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private PipModeChangeItem() {} + + /** Obtain an instance initialized with provided params. */ + public static PipModeChangeItem obtain(boolean isInPipMode, Configuration overrideConfig) { + PipModeChangeItem instance = ObjectPool.obtain(PipModeChangeItem.class); + if (instance == null) { + instance = new PipModeChangeItem(); + } + instance.mIsInPipMode = isInPipMode; + instance.mOverrideConfig = overrideConfig; + + return instance; + } + + @Override + public void recycle() { + mIsInPipMode = false; + mOverrideConfig = null; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ @@ -78,7 +99,8 @@ public class PipModeChangeItem extends ClientTransactionItem { return false; } final PipModeChangeItem other = (PipModeChangeItem) o; - return mIsInPipMode == other.mIsInPipMode && mOverrideConfig.equals(other.mOverrideConfig); + return mIsInPipMode == other.mIsInPipMode + && Objects.equals(mOverrideConfig, other.mOverrideConfig); } @Override diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index d659b80bb54f..9249c6e8ed54 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -33,21 +33,9 @@ public class ResumeActivityItem extends ActivityLifecycleItem { private static final String TAG = "ResumeActivityItem"; - private final int mProcState; - private final boolean mUpdateProcState; - private final boolean mIsForward; - - public ResumeActivityItem(boolean isForward) { - mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; - mUpdateProcState = false; - mIsForward = isForward; - } - - public ResumeActivityItem(int procState, boolean isForward) { - mProcState = procState; - mUpdateProcState = true; - mIsForward = isForward; - } + private int mProcState; + private boolean mUpdateProcState; + private boolean mIsForward; @Override public void preExecute(ClientTransactionHandler client, IBinder token) { @@ -81,6 +69,45 @@ public class ResumeActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private ResumeActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static ResumeActivityItem obtain(int procState, boolean isForward) { + ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); + if (instance == null) { + instance = new ResumeActivityItem(); + } + instance.mProcState = procState; + instance.mUpdateProcState = true; + instance.mIsForward = isForward; + + return instance; + } + + /** Obtain an instance initialized with provided params. */ + public static ResumeActivityItem obtain(boolean isForward) { + ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); + if (instance == null) { + instance = new ResumeActivityItem(); + } + instance.mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; + instance.mUpdateProcState = false; + instance.mIsForward = isForward; + + return instance; + } + + @Override + public void recycle() { + mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; + mUpdateProcState = false; + mIsForward = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index 6e49386b9c38..5c5c3041344f 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -31,13 +31,8 @@ public class StopActivityItem extends ActivityLifecycleItem { private static final String TAG = "StopActivityItem"; - private final boolean mShowWindow; - private final int mConfigChanges; - - public StopActivityItem(boolean showWindow, int configChanges) { - mShowWindow = showWindow; - mConfigChanges = configChanges; - } + private boolean mShowWindow; + private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -59,6 +54,30 @@ public class StopActivityItem extends ActivityLifecycleItem { } + // ObjectPoolItem implementation + + private StopActivityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static StopActivityItem obtain(boolean showWindow, int configChanges) { + StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); + if (instance == null) { + instance = new StopActivityItem(); + } + instance.mShowWindow = showWindow; + instance.mConfigChanges = configChanges; + + return instance; + } + + @Override + public void recycle() { + mShowWindow = false; + mConfigChanges = 0; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index bd24824f142d..5b0ea6b1f9d4 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -243,8 +243,6 @@ public class TransactionExecutor { } private static void log(String message) { - if (DEBUG_RESOLVER) { - Slog.d(TAG, message); - } + if (DEBUG_RESOLVER) Slog.d(TAG, message); } } diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java index 6fcdcfa9efd6..d9956b1348b1 100644 --- a/core/java/android/app/servertransaction/WindowVisibilityItem.java +++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java @@ -29,11 +29,7 @@ import android.os.Trace; */ public class WindowVisibilityItem extends ClientTransactionItem { - private final boolean mShowWindow; - - public WindowVisibilityItem(boolean showWindow) { - mShowWindow = showWindow; - } + private boolean mShowWindow; @Override public void execute(ClientTransactionHandler client, IBinder token, @@ -44,6 +40,28 @@ public class WindowVisibilityItem extends ClientTransactionItem { } + // ObjectPoolItem implementation + + private WindowVisibilityItem() {} + + /** Obtain an instance initialized with provided params. */ + public static WindowVisibilityItem obtain(boolean showWindow) { + WindowVisibilityItem instance = ObjectPool.obtain(WindowVisibilityItem.class); + if (instance == null) { + instance = new WindowVisibilityItem(); + } + instance.mShowWindow = showWindow; + + return instance; + } + + @Override + public void recycle() { + mShowWindow = false; + ObjectPool.recycle(this); + } + + // Parcelable implementation /** Write to Parcel. */ diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index ddc5760a6b3d..9b3b4624f42e 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -37,6 +37,8 @@ import android.os.RemoteException; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -53,9 +55,21 @@ public final class Slice implements Parcelable { /** * @hide */ - @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED, - HINT_NO_TINT, HINT_PARTIAL}) - public @interface SliceHint{ } + @StringDef(prefix = { "HINT_" }, value = { + HINT_TITLE, + HINT_LIST, + HINT_LIST_ITEM, + HINT_LARGE, + HINT_ACTIONS, + HINT_SELECTED, + HINT_NO_TINT, + HINT_HIDDEN, + HINT_TOGGLE, + HINT_HORIZONTAL, + HINT_PARTIAL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceHint {} /** * The meta-data key that allows an activity to easily be linked directly to a slice. @@ -129,6 +143,11 @@ public final class Slice implements Parcelable { * OS and should not be cached by apps. */ public static final String HINT_PARTIAL = "partial"; + /** + * A hint representing that this item is the max value possible for the slice containing this. + * Used to indicate the maximum integer value for a {@link #SUBTYPE_SLIDER}. + */ + public static final String HINT_MAX = "max"; /** * Key to retrieve an extra added to an intent when a control is changed. @@ -144,6 +163,14 @@ public final class Slice implements Parcelable { * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}. */ public static final String SUBTYPE_SOURCE = "source"; + /** + * Subtype to tag an item as representing a color. + */ + public static final String SUBTYPE_COLOR = "color"; + /** + * Subtype to tag an item represents a slider. + */ + public static final String SUBTYPE_SLIDER = "slider"; private final SliceItem[] mItems; private final @SliceHint String[] mHints; @@ -375,9 +402,10 @@ public final class Slice implements Parcelable { * Add a color to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} + * @deprecated will be removed once supportlib updates */ public Builder addColor(int color, @Nullable String subType, @SliceHint String... hints) { - mItems.add(new SliceItem(color, SliceItem.FORMAT_COLOR, subType, hints)); + mItems.add(new SliceItem(color, SliceItem.FORMAT_INT, subType, hints)); return this; } @@ -385,6 +413,7 @@ public final class Slice implements Parcelable { * Add a color to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} + * @deprecated will be removed once supportlib updates */ public Builder addColor(int color, @Nullable String subType, @SliceHint List<String> hints) { @@ -392,6 +421,26 @@ public final class Slice implements Parcelable { } /** + * Add a color to the slice being constructed + * @param subType Optional template-specific type information + * @see {@link SliceItem#getSubType()} + */ + public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) { + mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints)); + return this; + } + + /** + * Add a color to the slice being constructed + * @param subType Optional template-specific type information + * @see {@link SliceItem#getSubType()} + */ + public Builder addInt(int value, @Nullable String subType, + @SliceHint List<String> hints) { + return addInt(value, subType, hints.toArray(new String[hints.size()])); + } + + /** * Add a timestamp to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java index cdeee3575756..5d04c3e813b1 100644 --- a/core/java/android/app/slice/SliceItem.java +++ b/core/java/android/app/slice/SliceItem.java @@ -29,6 +29,8 @@ import android.widget.RemoteViews; import com.android.internal.util.ArrayUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; @@ -42,7 +44,7 @@ import java.util.List; * <li>{@link #FORMAT_TEXT}</li> * <li>{@link #FORMAT_IMAGE}</li> * <li>{@link #FORMAT_ACTION}</li> - * <li>{@link #FORMAT_COLOR}</li> + * <li>{@link #FORMAT_INT}</li> * <li>{@link #FORMAT_TIMESTAMP}</li> * <li>{@link #FORMAT_REMOTE_INPUT}</li> * @@ -55,8 +57,16 @@ public final class SliceItem implements Parcelable { /** * @hide */ - @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_COLOR, - FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT}) + @StringDef(prefix = { "FORMAT_" }, value = { + FORMAT_SLICE, + FORMAT_TEXT, + FORMAT_IMAGE, + FORMAT_ACTION, + FORMAT_INT, + FORMAT_TIMESTAMP, + FORMAT_REMOTE_INPUT, + }) + @Retention(RetentionPolicy.SOURCE) public @interface SliceType {} /** @@ -79,7 +89,12 @@ public final class SliceItem implements Parcelable { */ public static final String FORMAT_ACTION = "action"; /** - * A {@link SliceItem} that contains a Color int. + * A {@link SliceItem} that contains an int. + */ + public static final String FORMAT_INT = "int"; + /** + * A {@link SliceItem} that contains an int. + * @deprecated to be removed */ public static final String FORMAT_COLOR = "color"; /** @@ -149,7 +164,7 @@ public final class SliceItem implements Parcelable { * <li>{@link #FORMAT_TEXT}</li> * <li>{@link #FORMAT_IMAGE}</li> * <li>{@link #FORMAT_ACTION}</li> - * <li>{@link #FORMAT_COLOR}</li> + * <li>{@link #FORMAT_INT}</li> * <li>{@link #FORMAT_TIMESTAMP}</li> * <li>{@link #FORMAT_REMOTE_INPUT}</li> * @see #getSubType() () @@ -206,7 +221,14 @@ public final class SliceItem implements Parcelable { } /** - * @return The color held by this {@link #FORMAT_COLOR} SliceItem + * @return The color held by this {@link #FORMAT_INT} SliceItem + */ + public int getInt() { + return (Integer) mObj; + } + + /** + * @deprecated to be removed. */ public int getColor() { return (Integer) mObj; @@ -308,7 +330,7 @@ public final class SliceItem implements Parcelable { case FORMAT_TEXT: TextUtils.writeToParcel((CharSequence) obj, dest, flags); break; - case FORMAT_COLOR: + case FORMAT_INT: dest.writeInt((Integer) obj); break; case FORMAT_TIMESTAMP: @@ -329,7 +351,7 @@ public final class SliceItem implements Parcelable { return new Pair<>( PendingIntent.CREATOR.createFromParcel(in), Slice.CREATOR.createFromParcel(in)); - case FORMAT_COLOR: + case FORMAT_INT: return in.readInt(); case FORMAT_TIMESTAMP: return in.readLong(); diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java index ad9b698a8fd7..417e7d26f4f5 100644 --- a/core/java/android/app/timezone/RulesManager.java +++ b/core/java/android/app/timezone/RulesManager.java @@ -105,9 +105,9 @@ public final class RulesManager { */ public RulesState getRulesState() { try { - logDebug("sIRulesManager.getRulesState()"); + logDebug("mIRulesManager.getRulesState()"); RulesState rulesState = mIRulesManager.getRulesState(); - logDebug("sIRulesManager.getRulesState() returned " + rulesState); + logDebug("mIRulesManager.getRulesState() returned " + rulesState); return rulesState; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -131,7 +131,7 @@ public final class RulesManager { ICallback iCallback = new CallbackWrapper(mContext, callback); try { - logDebug("sIRulesManager.requestInstall()"); + logDebug("mIRulesManager.requestInstall()"); return mIRulesManager.requestInstall(distroFileDescriptor, checkToken, iCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -151,7 +151,7 @@ public final class RulesManager { public int requestUninstall(byte[] checkToken, Callback callback) { ICallback iCallback = new CallbackWrapper(mContext, callback); try { - logDebug("sIRulesManager.requestUninstall()"); + logDebug("mIRulesManager.requestUninstall()"); return mIRulesManager.requestUninstall(checkToken, iCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -196,7 +196,7 @@ public final class RulesManager { */ public void requestNothing(byte[] checkToken, boolean succeeded) { try { - logDebug("sIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken)); + logDebug("mIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken)); mIRulesManager.requestNothing(checkToken, succeeded); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java new file mode 100644 index 000000000000..7ab3d8bdd857 --- /dev/null +++ b/core/java/android/appwidget/AppWidgetManagerInternal.java @@ -0,0 +1,39 @@ +/* + * 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.appwidget; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArraySet; + +import java.util.Set; + +/** + * App widget manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class AppWidgetManagerInternal { + + /** + * Gets the packages from which the uid hosts widgets. + * + * @param uid The potential host UID. + * @return Whether the UID hosts widgets from the package. + */ + public abstract @Nullable ArraySet<String> getHostedWidgetPackages(int uid); +} diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 1241f2306c73..838d3153d54c 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -678,33 +678,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Get battery usage hint for Bluetooth Headset service. - * This is a monotonically increasing integer. Wraps to 0 at - * Integer.MAX_INT, and at boot. - * Current implementation returns the number of AT commands handled since - * boot. This is a good indicator for spammy headset/handsfree units that - * can keep the device awake by polling for cellular status updates. As a - * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms - * - * @param device the bluetooth headset. - * @return monotonically increasing battery usage hint, or a negative error code on error - * @hide - */ - public int getBatteryUsageHint(BluetoothDevice device) { - if (VDBG) log("getBatteryUsageHint()"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getBatteryUsageHint(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return -1; - } - - /** * Indicates if current platform supports voice dialing over bluetooth SCO. * * @return true if voice dialing over bluetooth is supported, false otherwise. @@ -716,49 +689,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Accept the incoming connection. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - public boolean acceptIncomingConnect(BluetoothDevice device) { - if (DBG) log("acceptIncomingConnect"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.acceptIncomingConnect(device); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } - return false; - } - - /** - * Reject the incoming connection. - * - * @hide - */ - public boolean rejectIncomingConnect(BluetoothDevice device) { - if (DBG) log("rejectIncomingConnect"); - final IBluetoothHeadset service = mService; - if (service != null) { - try { - return service.rejectIncomingConnect(device); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } - return false; - } - - /** * Get the current audio state of the Headset. * Note: This is an internal function and shouldn't be exposed * @@ -1053,50 +983,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * enable WBS codec setting. - * - * @return true if successful false if there was some error such as there is no connected - * headset - * @hide - */ - public boolean enableWBS() { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.enableWBS(); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } - return false; - } - - /** - * disable WBS codec settting. It set NBS codec. - * - * @return true if successful false if there was some error such as there is no connected - * headset - * @hide - */ - public boolean disableWBS() { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.disableWBS(); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } - return false; - } - - /** * check if in-band ringing is supported for this platform. * * @return true if in-band ringing is supported false if in-band ringing is not supported @@ -1107,30 +993,6 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); } - /** - * Send Headset the BIND response from AG to report change in the status of the - * HF indicators to the headset - * - * @param indId Assigned Number of the indicator (defined by SIG) - * @param indStatus possible values- false-Indicator is disabled, no value changes shall be - * sent for this indicator true-Indicator is enabled, value changes may be sent for this - * indicator - * @hide - */ - public void bindResponse(int indId, boolean indStatus) { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - service.bindResponse(indId, indStatus); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } - } - private final IBluetoothProfileServiceConnection mConnection = new IBluetoothProfileServiceConnection.Stub() { @Override diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index bfee27974f57..4a0048673c28 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -31,36 +31,33 @@ import java.util.Arrays; import java.util.List; /** - * Provides the public APIs to control the Bluetooth HID Device - * profile. + * Provides the public APIs to control the Bluetooth HID Device profile. * - * BluetoothHidDevice is a proxy object for controlling the Bluetooth HID - * Device Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHidDevice proxy object. + * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. + * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. * - * {@hide} + * <p>{@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** - * Intent used to broadcast the change in connection state of the Input - * Host profile. + * Intent used to broadcast the change in connection state of the Input Host profile. * * <p>This intent will have 3 extras: + * * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * <li>{@link #EXTRA_STATE} - The current state of the profile. + * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. + * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device. * </ul> * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link + * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link + * #STATE_DISCONNECTING}. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = @@ -69,9 +66,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Constants representing device subclass. * - * @see #registerApp - * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; @@ -110,8 +106,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { public static final byte ERROR_RSP_UNKNOWN = (byte) 14; /** - * Constants representing protocol mode used set by host. Default is always - * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. + * Constants representing protocol mode used set by host. Default is always {@link + * #PROTOCOL_REPORT_MODE} unless notified otherwise. * * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte) */ @@ -126,8 +122,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { private BluetoothAdapter mAdapter; - private static class BluetoothHidDeviceCallbackWrapper extends - IBluetoothHidDeviceCallback.Stub { + private static class BluetoothHidDeviceCallbackWrapper + extends IBluetoothHidDeviceCallback.Stub { private BluetoothHidDeviceCallback mCallback; @@ -184,13 +180,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { doBind(); } } catch (IllegalStateException e) { - Log.e(TAG, - "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); + Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", e); } catch (SecurityException e) { - Log.e(TAG, - "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); + Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", e); } } else { Log.d(TAG, "Unbinding service..."); @@ -200,23 +194,25 @@ public final class BluetoothHidDevice implements BluetoothProfile { } }; - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, - BluetoothHidDevice.this); - } - } - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } - } - }; + private final ServiceConnection mConnection = + new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected()"); + mService = IBluetoothHidDevice.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected( + BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected()"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + } + } + }; BluetoothHidDevice(Context context, ServiceListener listener) { Log.v(TAG, "BluetoothHidDevice"); @@ -280,9 +276,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { mServiceListener = null; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public List<BluetoothDevice> getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); @@ -301,9 +295,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); @@ -322,9 +314,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); @@ -344,33 +334,31 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Registers application to be used for HID device. Connections to HID - * Device are only possible when application is registered. Only one - * application can be registered at time. When no longer used, application - * should be unregistered using - * {@link #unregisterApp()}. - * The registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related - * to the return value of this method. + * Registers application to be used for HID device. Connections to HID Device are only possible + * when application is registered. Only one application can be registered at one time. When an + * application is registered, the HID Host service will be disabled until it is unregistered. + * When no longer used, application should be unregistered using {@link #unregisterApp()}. The + * registration status should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to + * the return value of this method. * - * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. - * The HID Device SDP record is required. - * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. - * The Incoming QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. - * The Outgoing QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID + * Device SDP record is required. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The + * Incoming QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The + * Outgoing QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be - * sent. - * The BluetoothHidDeviceCallback object is required. + * sent. The BluetoothHidDeviceCallback object is required. * @return true if the command is successfully sent; otherwise false. */ public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, BluetoothHidDeviceCallback callback) { Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos - + " callback=" + callback); + + " callback=" + callback); boolean result = false; @@ -395,14 +383,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Unregisters application. Active connection will be disconnected and no - * new connections will be allowed until registered again using - * {@link #registerApp + * Unregisters application. Active connection will be disconnected and no new connections will + * be allowed until registered again using {@link #registerApp * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} - * The registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related - * to the return value of this method. + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should + * be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to + * the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ @@ -429,7 +416,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Sends report to remote host using interrupt channel. * * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in - * descriptor. + * descriptor. * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ @@ -451,8 +438,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Sends report to remote host as reply for GET_REPORT request from - * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. + * Sends report to remote host as reply for GET_REPORT request from {@link + * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. * * @param type Report Type, as in request. * @param id Report Id, as in request. @@ -479,8 +466,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Sends error handshake message as reply for invalid SET_REPORT request - * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. + * Sends error handshake message as reply for invalid SET_REPORT request from {@link + * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. @@ -508,6 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Sends Virtual Cable Unplug to currently connected host. * * @return + * {@hide} */ public boolean unplug(BluetoothDevice device) { Log.v(TAG, "unplug(): device=" + device); @@ -529,11 +517,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Initiates connection to host which is currently paired with this device. - * If the application is not registered, #connect(BluetoothDevice) will fail. - * The connection state should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related - * to the return value of this method. + * Initiates connection to host which is currently paired with this device. If the application + * is not registered, #connect(BluetoothDevice) will fail. The connection state should be + * tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to + * the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ @@ -557,10 +545,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Disconnects from currently connected host. - * The connection state should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related - * to the return value of this method. + * Disconnects from currently connected host. The connection state should be tracked by the + * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged. + * The connection state is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 881ae98d9889..4609d52df0c0 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -20,15 +20,14 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device - * application. + * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application. * - * The BluetoothHidDevice framework will update the L2CAP QoS settings for the - * app during registration. + * <p>The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during + * registration. * - * {@see BluetoothHidDevice} + * <p>{@see BluetoothHidDevice} * - * {@hide} + * <p>{@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,13 +45,12 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public static final int MAX = (int) 0xffffffff; /** - * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. - * The QoS Settings is optional. - * Recommended to use BluetoothHidDeviceAppQosSettings.Builder. - * {@see <a href="https://www.bluetooth.com/specifications/profiles-overview"> - * https://www.bluetooth.com/specifications/profiles-overview - * </a> - * Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D } + * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a + * href="https://www.bluetooth.com/specifications/profiles-overview"> + * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication + * v1.1.1 Section 5.2 and Appendix D } + * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate * @param tokenBucketSize L2CAP token bucket size @@ -123,13 +121,11 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** @return an int array representation of this instance */ public int[] toArray() { return new int[] { - serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation + serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation }; } - /** - * A helper to build the BluetoothHidDeviceAppQosSettings object. - */ + /** A helper to build the BluetoothHidDeviceAppQosSettings object. */ public static class Builder { // Optional parameters - initialized to default values private int mServiceType = SERVICE_BEST_EFFORT; @@ -141,8 +137,9 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the service type. + * * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT, - * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. + * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. * @return BluetoothHidDeviceAppQosSettings Builder with specified service type. */ public Builder serviceType(int val) { @@ -151,6 +148,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } /** * Set the token rate. + * * @param val token rate * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate. */ @@ -161,6 +159,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the bucket size. + * * @param val bucket size * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size. */ @@ -171,6 +170,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the peak bandwidth. + * * @param val peak bandwidth * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth. */ @@ -180,6 +180,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } /** * Set the latency. + * * @param val latency * @return BluetoothHidDeviceAppQosSettings Builder with specified latency. */ @@ -190,6 +191,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the delay variation. + * * @param val delay variation * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation. */ @@ -200,6 +202,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Build the BluetoothHidDeviceAppQosSettings object. + * * @return BluetoothHidDeviceAppQosSettings object with current settings. */ public BluetoothHidDeviceAppQosSettings build() { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 4669637043af..2da64e5a5023 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -22,16 +22,14 @@ import android.os.Parcelable; import java.util.Arrays; /** - * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth - * HID Device application. + * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application. * - * The BluetoothHidDevice framework adds the SDP record during app - * registration, so that the Android device can be discovered as a Bluetooth - * HID Device. + * <p>The BluetoothHidDevice framework adds the SDP record during app registration, so that the + * Android device can be discovered as a Bluetooth HID Device. * - * {@see BluetoothHidDevice} + * <p>{@see BluetoothHidDevice} * - * {@hide} + * <p>{@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @@ -43,18 +41,19 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. + * * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes. * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes. * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes. - * @param subclass Subclass of this Bluetooth HID device. - * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf"> + * @param subclass Subclass of this Bluetooth HID device. See <a + * href="www.usb.org/developers/hidpage/HID1_11.pdf"> * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a> - * @param descriptors Descriptors of this Bluetooth HID device. - * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf"> + * @param descriptors Descriptors of this Bluetooth HID device. See <a + * href="www.usb.org/developers/hidpage/HID1_11.pdf"> * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes. */ - public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, - byte subclass, byte[] descriptors) { + public BluetoothHidDeviceAppSdpSettings( + String name, String description, String provider, byte subclass, byte[] descriptors) { this.name = name; this.description = description; this.provider = provider; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java index dc6f9fa1abf3..6ed19654b4c5 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -19,46 +19,43 @@ package android.bluetooth; import android.util.Log; /** - * The template class that applications use to call callback functions on - * events from the HID host. Callback functions are wrapped in this class and - * registered to the Android system during app registration. + * The template class that applications use to call callback functions on events from the HID host. + * Callback functions are wrapped in this class and registered to the Android system during app + * registration. * - * {@see BluetoothHidDevice} + * <p>{@see BluetoothHidDevice} * - * {@hide} + * <p>{@hide} */ public abstract class BluetoothHidDeviceCallback { private static final String TAG = "BluetoothHidDevCallback"; /** - * Callback called when application registration state changes. Usually it's - * called due to either - * {@link BluetoothHidDevice#registerApp - * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} - * or - * {@link BluetoothHidDevice#unregisterApp()} - * , but can be also unsolicited in case e.g. Bluetooth was turned off in - * which case application is unregistered automatically. + * Callback called when application registration state changes. Usually it's called due to + * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], + * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also + * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered + * automatically. * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has - * Virtual Cable established with device. Only valid when application is registered, can be - * <code>null</code>. + * Virtual Cable established with device. Only valid when application is registered, can be + * <code>null</code>. * @param registered <code>true</code> if application is registered, <code>false</code> - * otherwise. + * otherwise. */ public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" - + registered); + Log.d(TAG, + "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); } /** - * Callback called when connection state with remote host was changed. - * Application can assume than Virtual Cable is established when called with - * {@link BluetoothProfile#STATE_CONNECTED} <code>state</code>. + * Callback called when connection state with remote host was changed. Application can assume + * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED} + * <code>state</code>. * * @param device {@link BluetoothDevice} object representing host device which connection state - * was changed. + * was changed. * @param state Connection state as defined in {@link BluetoothProfile}. */ public void onConnectionStateChanged(BluetoothDevice device, int state) { @@ -66,14 +63,14 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when GET_REPORT is received from remote host. Should be - * replied by application using - * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}. + * Callback called when GET_REPORT is received from remote host. Should be replied by + * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, + * byte[])}. * * @param type Requested Report Type. * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. * @param bufferSize Requested buffer size, application shall respond with at least given number - * of bytes. + * of bytes. */ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" @@ -81,9 +78,9 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when SET_REPORT is received from remote host. In case - * received data are invalid, application shall respond with - * {@link BluetoothHidDevice#reportError(BluetoothDevice, byte)}. + * Callback called when SET_REPORT is received from remote host. In case received data are + * invalid, application shall respond with {@link + * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. * * @param type Report Type. * @param id Report Id. @@ -94,10 +91,9 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when SET_PROTOCOL is received from remote host. - * Application shall use this information to send only reports valid for - * given protocol mode. By default, - * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * Callback called when SET_PROTOCOL is received from remote host. Application shall use this + * information to send only reports valid for given protocol mode. By default, {@link + * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. * * @param protocol Protocol Mode. */ @@ -106,9 +102,8 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when report data is received over interrupt channel. - * Report Type is assumed to be - * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * Callback called when report data is received over interrupt channel. Report Type is assumed + * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. * * @param reportId Report Id. * @param data Report data. @@ -118,10 +113,8 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when Virtual Cable is removed. This can be either due to - * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote - * side. After this callback is received connection will be disconnected - * automatically. + * Callback called when Virtual Cable is removed. After this callback is + * received connection will be disconnected automatically. */ public void onVirtualCableUnplug(BluetoothDevice device) { Log.d(TAG, "onVirtualCableUnplug: device=" + device); diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 55c3a730a833..73a2e74de5b3 100644 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -71,7 +71,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mSyncHandle); - dest.writeLong(mTxPower); + dest.writeInt(mTxPower); dest.writeInt(mRssi); dest.writeInt(mDataStatus); if (mData != null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9800ab0041b7..a47433093988 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2913,7 +2913,7 @@ public abstract class Context { @Nullable String profileFile, @Nullable Bundle arguments); /** @hide */ - @StringDef({ + @StringDef(suffix = { "_SERVICE" }, value = { POWER_SERVICE, WINDOW_SERVICE, LAYOUT_INFLATER_SERVICE, @@ -2947,7 +2947,7 @@ public abstract class Context { //@hide: LOWPAN_SERVICE, //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, - WIFI_RTT_SERVICE, + WIFI_RTT_RANGING_SERVICE, NSD_SERVICE, AUDIO_SERVICE, FINGERPRINT_SERVICE, @@ -3496,7 +3496,7 @@ public abstract class Context { * @see android.net.wifi.rtt.WifiRttManager * @hide */ - public static final String WIFI_RTT2_SERVICE = "rttmanager2"; + public static final String WIFI_RTT_RANGING_SERVICE = "rttmanager2"; /** * Use with {@link #getSystemService} to retrieve a {@link diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 9e54e235325c..d09ba0b55a3c 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -265,6 +265,14 @@ public class LauncherApps { /** * Include pinned shortcuts in the result. + * + * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the + * user owns on the launcher (or by other launchers, in case the user has multiple), use + * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. + * + * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other + * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this + * flag to get own pinned shortcuts. */ public static final int FLAG_MATCH_PINNED = 1 << 1; @@ -285,8 +293,15 @@ public class LauncherApps { * Include all pinned shortcuts by any launchers, not just by the caller, * in the result. * - * The caller must be the selected assistant app to use this flag, or have the system + * <p>The caller must be the selected assistant app to use this flag, or have the system * {@code ACCESS_SHORTCUTS} permission. + * + * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the + * user owns on the launcher (or by other launchers, in case the user has multiple), use + * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead. + * + * <p>If you're a regular launcher app (or any app that's not the selected assistant app) + * then this flag will be ignored. */ public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10; diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 0c893b0ff06a..5a91e94781d7 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -16,10 +16,14 @@ package android.content.pm; +import android.annotation.IntDef; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Overall information about the contents of a package. This corresponds * to all of the information collected from AndroidManifest.xml. @@ -330,8 +334,29 @@ public class PackageInfo implements Parcelable { /** @hide */ public int overlayPriority; - /** @hide */ - public boolean isStaticOverlay; + /** + * Flag for use with {@link #mOverlayFlags}. Marks the overlay as static, meaning it cannot + * be enabled/disabled at runtime. + */ + static final int FLAG_OVERLAY_STATIC = 1 << 1; + + /** + * Flag for use with {@link #mOverlayFlags}. Marks the overlay as trusted (not 3rd party). + */ + static final int FLAG_OVERLAY_TRUSTED = 1 << 2; + + @IntDef(flag = true, prefix = "FLAG_OVERLAY_", value = { + FLAG_OVERLAY_STATIC, + FLAG_OVERLAY_TRUSTED + }) + @Retention(RetentionPolicy.SOURCE) + @interface OverlayFlags {} + + /** + * Modifiers that affect the state of this overlay. See {@link #FLAG_OVERLAY_STATIC}, + * {@link #FLAG_OVERLAY_TRUSTED}. + */ + @OverlayFlags int mOverlayFlags; /** * The user-visible SDK version (ex. 26) of the framework against which the application claims @@ -359,6 +384,23 @@ public class PackageInfo implements Parcelable { public PackageInfo() { } + /** + * Returns true if the package is a valid Runtime Overlay package. + * @hide + */ + public boolean isOverlayPackage() { + return overlayTarget != null && (mOverlayFlags & FLAG_OVERLAY_TRUSTED) != 0; + } + + /** + * Returns true if the package is a valid static Runtime Overlay package. Static overlays + * are not updatable outside of a system update and are safe to load in the system process. + * @hide + */ + public boolean isStaticOverlayPackage() { + return overlayTarget != null && (mOverlayFlags & FLAG_OVERLAY_STATIC) != 0; + } + @Override public String toString() { return "PackageInfo{" @@ -410,8 +452,8 @@ public class PackageInfo implements Parcelable { dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); dest.writeString(overlayTarget); - dest.writeInt(isStaticOverlay ? 1 : 0); dest.writeInt(overlayPriority); + dest.writeInt(mOverlayFlags); dest.writeInt(compileSdkVersion); dest.writeString(compileSdkVersionCodename); } @@ -465,8 +507,8 @@ public class PackageInfo implements Parcelable { restrictedAccountType = source.readString(); requiredAccountType = source.readString(); overlayTarget = source.readString(); - isStaticOverlay = source.readInt() != 0; overlayPriority = source.readInt(); + mOverlayFlags = source.readInt(); compileSdkVersion = source.readInt(); compileSdkVersionCodename = source.readString(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 153c944a8d93..97c2b7d11354 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -689,7 +689,15 @@ public class PackageParser { pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; pi.overlayPriority = p.mOverlayPriority; - pi.isStaticOverlay = p.mIsStaticOverlay; + + if (p.mIsStaticOverlay) { + pi.mOverlayFlags |= PackageInfo.FLAG_OVERLAY_STATIC; + } + + if (p.mTrustedOverlay) { + pi.mOverlayFlags |= PackageInfo.FLAG_OVERLAY_TRUSTED; + } + pi.compileSdkVersion = p.mCompileSdkVersion; pi.compileSdkVersionCodename = p.mCompileSdkVersionCodename; pi.firstInstallTime = firstInstallTime; @@ -6454,6 +6462,7 @@ public class PackageParser { mAdoptPermissions = dest.createStringArrayList(); mAppMetaData = dest.readBundle(); mVersionCode = dest.readInt(); + mVersionCodeMajor = dest.readInt(); mVersionName = dest.readString(); if (mVersionName != null) { mVersionName = mVersionName.intern(); @@ -6600,6 +6609,7 @@ public class PackageParser { dest.writeStringList(mAdoptPermissions); dest.writeBundle(mAppMetaData); dest.writeInt(mVersionCode); + dest.writeInt(mVersionCodeMajor); dest.writeString(mVersionName); dest.writeString(mSharedUserId); dest.writeInt(mSharedUserLabel); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 551d53b631cd..21bd7f0ce763 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -150,6 +151,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * * @hide */ + @TestApi public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000; /** diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index dadfaa9f1a66..e6f682d22b14 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -81,4 +81,7 @@ public abstract class ShortcutServiceInternal { @Nullable IntentSender resultIntent, int userId); public abstract boolean isRequestPinItemSupported(int callingUserId, int requestType); + + public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage, + int callingUid); } diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java index 5af49d4d46a2..6be9b9eb0438 100644 --- a/core/java/android/content/res/XmlResourceParser.java +++ b/core/java/android/content/res/XmlResourceParser.java @@ -16,20 +16,19 @@ package android.content.res; -import org.xmlpull.v1.XmlPullParser; - import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; + /** * The XML parsing interface returned for an XML resource. This is a standard - * XmlPullParser interface, as well as an extended AttributeSet interface and - * an additional close() method on this interface for the client to indicate - * when it is done reading the resource. + * {@link XmlPullParser} interface but also extends {@link AttributeSet} and + * adds an additional {@link #close()} method for the client to indicate when + * it is done reading the resource. */ public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable { /** - * Close this interface to the resource. Calls on the interface are no - * longer value after this call. + * Close this parser. Calls on the interface are no longer valid after this call. */ public void close(); } diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index 7049628b3590..b111ad30f27d 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -17,6 +17,7 @@ package android.hardware; import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; @@ -70,7 +71,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN, + @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN, USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE, USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA}) diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index b31c7bcdb31e..1d66dc6d939f 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -342,7 +342,17 @@ public final class ContextHubManager { @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubTransaction<Void> loadNanoApp( ContextHubInfo hubInfo, NanoAppBinary appBinary) { - throw new UnsupportedOperationException("TODO: Implement this"); + ContextHubTransaction<Void> transaction = + new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP); + IContextHubTransactionCallback callback = createTransactionCallback(transaction); + + try { + mService.loadNanoAppOnHub(hubInfo.getId(), callback, appBinary); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return transaction; } /** @@ -357,7 +367,17 @@ public final class ContextHubManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubTransaction<Void> unloadNanoApp(ContextHubInfo hubInfo, long nanoAppId) { - throw new UnsupportedOperationException("TODO: Implement this"); + ContextHubTransaction<Void> transaction = + new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP); + IContextHubTransactionCallback callback = createTransactionCallback(transaction); + + try { + mService.unloadNanoAppFromHub(hubInfo.getId(), callback, nanoAppId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return transaction; } /** @@ -401,7 +421,17 @@ public final class ContextHubManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubTransaction<List<NanoAppState>> queryNanoApps(ContextHubInfo hubInfo) { - throw new UnsupportedOperationException("TODO: Implement this"); + ContextHubTransaction<List<NanoAppState>> transaction = + new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS); + IContextHubTransactionCallback callback = createQueryCallback(transaction); + + try { + mService.queryNanoApps(hubInfo.getId(), callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return transaction; } /** diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java index a8569ef479a1..b808de3a11d6 100644 --- a/core/java/android/hardware/location/ContextHubTransaction.java +++ b/core/java/android/hardware/location/ContextHubTransaction.java @@ -72,7 +72,8 @@ public class ContextHubTransaction<T> { TRANSACTION_FAILED_PENDING, TRANSACTION_FAILED_AT_HUB, TRANSACTION_FAILED_TIMEOUT, - TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE}) + TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE, + TRANSACTION_FAILED_HAL_UNAVAILABLE}) public @interface Result {} public static final int TRANSACTION_SUCCESS = 0; /** @@ -103,6 +104,10 @@ public class ContextHubTransaction<T> { * Failure mode when the transaction has failed internally at the service. */ public static final int TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE = 7; + /** + * Failure mode when the Context Hub HAL was not available. + */ + public static final int TRANSACTION_FAILED_HAL_UNAVAILABLE = 8; /** * A class describing the response for a ContextHubTransaction. @@ -189,6 +194,30 @@ public class ContextHubTransaction<T> { } /** + * Converts a transaction type to a human-readable string + * + * @param type the type of a transaction + * @param upperCase {@code true} if upper case the first letter, {@code false} otherwise + * @return a string describing the transaction + */ + public static String typeToString(@Type int type, boolean upperCase) { + switch (type) { + case ContextHubTransaction.TYPE_LOAD_NANOAPP: + return upperCase ? "Load" : "load"; + case ContextHubTransaction.TYPE_UNLOAD_NANOAPP: + return upperCase ? "Unload" : "unload"; + case ContextHubTransaction.TYPE_ENABLE_NANOAPP: + return upperCase ? "Enable" : "enable"; + case ContextHubTransaction.TYPE_DISABLE_NANOAPP: + return upperCase ? "Disable" : "disable"; + case ContextHubTransaction.TYPE_QUERY_NANOAPPS: + return upperCase ? "Query" : "query"; + default: + return upperCase ? "Unknown" : "unknown"; + } + } + + /** * @return the type of the transaction */ @Type @@ -239,7 +268,7 @@ public class ContextHubTransaction<T> { * A transaction can be invalidated if the process owning the transaction is no longer active * and the reference to this object is lost. * - * This method or {@link #setCallbackOnCompletecan(ContextHubTransaction.Callback)} can only be + * This method or {@link #setCallbackOnComplete(ContextHubTransaction.Callback)} can only be * invoked once, or an IllegalStateException will be thrown. * * @param callback the callback to be invoked upon completion diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index 1bb7c8fbb6e7..628ebc7d4579 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -20,41 +20,56 @@ package android.hardware.location; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.NanoApp; +import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppFilter; import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.IContextHubCallback; import android.hardware.location.IContextHubClient; import android.hardware.location.IContextHubClientCallback; +import android.hardware.location.IContextHubTransactionCallback; /** * @hide */ interface IContextHubService { - // register a callback to receive messages + // Registers a callback to receive messages int registerCallback(in IContextHubCallback callback); // Gets a list of available context hub handles int[] getContextHubHandles(); - // Get the properties of a hub + // Gets the properties of a hub ContextHubInfo getContextHubInfo(int contextHubHandle); - // Load a nanoapp on a specified context hub + // Loads a nanoapp at the specified hub (old API) int loadNanoApp(int hubHandle, in NanoApp app); - // Unload a nanoapp instance + // Unloads a nanoapp given its instance ID (old API) int unloadNanoApp(int nanoAppInstanceHandle); - // get information about a nanoAppInstance + // Gets the NanoAppInstanceInfo of a nanoapp give its instance ID NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle); - // find all nanoApp instances matching some filter + // Finds all nanoApp instances matching some filter int[] findNanoAppOnHub(int hubHandle, in NanoAppFilter filter); - // send a message to a nanoApp + // Sends a message to a nanoApp int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg); // Creates a client to send and receive messages IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId); + + // Loads a nanoapp at the specified hub (new API) + void loadNanoAppOnHub( + int contextHubId, in IContextHubTransactionCallback transactionCallback, + in NanoAppBinary nanoAppBinary); + + // Unloads a nanoapp on a specified context hub (new API) + void unloadNanoAppFromHub( + int contextHubId, in IContextHubTransactionCallback transactionCallback, + long nanoAppId); + + // Queries for a list of nanoapps + void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 223ed73bb15a..02b1c658b2ec 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -392,7 +392,7 @@ public class InputMethodService extends AbstractInputMethodService { mWindow.setToken(token); } } - + /** * {@inheritDoc} * @@ -1064,7 +1064,89 @@ public class InputMethodService extends AbstractInputMethodService { } return mInputConnection; } - + + /** + * Force switch to a new input method component. This can only be called + * from an application or a service which has a token of the currently active input method. + * @param id The unique identifier for the new input method to be switched to. + */ + public void setInputMethod(String id) { + mImm.setInputMethodInternal(mToken, id); + } + + /** + * Force switch to a new input method and subtype. This can only be called + * from an application or a service which has a token of the currently active input method. + * @param id The unique identifier for the new input method to be switched to. + * @param subtype The new subtype of the new input method to be switched to. + */ + public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) { + mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype); + } + + /** + * Close/hide the input method's soft input area, so the user no longer + * sees it or can interact with it. This can only be called + * from the currently active input method, as validated by the given token. + * + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY}, + * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set. + */ + public void hideSoftInputFromInputMethod(int flags) { + mImm.hideSoftInputFromInputMethodInternal(mToken, flags); + } + + /** + * Show the input method's soft input area, so the user + * sees the input method window and can interact with it. + * This can only be called from the currently active input method, + * as validated by the given token. + * + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} or + * {@link InputMethodManager#SHOW_FORCED} bit set. + */ + public void showSoftInputFromInputMethod(int flags) { + mImm.showSoftInputFromInputMethodInternal(mToken, flags); + } + + /** + * Force switch to the last used input method and subtype. If the last input method didn't have + * any subtypes, the framework will simply switch to the last input method with no subtype + * specified. + * @return true if the current input method and subtype was successfully switched to the last + * used input method and subtype. + */ + public boolean switchToLastInputMethod() { + return mImm.switchToLastInputMethodInternal(mToken); + } + + /** + * Force switch to the next input method and subtype. If there is no IME enabled except + * current IME and subtype, do nothing. + * @param onlyCurrentIme if true, the framework will find the next subtype which + * belongs to the current IME + * @return true if the current input method and subtype was successfully switched to the next + * input method and subtype. + */ + public boolean switchToNextInputMethod(boolean onlyCurrentIme) { + return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme); + } + + /** + * Returns true if the current IME needs to offer the users ways to switch to a next input + * method (e.g. a globe key.). + * When an IME sets supportsSwitchingToNextInputMethod and this method returns true, + * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly. + * <p> Note that the system determines the most appropriate next input method + * and subtype in order to provide the consistent user experience in switching + * between IMEs and subtypes. + */ + public boolean shouldOfferSwitchingToNextInputMethod() { + return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken); + } + public boolean getCurrentInputStarted() { return mInputStarted; } diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java index 8ff8e4f38d6d..6f383b4d515b 100644 --- a/core/java/android/net/metrics/DefaultNetworkEvent.java +++ b/core/java/android/net/metrics/DefaultNetworkEvent.java @@ -74,7 +74,7 @@ public class DefaultNetworkEvent { j.add("final_score=" + finalScore); } j.add(String.format("duration=%.0fs", durationMs / 1000.0)); - j.add(String.format("validation=%4.1f%%", (validatedMs * 100.0) / durationMs)); + j.add(String.format("validation=%04.1f%%", (validatedMs * 100.0) / durationMs)); return j.toString(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 948d4cec1003..2e9eeb16a887 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -6742,7 +6742,7 @@ public abstract class BatteryStats implements Parcelable { /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */ public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps, - int flags, long historyStart) { + int flags) { final ProtoOutputStream proto = new ProtoOutputStream(fd); final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS); prepareForDumpLocked(); @@ -6752,13 +6752,7 @@ public abstract class BatteryStats implements Parcelable { proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion()); proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion()); - long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); - - if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) { - if (startIteratingHistoryLocked()) { - // TODO: implement dumpProtoHistoryLocked(proto); - } - } + // History intentionally not included in proto dump. if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) { final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1d4477a1c4c1..6b1632a0a693 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10164,6 +10164,16 @@ public final class Settings { public static final String POLICY_CONTROL = "policy_control"; /** + * {@link android.view.DisplayCutout DisplayCutout} emulation mode. + * + * @hide + */ + public static final String EMULATE_DISPLAY_CUTOUT = "emulate_display_cutout"; + + /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_OFF = 0; + /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_ON = 1; + + /** * Defines global zen mode. ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS, * or ZEN_MODE_NO_INTERRUPTIONS. * diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java b/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java new file mode 100644 index 000000000000..4125f0baf49d --- /dev/null +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java @@ -0,0 +1,133 @@ +/* + * 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.security.recoverablekeystore; + +import android.security.keystore.AndroidKeyStoreSecretKey; +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyProtection; +import android.util.Log; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.security.auth.DestroyFailedException; + +/** + * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form. + * + * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding. + * They are synced to disk wrapped by a platform key. This allows them to be exported to a remote + * service. + * + * @hide + */ +public class RecoverableKeyGenerator { + private static final String TAG = "RecoverableKeyGenerator"; + private static final String KEY_GENERATOR_ALGORITHM = "AES"; + private static final int KEY_SIZE_BITS = 256; + + /** + * A new {@link RecoverableKeyGenerator} instance. + * + * @param platformKey Secret key used to wrap generated keys before persisting to disk. + * @param recoverableKeyStorage Class that manages persisting wrapped keys to disk. + * @throws NoSuchAlgorithmException if "AES" key generation or "AES/GCM/NoPadding" cipher is + * unavailable. Should never happen. + * + * @hide + */ + public static RecoverableKeyGenerator newInstance( + AndroidKeyStoreSecretKey platformKey, RecoverableKeyStorage recoverableKeyStorage) + throws NoSuchAlgorithmException { + // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key + // material, so that it can be synced to disk in encrypted form. + KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM); + return new RecoverableKeyGenerator(keyGenerator, platformKey, recoverableKeyStorage); + } + + private final KeyGenerator mKeyGenerator; + private final RecoverableKeyStorage mRecoverableKeyStorage; + private final AndroidKeyStoreSecretKey mPlatformKey; + + private RecoverableKeyGenerator( + KeyGenerator keyGenerator, + AndroidKeyStoreSecretKey platformKey, + RecoverableKeyStorage recoverableKeyStorage) { + mKeyGenerator = keyGenerator; + mRecoverableKeyStorage = recoverableKeyStorage; + mPlatformKey = platformKey; + } + + /** + * Generates a 256-bit AES key with the given alias. + * + * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is + * persisted to disk so that it can be synced remotely, and then recovered on another device. + * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding. + * + * <p>The key handle returned to the caller is a reference to the AndroidKeyStore key, + * meaning that the caller is never able to access the raw, unencrypted key. + * + * @param alias The alias by which the key will be known in AndroidKeyStore. + * @throws InvalidKeyException if the platform key cannot be used to wrap keys. + * @throws IOException if there was an issue writing the wrapped key to the wrapped key store. + * @throws UnrecoverableEntryException if could not retrieve key after putting it in + * AndroidKeyStore. This should not happen. + * @return A handle to the AndroidKeyStore key. + * + * @hide + */ + public SecretKey generateAndStoreKey(String alias) throws KeyStoreException, + InvalidKeyException, IOException, UnrecoverableEntryException { + mKeyGenerator.init(KEY_SIZE_BITS); + SecretKey key = mKeyGenerator.generateKey(); + + mRecoverableKeyStorage.importIntoAndroidKeyStore( + alias, + key, + new KeyProtection.Builder( + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + WrappedKey wrappedKey = WrappedKey.fromSecretKey(mPlatformKey, key); + + try { + // Keep raw key material in memory for minimum possible time. + key.destroy(); + } catch (DestroyFailedException e) { + Log.w(TAG, "Could not destroy SecretKey."); + } + + mRecoverableKeyStorage.persistToDisk(alias, wrappedKey); + + try { + // Reload from the keystore, so that the caller is only provided with the handle of the + // key, not the raw key material. + return mRecoverableKeyStorage.loadFromAndroidKeyStore(alias); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException( + "Impossible: NoSuchAlgorithmException when attempting to retrieve a key " + + "that has only just been stored in AndroidKeyStore.", e); + } + } +} diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java new file mode 100644 index 000000000000..c239e006c23b --- /dev/null +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java @@ -0,0 +1,71 @@ +/* + * 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.security.recoverablekeystore; + +import android.security.keystore.KeyProtection; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; + +import javax.crypto.SecretKey; + +/** + * Stores wrapped keys to disk, so they can be synced on the next screen unlock event. + * + * @hide + */ +public interface RecoverableKeyStorage { + + /** + * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}. + * + * @throws IOException if an error occurred writing to disk. + * + * @hide + */ + void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException; + + /** + * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and + * the {@code alias}. + * + * @param alias The alias of the key. + * @param key The key. + * @param keyProtection Protection params denoting what the key can be used for. (e.g., what + * Cipher modes, whether for encrpyt/decrypt or signing, etc.) + * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore. + * + * @hide + */ + void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection) throws + KeyStoreException; + + /** + * Loads a key handle from AndroidKeyStore. + * + * @param alias Alias of the key to load. + * @return The key handle. + * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore. + * + * @hide + */ + SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException, + NoSuchAlgorithmException, + UnrecoverableEntryException; +} diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java new file mode 100644 index 000000000000..b9926dd9cf8d --- /dev/null +++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java @@ -0,0 +1,110 @@ +/* + * 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.security.recoverablekeystore; + +import android.security.keystore.KeyProtection; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; + +import javax.crypto.SecretKey; + +/** + * Implementation of {@link RecoverableKeyStorage}. + * + * <p>Persists wrapped keys to disk, and loads raw keys into AndroidKeyStore. + * + * @hide + */ +public class RecoverableKeyStorageImpl implements RecoverableKeyStorage { + private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; + + private final KeyStore mKeyStore; + + /** + * A new instance. + * + * @throws KeyStoreException if unable to load AndroidKeyStore. + * + * @hide + */ + public static RecoverableKeyStorageImpl newInstance() throws KeyStoreException { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + try { + keyStore.load(/*param=*/ null); + } catch (CertificateException | IOException | NoSuchAlgorithmException e) { + // Should never happen. + throw new KeyStoreException("Unable to load keystore.", e); + } + return new RecoverableKeyStorageImpl(keyStore); + } + + private RecoverableKeyStorageImpl(KeyStore keyStore) { + mKeyStore = keyStore; + } + + /** + * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}. + * + * @throws IOException if an error occurred writing to disk. + * + * @hide + */ + @Override + public void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException { + // TODO(robertberry) Add implementation. + throw new UnsupportedOperationException(); + } + + /** + * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and the + * {@code alias}. + * + * @param alias The alias of the key. + * @param key The key. + * @param keyProtection Protection params denoting what the key can be used for. (e.g., what + * Cipher modes, whether for encrpyt/decrypt or signing, etc.) + * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore. + * + * @hide + */ + @Override + public void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection) + throws KeyStoreException { + mKeyStore.setEntry(alias, new KeyStore.SecretKeyEntry(key), keyProtection); + } + + /** + * Loads a key handle from AndroidKeyStore. + * + * @param alias Alias of the key to load. + * @return The key handle. + * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore. + * + * @hide + */ + @Override + public SecretKey loadFromAndroidKeyStore(String alias) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + return ((KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, /*protParam=*/ null)) + .getSecretKey(); + } +} diff --git a/core/java/android/security/recoverablekeystore/WrappedKey.java b/core/java/android/security/recoverablekeystore/WrappedKey.java new file mode 100644 index 000000000000..51665ae2931d --- /dev/null +++ b/core/java/android/security/recoverablekeystore/WrappedKey.java @@ -0,0 +1,115 @@ +/* + * 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.security.recoverablekeystore; + +import java.security.InvalidKeyException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + +/** + * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding. + * + * @hide + */ +public class WrappedKey { + private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding"; + + private final byte[] mNonce; + private final byte[] mKeyMaterial; + + /** + * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material. + * + * @throws InvalidKeyException if {@code wrappingKey} cannot be used to encrypt {@code key}, or + * if {@code key} does not expose its key material. See + * {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does + * not expose its key material. + */ + public static WrappedKey fromSecretKey( + SecretKey wrappingKey, SecretKey key) throws InvalidKeyException, KeyStoreException { + if (key.getEncoded() == null) { + throw new InvalidKeyException( + "key does not expose encoded material. It cannot be wrapped."); + } + + Cipher cipher; + try { + cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException( + "Android does not support AES/GCM/NoPadding. This should never happen."); + } + + cipher.init(Cipher.WRAP_MODE, wrappingKey); + byte[] encryptedKeyMaterial; + try { + encryptedKeyMaterial = cipher.wrap(key); + } catch (IllegalBlockSizeException e) { + Throwable cause = e.getCause(); + if (cause instanceof KeyStoreException) { + // If AndroidKeyStore encounters any error here, it throws IllegalBlockSizeException + // with KeyStoreException as the cause. This is due to there being no better option + // here, as the Cipher#wrap only checked throws InvalidKeyException or + // IllegalBlockSizeException. If this is the case, we want to propagate it to the + // caller, so rethrow the cause. + throw (KeyStoreException) cause; + } else { + throw new RuntimeException( + "IllegalBlockSizeException should not be thrown by AES/GCM/NoPadding mode.", + e); + } + } + + return new WrappedKey(/*mNonce=*/ cipher.getIV(), /*mKeyMaterial=*/ encryptedKeyMaterial); + } + + /** + * A new instance. + * + * @param nonce The nonce with which the key material was encrypted. + * @param keyMaterial The encrypted bytes of the key material. + * + * @hide + */ + public WrappedKey(byte[] nonce, byte[] keyMaterial) { + mNonce = nonce; + mKeyMaterial = keyMaterial; + } + + /** + * Returns the nonce with which the key material was encrypted. + * + * @hide + */ + public byte[] getNonce() { + return mNonce; + } + + /** + * Returns the encrypted key material. + * + * @hide + */ + public byte[] getKeyMaterial() { + return mKeyMaterial; + } +} diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 1afa8b3eb4dd..97fdcefc9d56 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -445,7 +445,7 @@ import com.android.internal.os.SomeArgs; * * <p>The {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} method is called * without the user content. The Android system strips some properties of the - * {@link android.app.assist.AssistStructure.ViewNode view nodes} passed to these calls, but not all + * {@link android.app.assist.AssistStructure.ViewNode view nodes} passed to this call, but not all * of them. For example, the data provided in the {@link android.view.ViewStructure.HtmlInfo} * objects set by {@link android.webkit.WebView} is never stripped out. * diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 3a84224084c0..ddc92f6a656f 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -127,12 +127,15 @@ public final class FillRequest implements Parcelable { } /** - * Gets the extra client state returned from the last {@link - * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback) - * fill request}, so the service can use it for state management. + * Gets the latest client state bundle set by the service in a + * {@link FillResponse.Builder#setClientState(Bundle) fill response}. * - * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) - * save request} is made, the client state is cleared. + * <p><b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state + * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} were considered. On + * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of + * an authenticated request through the + * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are + * also considered (and take precedence when set). * * @return The client state. */ diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 06d2b07eef09..04db69845c86 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -49,14 +49,16 @@ import java.util.List; public final class FillResponse implements Parcelable { /** - * Must be set in the last response to generate - * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} events. + * Flag used to generate {@link FillEventHistory.Event events} of type + * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}—if this flag is not passed to + * {@link Builder#setFlags(int)}, these events are not generated. */ public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1; /** - * Used in conjunction to {@link FillResponse.Builder#disableAutofill(long)} to disable autofill - * only for the activiy associated with the {@link FillResponse}, instead of the whole app. + * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}— + * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the + * activiy that generated the {@link FillRequest}, not the whole app. */ public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2; @@ -329,19 +331,16 @@ public final class FillResponse implements Parcelable { } /** - * Sets a {@link Bundle state} that will be passed to subsequent APIs that - * manipulate this response. For example, they are passed to subsequent - * calls to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, - * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}. - * You can use this to store intermediate state that is persistent across multiple - * fill requests and the subsequent save request. + * Sets a bundle with state that is passed to subsequent APIs that manipulate this response. + * + * <p>You can use this bundle to store intermediate state that is passed to subsequent calls + * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, + * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and + * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}. * * <p>If this method is called on multiple {@link FillResponse} objects for the same * screen, just the latest bundle is passed back to the service. * - * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) - * save request} is made the client state is cleared. - * * @param clientState The custom client state. * @return This builder. */ diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java index f53967bde773..4f85e6b9b23c 100644 --- a/core/java/android/service/autofill/SaveRequest.java +++ b/core/java/android/service/autofill/SaveRequest.java @@ -59,10 +59,11 @@ public final class SaveRequest implements Parcelable { } /** - * Gets the latest client state extra returned from the service. + * Gets the latest client state bundle set by the service in a + * {@link FillResponse.Builder#setClientState(Bundle) fill response}. * * <p><b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state - * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} where considered. On + * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} were considered. On * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of * an authenticated request through the * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are diff --git a/core/java/android/service/autofill/Validators.java b/core/java/android/service/autofill/Validators.java index 1c8386870fe7..0f1ba9891a99 100644 --- a/core/java/android/service/autofill/Validators.java +++ b/core/java/android/service/autofill/Validators.java @@ -33,6 +33,8 @@ public final class Validators { /** * Creates a validator that is only valid if all {@code validators} are valid. * + * <p>Used to represent an {@code AND} boolean operation in a chain of validators. + * * @throws IllegalArgumentException if any element of {@code validators} is an instance of a * class that is not provided by the Android System. */ @@ -44,6 +46,8 @@ public final class Validators { /** * Creates a validator that is valid if any of the {@code validators} is valid. * + * <p>Used to represent an {@code OR} boolean operation in a chain of validators. + * * @throws IllegalArgumentException if any element of {@code validators} is an instance of a * class that is not provided by the Android System. */ @@ -53,7 +57,9 @@ public final class Validators { } /** - * Creates a validator that is valid only if {@code validator} is not. + * Creates a validator that is valid when {@code validator} is not, and vice versa. + * + * <p>Used to represent a {@code NOT} boolean operation in a chain of validators. * * @throws IllegalArgumentException if {@code validator} is an instance of a class that is not * provided by the Android System. diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl index 7285fb40ae02..f7acfc5918a8 100644 --- a/core/java/android/service/vr/IVrManager.aidl +++ b/core/java/android/service/vr/IVrManager.aidl @@ -17,6 +17,7 @@ package android.service.vr; import android.app.Vr2dDisplayProperties; +import android.content.ComponentName; import android.service.vr.IVrStateCallbacks; import android.service.vr.IPersistentVrStateCallbacks; @@ -109,5 +110,13 @@ interface IVrManager { * @param standy True if the device is entering standby, false if it's exiting standby. */ void setStandbyEnabled(boolean standby); + + /** + * Start VR Input method for the given packageName in {@param componentName}. + * This method notifies InputMethodManagerService to use VR IME instead of + * regular phone IME. + */ + void setVrInputMethod(in ComponentName componentName); + } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 400b075d889d..53ddd1699d68 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -887,6 +887,7 @@ public class StaticLayout extends Layout { if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) { + measured = MeasuredText.buildForBidi(source, bufEnd, bufEnd, textDir, measured); paint.getFontMetricsInt(fm); v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, @@ -894,7 +895,7 @@ public class StaticLayout extends Layout { v, spacingmult, spacingadd, null, null, fm, 0, - needMultiply, null, bufEnd, + needMultiply, measured, bufEnd, includepad, trackpad, addLastLineSpacing, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false); @@ -912,7 +913,7 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, - final int flags, final boolean needMultiply, @Nullable final MeasuredText measured, + final int flags, final boolean needMultiply, @NonNull final MeasuredText measured, final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final float[] widths, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, @@ -921,7 +922,7 @@ public class StaticLayout extends Layout { final int off = j * mColumns; final int want = off + mColumns + TOP; int[] lines = mLines; - final int dir = (start == end) ? Layout.DIR_LEFT_TO_RIGHT : measured.getParagraphDir(); + final int dir = measured.getParagraphDir(); if (want >= lines.length) { final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want)); @@ -948,11 +949,7 @@ public class StaticLayout extends Layout { lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; lines[off + DIR] |= dir << DIR_SHIFT; - if (start == end) { - mLineDirections[j] = Layout.DIRS_ALL_LEFT_TO_RIGHT; - } else { - mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); - } + mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); final boolean firstLine = (j == 0); final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount); diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 29baea17da33..54b48b619101 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -21,6 +21,7 @@ import android.os.SystemProperties; import android.provider.Settings; import android.text.TextUtils; +import java.util.HashMap; import java.util.Map; /** @@ -33,6 +34,18 @@ public class FeatureFlagUtils { public static final String FFLAG_PREFIX = "sys.fflag."; public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override."; + private static final Map<String, String> DEFAULT_FLAGS; + static { + DEFAULT_FLAGS = new HashMap<>(); + DEFAULT_FLAGS.put("device_info_v2", "true"); + DEFAULT_FLAGS.put("new_settings_suggestion", "true"); + DEFAULT_FLAGS.put("settings_search_v2", "false"); + DEFAULT_FLAGS.put("settings_app_info_v2", "false"); + DEFAULT_FLAGS.put("settings_connected_device_v2", "false"); + DEFAULT_FLAGS.put("settings_battery_v2", "false"); + DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); + } + /** * Whether or not a flag is enabled. * @@ -41,7 +54,7 @@ public class FeatureFlagUtils { */ public static boolean isEnabled(Context context, String feature) { // Override precedence: - // Settings.Global -> sys.fflag.override.* -> sys.fflag.* + // Settings.Global -> sys.fflag.override.* -> static list // Step 1: check if feature flag is set in Settings.Global. String value; @@ -57,8 +70,8 @@ public class FeatureFlagUtils { if (!TextUtils.isEmpty(value)) { return Boolean.parseBoolean(value); } - // Step 3: check if feature flag has any default value. Flag name: sys.fflag.<feature> - value = SystemProperties.get(FFLAG_PREFIX + feature); + // Step 3: check if feature flag has any default value. + value = getAllFeatureFlags().get(feature); return Boolean.parseBoolean(value); } @@ -73,6 +86,6 @@ public class FeatureFlagUtils { * Returns all feature flags in their raw form. */ public static Map<String, String> getAllFeatureFlags() { - return null; + return DEFAULT_FLAGS; } } diff --git a/core/java/android/view/FrameInfo.java b/core/java/android/view/FrameInfo.java index c79547c8313e..6c5e048f05d2 100644 --- a/core/java/android/view/FrameInfo.java +++ b/core/java/android/view/FrameInfo.java @@ -16,7 +16,7 @@ package android.view; -import android.annotation.IntDef; +import android.annotation.LongDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -48,7 +48,7 @@ final class FrameInfo { // Is this the first-draw following a window layout? public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1; - @IntDef(flag = true, value = { + @LongDef(flag = true, value = { FLAG_WINDOW_LAYOUT_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface FrameInfoFlags {} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 420a1bb34d28..357b8d9d039b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -18,11 +18,18 @@ package android.view; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MSCALE_Y; +import static android.graphics.Matrix.MSKEW_X; +import static android.graphics.Matrix.MSKEW_Y; +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; import android.annotation.Size; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; @@ -862,6 +869,22 @@ public class SurfaceControl implements Parcelable { } } + /** + * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation matrix. + * + * @param matrix The matrix to apply. + * @param float9 An array of 9 floats to be used to extract the values from the matrix. + */ + public void setMatrix(Matrix matrix, float[] float9) { + checkNotReleased(); + matrix.getValues(float9); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setMatrix(this, float9[MSCALE_X], float9[MSKEW_Y], + float9[MSKEW_X], float9[MSCALE_Y]); + sGlobalTransaction.setPosition(this, float9[MTRANS_X], float9[MTRANS_Y]); + } + } + public void setWindowCrop(Rect crop) { checkNotReleased(); synchronized (SurfaceControl.class) { @@ -1126,7 +1149,9 @@ public class SurfaceControl implements Parcelable { } /** - * Copy the current screen contents into a bitmap and return it. + * Copy the current screen contents into a hardware bitmap and return it. + * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into + * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)} * * CAVEAT: Versions of screenshot that return a {@link Bitmap} can * be extremely slow; avoid use unless absolutely necessary; prefer @@ -1151,7 +1176,7 @@ public class SurfaceControl implements Parcelable { * screenshots in its native portrait orientation by default, so this is * useful for returning screenshots that are independent of device * orientation. - * @return Returns a Bitmap containing the screen contents, or null + * @return Returns a hardware Bitmap containing the screen contents, or null * if an error occurs. Make sure to call Bitmap.recycle() as soon as * possible, once its content is not needed anymore. */ @@ -1179,7 +1204,7 @@ public class SurfaceControl implements Parcelable { } /** - * Like {@link SurfaceControl#screenshot(int, int, int, int, boolean)} but + * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but * includes all Surfaces in the screenshot. This will also update the orientation so it * sends the correct coordinates to SF based on the rotation value. * @@ -1236,7 +1261,7 @@ public class SurfaceControl implements Parcelable { } /** - * Captures a layer and its children into the provided {@link Surface}. + * Captures a layer and its children and returns a {@link GraphicBuffer} with the content. * * @param layerHandleToken The root layer to capture. * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new @@ -1348,6 +1373,14 @@ public class SurfaceControl implements Parcelable { return this; } + public Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) { + matrix.getValues(float9); + setMatrix(sc, float9[MSCALE_X], float9[MSKEW_Y], + float9[MSKEW_X], float9[MSCALE_Y]); + setPosition(sc, float9[MTRANS_X], float9[MTRANS_Y]); + return this; + } + public Transaction setWindowCrop(SurfaceControl sc, Rect crop) { sc.checkNotReleased(); if (crop != null) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0bae36bf4b04..129a255c1989 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -142,10 +142,11 @@ public final class ViewRootImpl implements ViewParent, private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV; /** - * Set to false if we do not want to use the multi threaded renderer. Note that by disabling + * Set to false if we do not want to use the multi threaded renderer even though + * threaded renderer (aka hardware renderering) is used. Note that by disabling * this, WindowCallbacks will not fire. */ - private static final boolean USE_MT_RENDERER = true; + private static final boolean MT_RENDERER_AVAILABLE = true; /** * Set this system property to true to force the view hierarchy to render @@ -302,6 +303,7 @@ public final class ViewRootImpl implements ViewParent, Rect mDirty; public boolean mIsAnimating; + private boolean mUseMTRenderer; private boolean mDragResizing; private boolean mInvalidateRootRequested; private int mResizeMode; @@ -548,18 +550,14 @@ public final class ViewRootImpl implements ViewParent, } public void addWindowCallbacks(WindowCallbacks callback) { - if (USE_MT_RENDERER) { - synchronized (mWindowCallbacks) { - mWindowCallbacks.add(callback); - } + synchronized (mWindowCallbacks) { + mWindowCallbacks.add(callback); } } public void removeWindowCallbacks(WindowCallbacks callback) { - if (USE_MT_RENDERER) { - synchronized (mWindowCallbacks) { - mWindowCallbacks.remove(callback); - } + synchronized (mWindowCallbacks) { + mWindowCallbacks.remove(callback); } } @@ -685,7 +683,17 @@ public final class ViewRootImpl implements ViewParent, // If the application owns the surface, don't enable hardware acceleration if (mSurfaceHolder == null) { + // While this is supposed to enable only, it can effectively disable + // the acceleration too. enableHardwareAcceleration(attrs); + final boolean useMTRenderer = MT_RENDERER_AVAILABLE + && mAttachInfo.mThreadedRenderer != null; + if (mUseMTRenderer != useMTRenderer) { + // Shouldn't be resizing, as it's done only in window setup, + // but end just in case. + endDragResizing(); + mUseMTRenderer = useMTRenderer; + } } boolean restore = false; @@ -2088,7 +2096,7 @@ public final class ViewRootImpl implements ViewParent, endDragResizing(); } } - if (!USE_MT_RENDERER) { + if (!mUseMTRenderer) { if (dragResizing) { mCanvasOffsetX = mWinFrame.left; mCanvasOffsetY = mWinFrame.top; @@ -2726,8 +2734,10 @@ public final class ViewRootImpl implements ViewParent, @Override public void onPostDraw(DisplayListCanvas canvas) { drawAccessibilityFocusedDrawableIfNeeded(canvas); - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onPostDraw(canvas); + if (mUseMTRenderer) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onPostDraw(canvas); + } } } @@ -3058,7 +3068,8 @@ public final class ViewRootImpl implements ViewParent, return; } - if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { + if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, + scalingRequired, dirty, surfaceInsets)) { return; } } @@ -3074,11 +3085,22 @@ public final class ViewRootImpl implements ViewParent, * @return true if drawing was successful, false if an error occurred */ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, - boolean scalingRequired, Rect dirty) { + boolean scalingRequired, Rect dirty, Rect surfaceInsets) { // Draw with software renderer. final Canvas canvas; + + // We already have the offset of surfaceInsets in xoff, yoff and dirty region, + // therefore we need to add it back when moving the dirty region. + int dirtyXOffset = xoff; + int dirtyYOffset = yoff; + if (surfaceInsets != null) { + dirtyXOffset += surfaceInsets.left; + dirtyYOffset += surfaceInsets.top; + } + try { + dirty.offset(-dirtyXOffset, -dirtyYOffset); final int left = dirty.left; final int top = dirty.top; final int right = dirty.right; @@ -3105,6 +3127,8 @@ public final class ViewRootImpl implements ViewParent, // kill stuff (or ourself) for no reason. mLayoutRequested = true; // ask wm for a new surface next time. return false; + } finally { + dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value. } try { @@ -6578,7 +6602,7 @@ public final class ViewRootImpl implements ViewParent, // Tell all listeners that we are resizing the window so that the chrome can get // updated as fast as possible on a separate thread, - if (mDragResizing) { + if (mDragResizing && mUseMTRenderer) { boolean fullscreen = frame.equals(backDropFrame); synchronized (mWindowCallbacks) { for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { @@ -7828,9 +7852,11 @@ public final class ViewRootImpl implements ViewParent, Rect stableInsets, int resizeMode) { if (!mDragResizing) { mDragResizing = true; - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen, - systemInsets, stableInsets, resizeMode); + if (mUseMTRenderer) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeStart( + initialBounds, fullscreen, systemInsets, stableInsets, resizeMode); + } } mFullRedrawNeeded = true; } @@ -7842,8 +7868,10 @@ public final class ViewRootImpl implements ViewParent, private void endDragResizing() { if (mDragResizing) { mDragResizing = false; - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowDragResizeEnd(); + if (mUseMTRenderer) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeEnd(); + } } mFullRedrawNeeded = true; } @@ -7851,19 +7879,21 @@ public final class ViewRootImpl implements ViewParent, private boolean updateContentDrawBounds() { boolean updated = false; - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - updated |= mWindowCallbacks.get(i).onContentDrawn( - mWindowAttributes.surfaceInsets.left, - mWindowAttributes.surfaceInsets.top, - mWidth, mHeight); + if (mUseMTRenderer) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + updated |= + mWindowCallbacks.get(i).onContentDrawn(mWindowAttributes.surfaceInsets.left, + mWindowAttributes.surfaceInsets.top, mWidth, mHeight); + } } return updated | (mDragResizing && mReportNextDraw); } private void requestDrawWindow() { - if (mReportNextDraw) { - mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); + if (!mUseMTRenderer) { + return; } + mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); } @@ -7907,6 +7937,7 @@ public final class ViewRootImpl implements ViewParent, if (!registered) { mAttachInfo.mAccessibilityWindowId = mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, + mContext.getPackageName(), new AccessibilityInteractionConnection(ViewRootImpl.this)); } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 905c0715ecb8..500701de7f23 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -20,6 +20,7 @@ import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT; import android.Manifest.permission; import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -1268,6 +1269,35 @@ public interface WindowManager extends ViewManager { }, formatToHexString = true) public int flags; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @LongDef( + flag = true, + value = { + LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA, + }) + @interface Flags2 {} + + /** + * Window flag: allow placing the window within the area that overlaps with the + * display cutout. + * + * <p> + * The window must correctly position its contents to take the display cutout into account. + * + * @see DisplayCutout + * @hide for now + */ + public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001; + + /** + * Various behavioral options/flags. Default is none. + * + * @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA + * @hide for now + */ + @Flags2 public long flags2; + /** * If the window has requested hardware acceleration, but this is not * allowed in the process it is in, then still render it as if it is diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index d890f329dd31..e14655536c75 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -29,6 +29,7 @@ import android.util.LongSparseArray; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.Collections; @@ -213,7 +214,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @return The {@link AccessibilityWindowInfo}. */ @@ -299,7 +300,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -335,18 +336,19 @@ public final class AccessibilityInteractionClient } final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success; + final String[] packageNames; try { - success = connection.findAccessibilityNodeInfoByAccessibilityId( + packageNames = connection.findAccessibilityNodeInfoByAccessibilityId( accessibilityWindowId, accessibilityNodeId, interactionId, this, prefetchFlags, Thread.currentThread().getId(), arguments); } finally { Binder.restoreCallingIdentity(identityToken); } - if (success) { + if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + bypassCache, packageNames); if (infos != null && !infos.isEmpty()) { for (int i = 1; i < infos.size(); i++) { infos.get(i).recycle(); @@ -373,7 +375,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -389,20 +391,21 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success; + final String[] packageNames; try { - success = connection.findAccessibilityNodeInfosByViewId( + packageNames = connection.findAccessibilityNodeInfosByViewId( accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, Thread.currentThread().getId()); } finally { Binder.restoreCallingIdentity(identityToken); } - if (success) { + if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); if (infos != null) { - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + false, packageNames); return infos; } } @@ -426,7 +429,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -442,20 +445,21 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success; + final String[] packageNames; try { - success = connection.findAccessibilityNodeInfosByText( + packageNames = connection.findAccessibilityNodeInfosByText( accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId()); } finally { Binder.restoreCallingIdentity(identityToken); } - if (success) { + if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); if (infos != null) { - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + false, packageNames); return infos; } } @@ -478,7 +482,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -494,19 +498,19 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success; + final String[] packageNames; try { - success = connection.findFocus(accessibilityWindowId, + packageNames = connection.findFocus(accessibilityWindowId, accessibilityNodeId, focusType, interactionId, this, Thread.currentThread().getId()); } finally { Binder.restoreCallingIdentity(identityToken); } - if (success) { + if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } } else { @@ -527,7 +531,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -543,19 +547,19 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success; + final String[] packageNames; try { - success = connection.focusSearch(accessibilityWindowId, + packageNames = connection.focusSearch(accessibilityWindowId, accessibilityNodeId, direction, interactionId, this, Thread.currentThread().getId()); } finally { Binder.restoreCallingIdentity(identityToken); } - if (success) { + if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } } else { @@ -574,7 +578,7 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use @@ -661,7 +665,7 @@ public final class AccessibilityInteractionClient int interactionId) { synchronized (mInstanceLock) { final boolean success = waitForResultTimedLocked(interactionId); - List<AccessibilityNodeInfo> result = null; + final List<AccessibilityNodeInfo> result; if (success) { result = mFindAccessibilityNodeInfosResult; } else { @@ -779,11 +783,19 @@ public final class AccessibilityInteractionClient * @param connectionId The id of the connection to the system. * @param bypassCache Whether or not to bypass the cache. The node is added to the cache if * this value is {@code false} + * @param packageNames The valid package names a node can come from. */ private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, - int connectionId, boolean bypassCache) { + int connectionId, boolean bypassCache, String[] packageNames) { if (info != null) { info.setConnectionId(connectionId); + // Empty array means any package name is Okay + if (!ArrayUtils.isEmpty(packageNames) + && !ArrayUtils.contains(packageNames, info.getPackageName().toString())) { + // If the node package not one of the valid ones, pick the top one - this + // is one of the packages running in the introspected UID. + info.setPackageName(packageNames[0]); + } info.setSealed(true); if (!bypassCache) { sAccessibilityCache.add(info); @@ -798,14 +810,16 @@ public final class AccessibilityInteractionClient * @param connectionId The id of the connection to the system. * @param bypassCache Whether or not to bypass the cache. The nodes are added to the cache if * this value is {@code false} + * @param packageNames The valid package names a node can come from. */ private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, - int connectionId, boolean bypassCache) { + int connectionId, boolean bypassCache, String[] packageNames) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, bypassCache); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, + bypassCache, packageNames); } } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 35f6acba04dc..b4499d1acac3 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -888,7 +888,7 @@ public final class AccessibilityManager { * @hide */ public int addAccessibilityInteractionConnection(IWindow windowToken, - IAccessibilityInteractionConnection connection) { + String packageName, IAccessibilityInteractionConnection connection) { final IAccessibilityManager service; final int userId; synchronized (mLock) { @@ -899,7 +899,8 @@ public final class AccessibilityManager { userId = mUserId; } try { - return service.addAccessibilityInteractionConnection(windowToken, connection, userId); + return service.addAccessibilityInteractionConnection(windowToken, connection, + packageName, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index faea9200c522..9c2f6bb8cc33 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3695,8 +3695,9 @@ public class AccessibilityNodeInfo implements Parcelable { if (DEBUG) { builder.append("; sourceNodeId: " + mSourceNodeId); - builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); - builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); + builder.append("; windowId: " + mWindowId); + builder.append("; accessibilityViewId: ").append(getAccessibilityViewId(mSourceNodeId)); + builder.append("; virtualDescendantId: ").append(getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); builder.append("; traversalBefore: ").append(mTraversalBefore); builder.append("; traversalAfter: ").append(mTraversalAfter); @@ -3726,8 +3727,8 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("]"); } - builder.append("; boundsInParent: " + mBoundsInParent); - builder.append("; boundsInScreen: " + mBoundsInScreen); + builder.append("; boundsInParent: ").append(mBoundsInParent); + builder.append("; boundsInScreen: ").append(mBoundsInScreen); builder.append("; packageName: ").append(mPackageName); builder.append("; className: ").append(mClassName); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 3f499abd2e4d..c93e2c15407b 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -45,7 +45,8 @@ interface IAccessibilityManager { List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId); int addAccessibilityInteractionConnection(IWindow windowToken, - in IAccessibilityInteractionConnection connection, int userId); + in IAccessibilityInteractionConnection connection, + String packageName, int userId); void removeAccessibilityInteractionConnection(IWindow windowToken); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 92d1de8e5a24..3cd8d4a2417d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -24,6 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.graphics.Rect; +import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -697,6 +698,19 @@ public final class InputMethodManager { } } + /** + * Returns a list of VR InputMethod currently installed. + * @hide + */ + @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) + public List<InputMethodInfo> getVrInputMethodList() { + try { + return mService.getVrInputMethodList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + public List<InputMethodInfo> getEnabledInputMethodList() { try { return mService.getEnabledInputMethodList(); @@ -722,7 +736,20 @@ public final class InputMethodManager { } } + /** + * @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was + * intended for IME developers who should be accessing APIs through the service. APIs in this + * class are intended for app developers interacting with the IME. + */ + @Deprecated public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { + showStatusIconInternal(imeToken, packageName, iconId); + } + + /** + * @hide + */ + public void showStatusIconInternal(IBinder imeToken, String packageName, int iconId) { try { mService.updateStatusIcon(imeToken, packageName, iconId); } catch (RemoteException e) { @@ -730,7 +757,20 @@ public final class InputMethodManager { } } + /** + * @deprecated Use {@link InputMethodService#hideStatusIcon()} instead. This method was + * intended for IME developers who should be accessing APIs through the service. APIs in + * this class are intended for app developers interacting with the IME. + */ + @Deprecated public void hideStatusIcon(IBinder imeToken) { + hideStatusIconInternal(imeToken); + } + + /** + * @hide + */ + public void hideStatusIconInternal(IBinder imeToken) { try { mService.updateStatusIcon(imeToken, null, 0); } catch (RemoteException e) { @@ -1108,7 +1148,6 @@ public final class InputMethodManager { } } - /** * This method toggles the input method window display. * If the input window is already displayed, it gets hidden. @@ -1787,8 +1826,19 @@ public final class InputMethodManager { * when it was started, which allows it to perform this operation on * itself. * @param id The unique identifier for the new input method to be switched to. + * @deprecated Use {@link InputMethodService#setInputMethod(String)} instead. This method + * was intended for IME developers who should be accessing APIs through the service. APIs in + * this class are intended for app developers interacting with the IME. */ + @Deprecated public void setInputMethod(IBinder token, String id) { + setInputMethodInternal(token, id); + } + + /** + * @hide + */ + public void setInputMethodInternal(IBinder token, String id) { try { mService.setInputMethod(token, id); } catch (RemoteException e) { @@ -1804,8 +1854,21 @@ public final class InputMethodManager { * itself. * @param id The unique identifier for the new input method to be switched to. * @param subtype The new subtype of the new input method to be switched to. + * @deprecated Use + * {@link InputMethodService#setInputMethodAndSubtype(String, InputMethodSubtype)} + * instead. This method was intended for IME developers who should be accessing APIs through + * the service. APIs in this class are intended for app developers interacting with the IME. */ + @Deprecated public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { + setInputMethodAndSubtypeInternal(token, id, subtype); + } + + /** + * @hide + */ + public void setInputMethodAndSubtypeInternal( + IBinder token, String id, InputMethodSubtype subtype) { try { mService.setInputMethodAndSubtype(token, id, subtype); } catch (RemoteException e) { @@ -1824,8 +1887,19 @@ public final class InputMethodManager { * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, * {@link #HIDE_NOT_ALWAYS} bit set. + * @deprecated Use {@link InputMethodService#hideSoftInputFromInputMethod(int)} + * instead. This method was intended for IME developers who should be accessing APIs through + * the service. APIs in this class are intended for app developers interacting with the IME. */ + @Deprecated public void hideSoftInputFromInputMethod(IBinder token, int flags) { + hideSoftInputFromInputMethodInternal(token, flags); + } + + /** + * @hide + */ + public void hideSoftInputFromInputMethodInternal(IBinder token, int flags) { try { mService.hideMySoftInput(token, flags); } catch (RemoteException e) { @@ -1845,8 +1919,19 @@ public final class InputMethodManager { * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link #SHOW_IMPLICIT} or * {@link #SHOW_FORCED} bit set. + * @deprecated Use {@link InputMethodService#showSoftInputFromInputMethod(int)} + * instead. This method was intended for IME developers who should be accessing APIs through + * the service. APIs in this class are intended for app developers interacting with the IME. */ + @Deprecated public void showSoftInputFromInputMethod(IBinder token, int flags) { + showSoftInputFromInputMethodInternal(token, flags); + } + + /** + * @hide + */ + public void showSoftInputFromInputMethodInternal(IBinder token, int flags) { try { mService.showMySoftInput(token, flags); } catch (RemoteException e) { @@ -2226,8 +2311,19 @@ public final class InputMethodManager { * which allows it to perform this operation on itself. * @return true if the current input method and subtype was successfully switched to the last * used input method and subtype. + * @deprecated Use {@link InputMethodService#switchToLastInputMethod()} instead. This method + * was intended for IME developers who should be accessing APIs through the service. APIs in + * this class are intended for app developers interacting with the IME. */ + @Deprecated public boolean switchToLastInputMethod(IBinder imeToken) { + return switchToLastInputMethodInternal(imeToken); + } + + /** + * @hide + */ + public boolean switchToLastInputMethodInternal(IBinder imeToken) { synchronized (mH) { try { return mService.switchToLastInputMethod(imeToken); @@ -2246,8 +2342,19 @@ public final class InputMethodManager { * belongs to the current IME * @return true if the current input method and subtype was successfully switched to the next * input method and subtype. + * @deprecated Use {@link InputMethodService#switchToNextInputMethod(boolean)} instead. This + * method was intended for IME developers who should be accessing APIs through the service. + * APIs in this class are intended for app developers interacting with the IME. */ + @Deprecated public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) { + return switchToNextInputMethodInternal(imeToken, onlyCurrentIme); + } + + /** + * @hide + */ + public boolean switchToNextInputMethodInternal(IBinder imeToken, boolean onlyCurrentIme) { synchronized (mH) { try { return mService.switchToNextInputMethod(imeToken, onlyCurrentIme); @@ -2267,8 +2374,19 @@ public final class InputMethodManager { * between IMEs and subtypes. * @param imeToken Supplies the identifying token given to an input method when it was started, * which allows it to perform this operation on itself. + * @deprecated Use {@link InputMethodService#shouldOfferSwitchingToNextInputMethod()} + * instead. This method was intended for IME developers who should be accessing APIs through + * the service. APIs in this class are intended for app developers interacting with the IME. */ + @Deprecated public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) { + return shouldOfferSwitchingToNextInputMethodInternal(imeToken); + } + + /** + * @hide + */ + public boolean shouldOfferSwitchingToNextInputMethodInternal(IBinder imeToken) { synchronized (mH) { try { return mService.shouldOfferSwitchingToNextInputMethod(imeToken); diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java index 77df4e3883a7..e13813e5199b 100644 --- a/core/java/android/view/inputmethod/InputMethodManagerInternal.java +++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java @@ -16,6 +16,8 @@ package android.view.inputmethod; +import android.content.ComponentName; + /** * Input method manager local system service interface. * @@ -37,4 +39,9 @@ public interface InputMethodManagerInternal { * Hides the current input method, if visible. */ void hideCurrentInputMethod(); + + /** + * Switches to VR InputMethod defined in the packageName of {@param componentName}. + */ + void startVrInputMethodNoCheck(ComponentName componentName); } diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index f4cbc54b452b..fdc9f92347db 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -48,8 +48,13 @@ public interface TextClassifier { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ - TYPE_UNKNOWN, TYPE_OTHER, TYPE_EMAIL, TYPE_PHONE, TYPE_ADDRESS, TYPE_URL + @StringDef(prefix = { "TYPE_" }, value = { + TYPE_UNKNOWN, + TYPE_OTHER, + TYPE_EMAIL, + TYPE_PHONE, + TYPE_ADDRESS, + TYPE_URL, }) @interface EntityType {} diff --git a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java index 931eb9901288..45555bf98071 100644 --- a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java +++ b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java @@ -26,7 +26,15 @@ import android.view.Choreographer; */ public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider { - private final Choreographer mChoreographer = Choreographer.getSfInstance(); + private final Choreographer mChoreographer; + + public SfVsyncFrameCallbackProvider() { + mChoreographer = Choreographer.getSfInstance(); + } + + public SfVsyncFrameCallbackProvider(Choreographer choreographer) { + mChoreographer = choreographer; + } @Override public void postFrameCallback(Choreographer.FrameCallback callback) { diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 3e231d0aaa8b..57efae61a9c6 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -836,7 +836,6 @@ public class InputMethodUtils { private final Resources mRes; private final ContentResolver mResolver; private final HashMap<String, InputMethodInfo> mMethodMap; - private final ArrayList<InputMethodInfo> mMethodList; /** * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}. @@ -906,7 +905,6 @@ public class InputMethodUtils { mRes = res; mResolver = resolver; mMethodMap = methodMap; - mMethodList = methodList; switchCurrentUser(userId, copyOnWrite); } @@ -1087,7 +1085,7 @@ public class InputMethodUtils { final ArrayList<InputMethodInfo> res = new ArrayList<>(); for (Pair<String, ArrayList<String>> ims: imsList) { InputMethodInfo info = mMethodMap.get(ims.first); - if (info != null) { + if (info != null && !info.isVrOnly()) { res.add(info); } } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index b9798075ad27..ca8624d9c01e 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -36,6 +36,7 @@ import com.android.internal.view.IInputMethodClient; interface IInputMethodManager { // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); + List<InputMethodInfo> getVrInputMethodList(); // TODO: Use ParceledListSlice instead List<InputMethodInfo> getEnabledInputMethodList(); List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId, diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java index 6a0edef49643..82affe2f86b1 100644 --- a/core/java/com/android/internal/widget/ButtonBarLayout.java +++ b/core/java/com/android/internal/widget/ButtonBarLayout.java @@ -151,7 +151,7 @@ public class ButtonBarLayout extends LinearLayout { private void setStacked(boolean stacked) { setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL); - setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM); + setGravity(stacked ? Gravity.END : Gravity.BOTTOM); final View spacer = findViewById(R.id.spacer); if (spacer != null) { diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index 26023b499919..e013553ec046 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.util.Pair; import android.view.Gravity; @@ -203,6 +204,11 @@ public class NotificationActionListLayout extends LinearLayout { public void onViewAdded(View child) { super.onViewAdded(child); clearMeasureOrder(); + // For some reason ripples + notification actions seem to be an unhappy combination + // b/69474443 so just turn them off for now. + if (child.getBackground() instanceof RippleDrawable) { + ((RippleDrawable)child.getBackground()).setForceSoftware(true); + } } @Override diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 43544862b5ec..8848e3939008 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -33,6 +33,7 @@ import android.os.storage.StorageManager; import android.provider.Downloads; import android.text.TextUtils; import android.util.AtomicFile; +import android.util.EventLog; import android.util.Slog; import android.util.Xml; @@ -40,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; +import com.android.server.DropboxLogTags; import java.io.File; import java.io.FileInputStream; @@ -297,6 +299,7 @@ public class BootReceiver extends BroadcastReceiver { Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers); + EventLog.writeEvent(DropboxLogTags.DROPBOX_FILE_COPY, filename, maxSize, tag); } private static void addAuditErrorsToDropBox(DropBoxManager db, diff --git a/core/java/com/android/server/DropboxLogTags.logtags b/core/java/com/android/server/DropboxLogTags.logtags new file mode 100644 index 000000000000..c461cfed9ceb --- /dev/null +++ b/core/java/com/android/server/DropboxLogTags.logtags @@ -0,0 +1,12 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +# The java package name happens to be the same as frameworks/base/services/core +# /java/com/android/server/EventLogTags.logtags. To avoid conflict, this file's name cannot +# be EventLogTags.logtags because it generates a class with the same name. + +option java_package com.android.server; + +# ----------------------------- +# BootReceiver.java +# ----------------------------- +81002 dropbox_file_copy (FileName|3),(Size|1),(Tag|3) diff --git a/core/java/org/chromium/arc/EventLogTags.logtags b/core/java/org/chromium/arc/EventLogTags.logtags new file mode 100644 index 000000000000..1b7160e90224 --- /dev/null +++ b/core/java/org/chromium/arc/EventLogTags.logtags @@ -0,0 +1,11 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package org.chromium.arc + +# We use ID range 300000-399999 for ARC. +# In case of conflicts build will fail, so we do not need to worry too much +# about it. + +# Emitted in ARC system events, such as start of ARC services. +# These events will be watched in automations like autotests. +300000 arc_system_event (event|3) diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 460a0d75ca4e..564afeb39490 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -101,7 +101,8 @@ public: SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height()); lattice.fBounds = &src; NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height()); - lattice.fFlags = nullptr; + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; SkRegion* region = nullptr; if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index d3da21b5635b..b9ff0a787931 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -747,7 +747,7 @@ static long long get_zram_mem_used() static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out) { - char buffer[1024]; + char buffer[4096]; size_t numFound = 0; if (out == NULL) { diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index cff187992fdb..0fa428e9c114 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -28,12 +28,8 @@ message BatteryStatsProto { optional int64 parcel_version = 2; optional string start_platform_version = 3; optional string end_platform_version = 4; - optional BatteryHistoryProto history = 5; - repeated UidProto uids = 6; - optional SystemProto system = 7; -} - -message BatteryHistoryProto { + repeated UidProto uids = 5; + optional SystemProto system = 6; } message ControllerActivityProto { diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 8ed958dcfec4..4015a7e7cea9 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -45,12 +45,21 @@ import "frameworks/base/libs/incident/proto/android/section.proto"; package android.os; +// This field contains internal metadata associated with an incident report, +// such as the section ids and privacy policy specs from caller as well as how long +// and how many bytes a section takes, etc. +message IncidentMetadata { + +} + // privacy field options must not be set at this level because all // the sections are able to be controlled and configured by section ids. // Instead privacy field options need to be configured in each section proto message. message IncidentProto { - // Incident header + // Incident header from callers repeated IncidentHeaderProto header = 1; + // Internal metadata of incidentd + optional IncidentMetadata metadata = 2; // Device information optional SystemPropertiesProto system_properties = 1000 [ diff --git a/core/proto/android/os/incidentheader.proto b/core/proto/android/os/incidentheader.proto index ce924da5ee3a..f0c736a866a6 100644 --- a/core/proto/android/os/incidentheader.proto +++ b/core/proto/android/os/incidentheader.proto @@ -16,18 +16,34 @@ syntax = "proto2"; option java_multiple_files = true; -option java_outer_classname = "IncidentHeaderProtoMetadata"; package android.os; +// IncidentHeaderProto contains extra information the caller of incidentd want to +// attach in an incident report, the data should just be informative. message IncidentHeaderProto { - enum Cause { - CAUSE_UNKNOWN = 0; - CAUSE_USER = 1; - CAUSE_ANR = 2; - CAUSE_CRASH = 3; + + // From statsd config, the name of the anomaly, usually human readable. + optional string incident_name = 1; + + // Format a human readable reason why an incident report is requested. + // It's optional and may directly come from a user clicking the bug-report button. + optional string reason = 2; + + // From statsd, the metric which causes the anomaly triggered. + optional string metric_name = 3; + + // From statsd, the metric value exceeds the threshold. This is useful for + // ValueMetric and GaugeMetric. + oneof metric_value { + int64 int64_value = 4; + double double_value = 5; } - optional Cause cause = 1; + // Defines which stats config used to fire the request. + message StatsdConfigKey { + optional int32 uid = 1; + optional string name = 2; + } + optional StatsdConfigKey config_key = 6; } - diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto index 921bb5a4b471..7ffdc9628cc6 100644 --- a/core/proto/android/os/system_properties.proto +++ b/core/proto/android/os/system_properties.proto @@ -17,641 +17,483 @@ syntax = "proto2"; option java_multiple_files = true; -option java_outer_classname = "SystemPropertiesProtoMetadata"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/tools/streaming_proto/stream.proto"; package android.os; -// System Properties from getprop +// Android Platform Exported System Properties +// TODO: This is not the completed list, new properties need to be whitelisted. message SystemPropertiesProto { option (stream_proto.stream_msg).enable_fields_mapping = true; - // Properties that are not specified below are appended here. + // Properties that are not specified below would be appended here. + // These values stay on device only. message Property { + option (android.msg_privacy).dest = DEST_LOCAL; + optional string name = 1; optional string value = 2; } repeated Property extra_properties = 1; - optional int32 aaudio_hw_burst_min_usec = 2; - optional int32 aaudio_mmap_exclusive_policy = 3; - optional int32 aaudio_mmap_policy = 4; - - optional int32 af_fast_track_multiplier = 5; - - optional int32 audio_adm_buffering_ms = 6; - optional int32 audio_hal_period_size = 7; - - optional string dalvik_vm_appimageformat = 8; - optional string dalvik_vm_dex2oat_Xms = 9; - optional string dalvik_vm_dex2oat_Xmx = 10; - optional bool dalvik_vm_dexopt_secondary = 11; - optional string dalvik_vm_heapgrowthlimit = 12; - optional string dalvik_vm_heapmaxfree = 13; - optional string dalvik_vm_heapminfree = 14; - optional string dalvik_vm_heapsize = 15; - optional string dalvik_vm_heapstartsize = 16; - optional float dalvik_vm_heaptargetutilization = 17; - optional string dalvik_vm_image_dex2oat_Xms = 18; - optional string dalvik_vm_image_dex2oat_Xmx = 19; - optional string dalvik_vm_image_dex2oat_filter = 20; - optional string dalvik_vm_isa_arm_features = 21; - optional string dalvik_vm_isa_arm_variant = 22; - optional string dalvik_vm_isa_arm64_features = 23; - optional string dalvik_vm_isa_arm64_variant = 24; - optional int32 dalvik_vm_lockprof_threshold = 25; - optional string dalvik_vm_stack_trace_dir = 26; - optional bool dalvik_vm_usejit = 27; - optional bool dalvik_vm_usejitprofiles = 28; - - optional int32 debug_atrace_tags_enableflags = 29; - optional int32 debug_force_rtl = 30; - optional string debug_htc_hrdump = 31; - - optional int32 dev_bootcomplete = 32; - - optional bool drm_service_enabled = 33; - - optional int32 fmas_hdph_sgain = 34; - - optional int32 gsm_current_phone_type = 35; - optional string gsm_network_type = 36; - optional string gsm_operator_alpha = 37; - optional string gsm_operator_iso_country = 38; - optional bool gsm_operator_isroaming = 39; - optional string gsm_operator_numeric = 40; - optional string gsm_sim_operator_alpha = 41; - optional string gsm_sim_operator_iso_country = 42; - optional int32 gsm_sim_operator_numeric = 43; - optional string gsm_sim_state = 44; - optional string gsm_version_baseband = 45; - optional string gsm_version_ril_impl = 46; - - optional sint32 htc_camera_sensor_inf = 47; - - optional bool hwservicemanager_ready = 48; - - // Enum values for a lot of system properties - enum Status { - STATUS_UNKNOWN = 0; - STATUS_RUNNING = 1; - STATUS_STOPPED = 2; + optional int32 aac_drc_boost = 2; + optional int32 aac_drc_cut = 3; + optional int32 aac_drc_enc_target_level = 4; + optional int32 aac_drc_heavy = 5; + optional int32 aac_drc_reference_level = 6; + + message Aaudio { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional int32 hw_burst_min_usec = 1; + optional int32 minimum_sleep_usec = 2; + optional int32 mixer_bursts = 3; + optional int32 mmap_exclusive_policy = 4; + optional int32 mmap_policy = 5; + optional int32 wakeup_delay_usec = 6; + + // Next Tag: 7 + } + optional Aaudio aaudio = 7; + + optional int32 af_fast_track_multiplier = 8; + + message Camera { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional bool disable_zsl_mode = 1; + optional int32 fifo_disable = 2; + + // Next Tag: 3 + } + optional Camera camera = 9; + + message DalvikVm { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string appimageformat = 1; + optional string backgroundgctype = 2; + optional bool checkjni = 3; + optional string dex2oat_filter = 4; + optional string dex2oat_flags = 5; + optional int32 dex2oat_threads = 6; + optional string dex2oat_Xms = 7; + optional string dex2oat_Xmx = 8; + optional bool dexopt_secondary = 9; + optional string execution_mode = 10; + optional string extra_opts = 11; + optional string gctype = 12; + optional string heapgrowthlimit = 13; + optional string heapmaxfree = 14; + optional string heapminfree = 15; + optional string heapsize = 16; + optional string heapstartsize = 17; + optional double heaptargetutilization = 18; + optional int32 hot_startup_method_samples = 19; + optional string image_dex2oat_filter = 20; + optional string image_dex2oat_flags = 21; + optional int32 image_dex2oat_threads = 22; + optional string image_dex2oat_Xms = 23; + optional string image_dex2oat_Xmx = 24; + optional string isa_arm_features = 25; + optional string isa_arm_variant = 26; + optional string isa_arm64_features = 27; + optional string isa_arm64_variant = 28; + optional string isa_mips_features = 29; + optional string isa_mips_variant = 30; + optional string isa_mips64_features = 31; + optional string isa_mips64_variant = 32; + optional string isa_unknown_features = 33; + optional string isa_unknown_variant = 34; + optional string isa_x86_64_features = 35; + optional string isa_x86_64_variant = 36; + optional string isa_x86_features = 37; + optional string isa_x86_variant = 38; + optional string jitinitialsize = 39; + optional string jitmaxsize = 40; + optional int32 jitprithreadweight = 41; + optional int32 jitthreshold = 42; + optional int32 jittransitionweight = 43; + optional string jniopts = 44; + optional int32 lockprof_threshold = 45; + optional bool method_trace = 46; + optional string method_trace_file = 47; + optional int32 method_trace_file_siz = 48; + optional bool method_trace_stream = 49; + optional bool profilesystemserver = 50; + optional string stack_trace_dir = 51; + optional bool usejit = 52; + optional bool usejitprofiles = 53; + optional int32 zygote_max_boot_retry = 54; + + // Next Tag: 55 + } + optional DalvikVm dalvik_vm = 10; + + optional bool drm_64bit_enabled = 11; + optional bool drm_service_enabled = 12; + optional bool dumpstate_dry_run = 13; + optional string gsm_sim_operator_numeric = 14; + optional bool hal_instrumentation_enable = 15; + + message InitSvc { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + enum Status { + STATUS_UNKNOWN = 0; + STATUS_RUNNING = 1; + STATUS_STOPPED = 2; + } + optional Status adbd = 1; + optional Status audioserver = 2; + optional Status bootanim = 3; + optional Status bufferhubd = 4; + optional Status cameraserver = 5; + optional Status clear_bcb = 6; + optional Status drm = 7; + optional Status gatekeeperd = 8; + optional Status healthd = 9; + optional Status hidl_memory = 10; + optional Status hostapd = 11; + optional Status hwservicemanager = 12; + optional Status installd = 13; + optional Status keystore = 14; + optional Status lmkd = 15; + optional Status logd = 16; + optional Status logd_reinit = 17; + optional Status media = 18; + optional Status mediadrm = 19; + optional Status mediaextractor = 20; + optional Status mediametrics = 21; + optional Status netd = 22; + optional Status performanced = 23; + optional Status ril_daemon = 24; + optional Status servicemanager = 25; + optional Status storaged = 26; + optional Status surfaceflinger = 27; + optional Status thermalservice = 28; + optional Status tombstoned = 29; + optional Status ueventd = 30; + optional Status update_engine = 31; + optional Status update_verifier_nonencrypted = 32; + optional Status virtual_touchpad = 33; + optional Status vndservicemanager = 34; + optional Status vold = 35; + optional Status vr_hwc = 36; + optional Status webview_zygote32 = 37; + optional Status wificond = 38; + optional Status wpa_supplicant = 39; + optional Status zygote = 40; + optional Status zygote_secondary = 41; + + // Next Tag: 42 + } + optional InitSvc init_svc = 16; + + optional bool keyguard_no_require_sim = 17; + optional string libc_debug_malloc_options = 18; + optional string libc_debug_malloc_program = 19; + + message Log { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string tag_WifiHAL = 1; + optional string tag_stats_log = 2; + + // Next Tag: 3 } - optional Status init_svc_adbd = 49; - optional Status init_svc_audioserver = 50; - optional Status init_svc_bootanim = 51; - optional Status init_svc_bufferhubd = 52; - optional Status init_svc_cameraserver = 53; - optional Status init_svc_clear_bcb = 54; - optional Status init_svc_drm = 55; - optional Status init_svc_gatekeeperd = 56; - optional Status init_svc_healthd = 57; - optional Status init_svc_hidl_memory = 58; - optional Status init_svc_hostapd = 59; - optional Status init_svc_hwservicemanager = 60; - optional Status init_svc_installd = 61; - optional Status init_svc_keystore = 62; - optional Status init_svc_lmkd = 63; - optional Status init_svc_logd = 64; - optional Status init_svc_logd_reinit = 65; - optional Status init_svc_media = 66; - optional Status init_svc_mediadrm = 67; - optional Status init_svc_mediaextractor = 68; - optional Status init_svc_mediametrics = 69; - optional Status init_svc_netd = 70; - optional Status init_svc_performanced = 71; - optional Status init_svc_ril_daemon = 72; - optional Status init_svc_servicemanager = 73; - optional Status init_svc_storaged = 74; - optional Status init_svc_surfaceflinger = 75; - optional Status init_svc_thermalservice = 76; - optional Status init_svc_tombstoned = 77; - optional Status init_svc_ueventd = 78; - optional Status init_svc_update_engine = 79; - optional Status init_svc_update_verifier_nonencrypted = 80; - optional Status init_svc_vendor_adsprpcd = 81; - optional Status init_svc_vendor_atfwd = 82; - optional Status init_svc_vendor_audio_hal_2_0 = 83; - optional Status init_svc_vendor_bluetooth_1_0 = 84; - optional Status init_svc_vendor_boot_hal_1_0 = 85; - optional Status init_svc_vendor_camera_provider_2_4 = 86; - optional Status init_svc_vendor_cas_hal_1_0 = 87; - optional Status init_svc_vendor_cnd = 88; - optional Status init_svc_vendor_cnss_daemon = 89; - optional Status init_svc_vendor_cnss_diag = 90; - optional Status init_svc_vendor_configstore_hal = 91; - optional Status init_svc_vendor_contexthub_hal_1_0 = 92; - optional Status init_svc_vendor_devstart_sh = 93; - optional Status init_svc_vendor_drm_hal_1_0 = 94; - optional Status init_svc_vendor_drm_widevine_hal_1_0 = 95; - optional Status init_svc_vendor_dumpstate_1_0 = 96; - optional Status init_svc_vendor_flash_nanohub_fw = 97; - optional Status init_svc_vendor_foreground_sh = 98; - optional Status init_svc_vendor_fps_hal = 99; - optional Status init_svc_vendor_gatekeeper_1_0 = 100; - optional Status init_svc_vendor_gnss_service = 101; - optional Status init_svc_vendor_gralloc_2_0 = 102; - optional Status init_svc_vendor_hci_filter_root = 103; - optional Status init_svc_vendor_hwcomposer_2_1 = 104; - optional Status init_svc_vendor_imsdatadaemon = 105; - optional Status init_svc_vendor_imsqmidaemon = 106; - optional Status init_svc_vendor_init_radio_sh = 107; - optional Status init_svc_vendor_irsc_util = 108; - optional Status init_svc_vendor_keymaster_3_0 = 109; - optional Status init_svc_vendor_light_hal_2_0 = 110; - optional Status init_svc_vendor_loc_launcher = 111; - optional Status init_svc_vendor_media_omx = 112; - optional Status init_svc_vendor_memtrack_hal_1_0 = 113; - optional Status init_svc_vendor_mid_sh = 114; - optional Status init_svc_vendor_msm_irqbalance = 115; - optional Status init_svc_vendor_nanohub_slpi = 116; - optional Status init_svc_vendor_netmgrd = 117; - optional Status init_svc_vendor_nfc_hal_service = 118; - optional Status init_svc_vendor_per_mgr = 119; - optional Status init_svc_vendor_per_proxy = 120; - optional Status init_svc_vendor_perfd = 121; - optional Status init_svc_vendor_port_bridge = 122; - optional Status init_svc_vendor_power_hal_1_1 = 123; - optional Status init_svc_vendor_power_sh = 124; - optional Status init_svc_vendor_qseecomd = 125; - optional Status init_svc_vendor_ramdump_auto = 126; - optional Status init_svc_vendor_rmt_storage = 127; - optional Status init_svc_vendor_sensors_hal_1_0 = 128; - optional Status init_svc_vendor_ss_ramdump = 129; - optional Status init_svc_vendor_ssr_setup = 130; - optional Status init_svc_vendor_thermal_engine = 131; - optional Status init_svc_vendor_time_daemon = 132; - optional Status init_svc_vendor_usb_hal_1_1 = 133; - optional Status init_svc_vendor_vibrator_1_0 = 134; - optional Status init_svc_vendor_vr_1_0 = 135; - optional Status init_svc_vendor_wifi_hal_legacy = 136; - optional Status init_svc_virtual_touchpad = 137; - optional Status init_svc_vndservicemanager = 138; - optional Status init_svc_vold = 139; - optional Status init_svc_vr_hwc = 140; - optional Status init_svc_webview_zygote32 = 141; - optional Status init_svc_wificond = 142; - optional Status init_svc_wpa_supplicant = 143; - optional Status init_svc_zygote = 144; - optional Status init_svc_zygote_secondary = 145; - - optional bool keyguard_no_require_sim = 146; - - optional string log_tag_WifiHAL = 147; - - optional bool logd_logpersistd_enable = 148; - - optional bool media_mediadrmservice_enable = 149; - optional bool media_recorder_show_manufacturer_and_model = 150; - - optional string net_bt_name = 151; - optional string net_dns1 = 152; - optional string net_dns2 = 153; - optional string net_dns3 = 154; - optional string net_dns4 = 155; - optional bool net_lte_ims_data_enabled = 156; - optional int32 net_qtaguid_enabled = 157; - optional int32 net_tcp_2g_init_rwnd = 158; - repeated int32 net_tcp_buffersize_default = 159; - repeated int32 net_tcp_buffersize_edge = 160; - repeated int32 net_tcp_buffersize_evdo = 161; - repeated int32 net_tcp_buffersize_gprs = 162; - repeated int32 net_tcp_buffersize_hsdpa = 163; - repeated int32 net_tcp_buffersize_hspa = 164; - repeated int32 net_tcp_buffersize_hspap = 165; - repeated int32 net_tcp_buffersize_hsupa = 166; - repeated int32 net_tcp_buffersize_lte = 167; - repeated int32 net_tcp_buffersize_umts = 168; - repeated int32 net_tcp_buffersize_wifi = 169; - optional int32 net_tcp_default_init_rwnd = 170; - - optional bool nfc_initialized = 171; - - optional bool persist_audio_fluence_speaker = 172; - optional bool persist_audio_fluence_voicecall = 173; - optional bool persist_audio_fluence_voicecomm = 174; - optional bool persist_audio_fluence_voicerec = 175; - - optional int32 persist_camera_debug_logfile = 176; - optional int32 persist_camera_eis_enable = 177; - optional int32 persist_camera_gyro_android = 178; - optional int32 persist_camera_is_type = 179; - optional int32 persist_camera_tnr_preview = 180; - optional int32 persist_camera_tnr_video = 181; - optional int32 persist_camera_tof_direct = 182; - - optional int32 persist_cne_feature = 183; - - optional bool persist_data_iwlan_enable = 184; - optional string persist_data_mode = 185; - - optional int32 persist_radio_RATE_ADAPT_ENABLE = 186; - optional int32 persist_radio_ROTATION_ENABLE = 187; - optional int32 persist_radio_VT_ENABLE = 188; - optional int32 persist_radio_VT_HYBRID_ENABLE = 189; - optional int32 persist_radio_adb_log_on = 190; - optional int32 persist_radio_airplane_mode_on = 191; - optional int32 persist_radio_apm_sim_not_pwdn = 192; - optional int32 persist_radio_custom_ecc = 193; - optional bool persist_radio_data_con_rprt = 194; - optional int32 persist_radio_data_ltd_sys_ind = 195; - optional string persist_radio_enable_tel_mon = 196; - optional bool persist_radio_eons_enabled = 197; - optional bool persist_radio_is_wps_enabled = 198; - optional int32 persist_radio_pwropt_modepref_0 = 199; - optional int32 persist_radio_ril_payload_on = 200; - optional int32 persist_radio_sglte_target = 201; - optional int32 persist_radio_sib16_support = 202; - optional int32 persist_radio_smlog_switch = 203; - optional int32 persist_radio_snapshot_enabled = 204; - optional int32 persist_radio_snapshot_timer = 205; - optional int32 persist_radio_sw_mbn_loaded = 206; - optional int32 persist_radio_sw_mbn_update = 207; - optional string persist_radio_ver_info = 208; - optional int32 persist_radio_videopause_mode = 209; - - optional int32 persist_rcs_supported = 210; - - optional string persist_sys_boot_reason = 211; - optional int32 persist_sys_cnd_iwlan = 212; - optional string persist_sys_dalvik_vm_lib_2 = 213; - optional string persist_sys_gps_lpp = 214; - optional string persist_sys_locale = 215; - optional int32 persist_sys_ssr_enable_ramdumps = 216; - optional string persist_sys_ssr_restart_level = 217; - optional string persist_sys_timezone = 218; - optional string persist_sys_usb_config = 219; - optional int32 persist_sys_webview_vmsize = 220; - - optional string pm_dexopt_ab_ota = 221; - optional string pm_dexopt_bg_dexopt = 222; - optional string pm_dexopt_boot = 223; - optional string pm_dexopt_first_boot = 224; - optional string pm_dexopt_inactive = 225; - optional string pm_dexopt_install = 226; - optional string pm_dexopt_shared = 227; - - optional string qcom_bluetooth_soc = 228; - - optional int32 qdcm_diagonal_matrix_mode = 229; - optional int32 qdcm_only_pcc_for_trans = 230; - - repeated string ril_ecclist = 231; - optional int32 ril_power_backoff_suppressed = 232; - optional int32 ril_qcril_pre_init_lock_held = 233; - optional string ril_voice_network_type = 234; - optional string rild_libpath = 235; - - optional int32 ro_adb_secure = 236; - optional int32 ro_allow_mock_location = 237; - repeated string ro_atrace_core_services = 238; - optional string ro_baseband = 239; - optional int32 ro_bionic_ld_warning = 240; - optional bool ro_bluetooth_dun = 241; - optional string ro_bluetooth_hfp_ver = 242; - optional bool ro_bluetooth_sap = 243; - optional string ro_board_platform = 244; - - optional string ro_boot_baseband = 245; - optional string ro_boot_bootdevice = 246; - optional string ro_boot_bootloader = 247; - optional string ro_boot_bootreason = 248; - repeated string ro_boot_boottime = 249; - optional int32 ro_boot_cid = 250; - optional string ro_boot_console = 251; - optional string ro_boot_ddrinfo = 252; - optional string ro_boot_ddrsize = 253; - optional int32 ro_boot_flash_locked = 254; - optional int32 ro_boot_fp_src = 255; - optional string ro_boot_hardware = 256; - optional string ro_boot_hardware_color = 257; - optional string ro_boot_hardware_ddr = 258; - optional string ro_boot_hardware_revision = 259; - optional string ro_boot_hardware_sku = 260; - optional string ro_boot_hardware_ufs = 261; - optional string ro_boot_htc_hrdump = 262; - optional int32 ro_boot_keymaster = 263; - optional string ro_boot_mid = 264; - optional int32 ro_boot_msm_hw_ver_id = 265; - optional int32 ro_boot_oem_unlock_support = 266; - optional int32 ro_boot_qf_st = 267; - optional int32 ro_boot_ramdump_enable = 268; - optional string ro_boot_serialno = 269; - optional string ro_boot_slot_suffix = 270; - optional int32 ro_boot_temp_protect_ignore = 271; - optional string ro_boot_vendor_overlay_theme = 272; - optional string ro_boot_verifiedbootstate = 273; - optional string ro_boot_veritymode = 274; - optional string ro_boot_wificountrycode = 275; - - optional string ro_bootimage_build_date = 276; - optional int64 ro_bootimage_build_date_utc = 277; - optional string ro_bootimage_build_fingerprint = 278; - - optional string ro_bootloader = 279; - optional string ro_bootmode = 280; - - optional int64 ro_boottime_adbd = 281; - optional int64 ro_boottime_audioserver = 282; - optional int64 ro_boottime_bootanim = 283; - optional int64 ro_boottime_bufferhubd = 284; - optional int64 ro_boottime_cameraserver = 285; - optional int64 ro_boottime_clear_bcb = 286; - optional int64 ro_boottime_drm = 287; - optional int64 ro_boottime_gatekeeperd = 288; - optional int64 ro_boottime_healthd = 289; - optional int64 ro_boottime_hidl_memory = 290; - optional int64 ro_boottime_hwservicemanager = 291; - optional int64 ro_boottime_init = 292; - optional int64 ro_boottime_init_cold_boot_wait = 293; - optional int32 ro_boottime_init_mount_all_early = 294; - optional int32 ro_boottime_init_mount_all_late = 295; - optional int32 ro_boottime_init_selinux = 296; - optional int64 ro_boottime_installd = 297; - optional int64 ro_boottime_keystore = 298; - optional int64 ro_boottime_lmkd = 299; - optional int64 ro_boottime_logd = 300; - optional int64 ro_boottime_logd_reinit = 301; - optional int64 ro_boottime_media = 302; - optional int64 ro_boottime_mediadrm = 303; - optional int64 ro_boottime_mediaextractor = 304; - optional int64 ro_boottime_mediametrics = 305; - optional int64 ro_boottime_netd = 306; - optional int64 ro_boottime_performanced = 307; - optional int64 ro_boottime_ril_daemon = 308; - optional int64 ro_boottime_servicemanager = 309; - optional int64 ro_boottime_storaged = 310; - optional int64 ro_boottime_surfaceflinger = 311; - optional int64 ro_boottime_thermalservice = 312; - optional int64 ro_boottime_tombstoned = 313; - optional int64 ro_boottime_ueventd = 314; - optional int64 ro_boottime_update_engine = 315; - optional int64 ro_boottime_update_verifier_nonencrypted = 316; - optional int64 ro_boottime_vendor_adsprpcd = 317; - optional int64 ro_boottime_vendor_atfwd = 318; - optional int64 ro_boottime_vendor_audio_hal_2_0 = 319; - optional int64 ro_boottime_vendor_bluetooth_1_0 = 320; - optional int64 ro_boottime_vendor_boot_hal_1_0 = 321; - optional int64 ro_boottime_vendor_camera_provider_2_4 = 322; - optional int64 ro_boottime_vendor_cas_hal_1_0 = 323; - optional int64 ro_boottime_vendor_cnd = 324; - optional int64 ro_boottime_vendor_cnss_daemon = 325; - optional int64 ro_boottime_vendor_cnss_diag = 326; - optional int64 ro_boottime_vendor_configstore_hal = 327; - optional int64 ro_boottime_vendor_contexthub_hal_1_0 = 328; - optional int64 ro_boottime_vendor_devstart_sh = 329; - optional int64 ro_boottime_vendor_drm_hal_1_0 = 330; - optional int64 ro_boottime_vendor_drm_widevine_hal_1_0 = 331; - optional int64 ro_boottime_vendor_dumpstate_1_0 = 332; - optional int64 ro_boottime_vendor_flash_nanohub_fw = 333; - optional int64 ro_boottime_vendor_foreground_sh = 334; - optional int64 ro_boottime_vendor_fps_hal = 335; - optional int64 ro_boottime_vendor_gatekeeper_1_0 = 336; - optional int64 ro_boottime_vendor_gnss_service = 337; - optional int64 ro_boottime_vendor_gralloc_2_0 = 338; - optional int64 ro_boottime_vendor_hci_filter_root = 339; - optional int64 ro_boottime_vendor_hwcomposer_2_1 = 340; - optional int64 ro_boottime_vendor_imsdatadaemon = 341; - optional int64 ro_boottime_vendor_imsqmidaemon = 342; - optional int64 ro_boottime_vendor_init_radio_sh = 343; - optional int64 ro_boottime_vendor_irsc_util = 344; - optional int64 ro_boottime_vendor_keymaster_3_0 = 345; - optional int64 ro_boottime_vendor_light_hal_2_0 = 346; - optional int64 ro_boottime_vendor_loc_launcher = 347; - optional int64 ro_boottime_vendor_media_omx = 348; - optional int64 ro_boottime_vendor_memtrack_hal_1_0 = 349; - optional int64 ro_boottime_vendor_mid_sh = 350; - optional int64 ro_boottime_vendor_msm_irqbalance = 351; - optional int64 ro_boottime_vendor_nanohub_slpi = 352; - optional int64 ro_boottime_vendor_netmgrd = 353; - optional int64 ro_boottime_vendor_nfc_hal_service = 354; - optional int64 ro_boottime_vendor_per_mgr = 355; - optional int64 ro_boottime_vendor_per_proxy = 356; - optional int64 ro_boottime_vendor_perfd = 357; - optional int64 ro_boottime_vendor_port_bridge = 358; - optional int64 ro_boottime_vendor_power_hal_1_1 = 359; - optional int64 ro_boottime_vendor_power_sh = 360; - optional int64 ro_boottime_vendor_qseecomd = 361; - optional int64 ro_boottime_vendor_ramdump_auto = 362; - optional int64 ro_boottime_vendor_rmt_storage = 363; - optional int64 ro_boottime_vendor_sensors_hal_1_0 = 364; - optional int64 ro_boottime_vendor_ss_ramdump = 365; - optional int64 ro_boottime_vendor_ssr_setup = 366; - optional int64 ro_boottime_vendor_thermal_engine = 367; - optional int64 ro_boottime_vendor_time_daemon = 368; - optional int64 ro_boottime_vendor_usb_hal_1_1 = 369; - optional int64 ro_boottime_vendor_vibrator_1_0 = 370; - optional int64 ro_boottime_vendor_vr_1_0 = 371; - optional int64 ro_boottime_vendor_wifi_hal_legacy = 372; - optional int64 ro_boottime_virtual_touchpad = 373; - optional int64 ro_boottime_vndservicemanager = 374; - optional int64 ro_boottime_vold = 375; - optional int64 ro_boottime_vr_hwc = 376; - optional int64 ro_boottime_webview_zygote32 = 377; - optional int64 ro_boottime_wificond = 378; - optional int64 ro_boottime_wpa_supplicant = 379; - optional int64 ro_boottime_zygote = 380; - optional int64 ro_boottime_zygote_secondary = 381; - - optional string ro_bt_bdaddr_path = 382; - - optional bool ro_build_ab_update = 383; - optional string ro_build_characteristics = 384; - optional string ro_build_date = 385; - optional int64 ro_build_date_utc = 386; - optional string ro_build_description = 387; - optional string ro_build_display_id = 388; - optional string ro_build_expect_baseband = 389; - optional string ro_build_expect_bootloader = 390; - optional string ro_build_fingerprint = 391; - optional string ro_build_flavor = 392; - optional string ro_build_host = 393; - optional string ro_build_id = 394; - optional string ro_build_product = 395; - optional bool ro_build_system_root_image = 396; - optional string ro_build_tags = 397; - optional string ro_build_type = 398; - optional string ro_build_user = 399; - optional string ro_build_version_all_codenames = 400; - optional string ro_build_version_base_os = 401; - optional string ro_build_version_codename = 402; - optional string ro_build_version_incremental = 403; - optional int32 ro_build_version_preview_sdk = 404; - optional string ro_build_version_release = 405; - optional int32 ro_build_version_sdk = 406; - optional string ro_build_version_security_patch = 407; - - optional int32 ro_camera_notify_nfc = 408; - optional string ro_carrier = 409; - optional bool ro_com_android_dataroaming = 410; - optional bool ro_com_android_prov_mobiledata = 411; - optional string ro_com_google_clientidbase = 412; - optional int32 ro_com_google_ime_theme_id = 413; - - optional string ro_config_alarm_alert = 414; - optional string ro_config_notification_sound = 415; - optional string ro_config_ringtone = 416; - optional int32 ro_config_vc_call_vol_steps = 417; - optional string ro_control_privapp_permissions = 418; - optional int32 ro_cp_system_other_odex = 419; - - optional string ro_crypto_scrypt_params = 420; - optional string ro_crypto_state = 421; - optional string ro_crypto_type = 422; - optional string ro_crypto_volume_filenames_mode = 423; - optional int32 ro_dalvik_vm_native_bridge = 424; - optional int32 ro_debuggable = 425; - optional bool ro_device_owner = 426; - optional string ro_error_receiver_system_apps = 427; - - optional int32 ro_facelock_black_timeout = 428; - optional int32 ro_facelock_det_timeout = 429; - optional int32 ro_facelock_est_max_time = 430; - optional int32 ro_facelock_rec_timeout = 431; - - optional string ro_frp_pst = 432; - - optional string ro_hardware = 433; - optional string ro_hardware_power = 434; - - optional int32 ro_hwui_drop_shadow_cache_size = 435; - optional int32 ro_hwui_gradient_cache_size = 436; - optional int32 ro_hwui_layer_cache_size = 437; - optional int32 ro_hwui_path_cache_size = 438; - optional int32 ro_hwui_r_buffer_cache_size = 439; - optional int32 ro_hwui_text_large_cache_height = 440; - optional int32 ro_hwui_text_large_cache_width = 441; - optional int32 ro_hwui_text_small_cache_height = 442; - optional int32 ro_hwui_text_small_cache_width = 443; - optional float ro_hwui_texture_cache_flushrate = 444; - optional int32 ro_hwui_texture_cache_size = 445; - - optional bool ro_init_subcontexts_enabled = 446; - optional int32 ro_kernel_android_checkjni = 447; - optional string ro_logd_size = 448; - optional int32 ro_min_freq_0 = 449; - optional int32 ro_oem_unlock_supported = 450; - optional bool ro_opa_eligible_device = 451; - optional int32 ro_opengles_version = 452; - optional bool ro_persistent_properties_ready = 453; - - optional string ro_product_board = 454; - optional string ro_product_brand = 455; - optional string ro_product_cpu_abi = 456; - repeated string ro_product_cpu_abilist = 457; - repeated string ro_product_cpu_abilist32 = 458; - optional string ro_product_cpu_abilist64 = 459; - optional string ro_product_device = 460; - optional int32 ro_product_first_api_level = 461; - optional string ro_product_locale = 462; - optional string ro_product_manufacturer = 463; - optional string ro_product_model = 464; - optional string ro_product_name = 465; - optional string ro_product_vendor_brand = 466; - optional string ro_product_vendor_device = 467; - optional string ro_product_vendor_manufacturer = 468; - optional string ro_product_vendor_model = 469; - optional string ro_product_vendor_name = 470; - - optional int32 ro_property_service_version = 471; - optional string ro_qc_sdk_audio_fluencetype = 472; - optional int32 ro_qcom_adreno_qgl_ShaderStorageImageExtendedFormats = 473; - optional bool ro_qualcomm_bluetooth_ftp = 474; - optional bool ro_qualcomm_bluetooth_hfp = 475; - optional bool ro_qualcomm_bluetooth_hsp = 476; - optional bool ro_qualcomm_bluetooth_map = 477; - optional bool ro_qualcomm_bluetooth_nap = 478; - optional bool ro_qualcomm_bluetooth_opp = 479; - optional bool ro_qualcomm_bluetooth_pbap = 480; - - optional string ro_radio_log_loc = 481; - optional string ro_radio_log_prefix = 482; - optional int32 ro_revision = 483; - optional bool ro_ril_svdo = 484; - optional bool ro_ril_svlte1x = 485; - optional int64 ro_runtime_firstboot = 486; - optional int32 ro_secure = 487; - optional string ro_serialno = 488; - optional int32 ro_setupwizard_enterprise_mode = 489; - optional bool ro_setupwizard_rotation_locked = 490; - optional int32 ro_sf_lcd_density = 491; - optional bool ro_storage_manager_enabled = 492; - optional bool ro_telephony_call_ring_multiple = 493; - optional int32 ro_telephony_default_cdma_sub = 494; - optional int32 ro_telephony_default_network = 495; - optional bool ro_treble_enabled = 496; - optional string ro_url_legal = 497; - optional string ro_url_legal_android_privacy = 498; - optional string ro_vendor_build_date = 499; - optional int64 ro_vendor_build_date_utc = 500; - optional string ro_vendor_build_fingerprint = 501; - optional string ro_vendor_extension_library = 502; - optional bool ro_wallpapers_loc_request_suw = 503; - optional string ro_wifi_channels = 504; - optional string ro_zygote = 505; - - optional int32 sdm_debug_disable_rotator_split = 506; - - optional string selinux_restorecon_recursive = 507; - - optional string sendbug_preferred_domain = 508; - - optional string sensors_contexthub_lid_state = 509; - - optional int32 service_bootanim_exit = 510; - optional int32 service_sf_present_timestamp = 511; - - optional string setupwizard_theme = 512; - - optional string sys_boot_reason = 513; - optional int32 sys_boot_completed = 514; - optional int32 sys_ims_QMI_DAEMON_STATUS = 515; - optional bool sys_keymaster_loaded = 516; - optional bool sys_listeners_registered = 517; - optional int32 sys_logbootcomplete = 518; - optional int32 sys_oem_unlock_allowed = 519; - optional int32 sys_post_boot_parsed = 520; - optional int32 sys_qcom_devup = 521; - optional int32 sys_retaildemo_enabled = 522; - optional string sys_slpi_firmware_version = 523; - optional int32 sys_sysctl_extra_free_kbytes = 524; - optional int32 sys_sysctl_tcp_def_init_rwnd = 525; - optional bool sys_time_set = 526; - optional string sys_usb_config = 527; - optional int32 sys_usb_configfs = 528; - optional string sys_usb_controller = 529; - optional int32 sys_usb_ffs_max_read = 530; - optional int32 sys_usb_ffs_max_write = 531; - optional int32 sys_usb_ffs_mtp_ready = 532; - optional int32 sys_usb_ffs_ready = 533; - optional int32 sys_usb_mtp_device_type = 534; - optional int32 sys_usb_rps_mask = 535; - optional string sys_usb_state = 536; - optional bool sys_user_0_ce_available = 537; - optional int32 sys_wifitracing_started = 538; - - optional int32 telephony_lteOnCdmaDevice = 539; - - optional int32 tombstoned_max_tombstone_count = 540; - - optional int32 vidc_debug_perf_mode = 541; - - optional int32 vold_has_adoptable = 542; - optional int32 vold_has_quota = 543; - optional int32 vold_post_fs_data_done = 544; - - optional int32 wc_transport_clean_up = 545; - optional int32 wc_transport_hci_filter_status = 546; - optional string wc_transport_patch_dnld_inprog = 547; - optional int32 wc_transport_ref_count = 548; - optional int32 wc_transport_soc_initialized = 549; - optional bool wc_transport_start_root = 550; - optional int32 wc_transport_vnd_power = 551; - - optional string wifi_interface = 552; - optional string wlan_driver_status = 553; - - // Next Tag: 554 + optional Log log = 20; + + optional bool media_mediadrmservice_enable = 21; + optional bool media_recorder_show_manufacturer_and_model = 22; + + message Persist { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string config_calibration_fac = 1; + optional int32 dbg_volte_avail_ovr = 2; + optional int32 dbg_vt_avail_ovr = 3; + optional int32 dbg_wfc_avail_ovr = 4; + optional int32 radio_airplane_mode_on = 5; + optional string radio_multisim_config = 6; + optional int32 rcs_supported = 7; + optional bool sys_crash_rcu = 8; + optional string sys_dalvik_vm_lib_2 = 9; + optional float sys_sf_color_saturation = 10; + optional string sys_timezone = 11; + + // Next Tag: 12 + } + optional Persist persist = 23; + + message PmDexopt { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string ab_ota = 1; + optional string bg_dexopt = 2; + optional string boot = 3; + optional string first_boot = 4; + optional string install = 5; + + // Next Tag: 6 + } + optional PmDexopt pm_dexopt = 24; + + message Ro { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional int32 adb_secure = 1; + optional string arch = 2; + optional bool audio_ignore_effects = 3; + optional bool audio_monitorRotation = 4; + optional string baseband = 5; + optional string board_platform = 6; + + message Boot { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string avb_version = 1; + optional string baseband = 2; + optional string bootdevice = 3; + optional string bootloader = 4; + repeated string boottime = 5; + optional string console = 6; + optional int32 fake_battery = 7; + optional string hardware = 8; + optional string hardware_color = 9; + optional string hardware_revision = 10; + optional string hardware_sku = 11; + optional string keymaster = 12; + optional string mode = 13; + optional string revision = 14; + optional string slot_suffix = 15; + optional string vbmeta_avb_version = 16; + optional string vendor_overlay_theme = 17; + optional string verifiedbootstate = 18; + optional string veritymode = 19; + optional string wificountrycode = 20; + + // Next Tag: 21 + } + optional Boot boot = 7; + + message BootImage { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string build_date = 1; + optional int32 build_date_utc = 2; + optional string build_fingerprint = 3; + + // Next Tag: 4 + } + optional BootImage bootimage = 8; + + optional string bootloader = 9; + optional string bootmode = 10; + + message Build { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string date = 1; + optional int32 date_utc = 2; + optional string description = 3; + optional string display_id = 4; + optional string host = 5; + optional string id = 6; + optional string product = 7; + optional bool system_root_image = 8; + optional string tags = 9; + optional string type = 10; + optional string user = 11; + + message Version { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string version_base_os = 1; + optional string version_codename = 2; + optional string version_incremental = 3; + optional int32 version_preview_sdk = 4; + optional string version_release = 5; + optional int32 version_sdk = 6; + optional string version_security_patch = 7; + + // Next Tag: 8 + } + optional Version version = 12; + + // Next Tag: 13 + } + optional Build build = 11; + + optional int32 camera_notify_nfc = 12; + optional string carrier = 13; + optional bool com_android_dataroaming = 14; + optional bool com_android_prov_mobiledata = 15; + optional string com_google_clientidbase = 16; + + message Config { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string alarm_alert = 1; + optional int32 media_vol_steps = 2; + optional string notification_sound = 3; + optional string ringtone = 4; + optional int32 vc_call_vol_steps = 5; + + // Next Tag: 6 + } + optional Config config = 17; + + optional string control_privapp_permissions = 18; + optional int32 cp_system_other_odex = 19; + optional string crypto_scrypt_params = 20; + optional string crypto_state = 21; + optional string crypto_type = 22; + optional string dalvik_vm_native_bridge = 23; + optional int32 debuggable = 24; + optional string frp_pst = 25; + optional string gfx_driver_0 = 26; + + message Hardware { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string value = 1; // value of ro.hardware itself + + optional string activity_recognition = 2; + optional string audio = 3; + optional string audio_policy = 4; + optional string audio_a2dp = 5; + optional string audio_primary = 6; + optional string audio_usb = 7; + optional string bootctrl = 8; + optional string camera = 9; + optional string consumerir = 10; + optional string context_hub = 11; + optional string egl = 12; + optional string fingerprint = 13; + optional string flp = 14; + optional string gatekeeper = 15; + optional string gps = 16; + optional string gralloc = 17; + optional string hdmi_cec = 18; + optional string hwcomposer = 19; + optional string input = 20; + optional string keystore = 21; + optional string lights = 22; + optional string local_time = 23; + optional string memtrack = 24; + optional string nfc = 25; + optional string nfc_nci = 26; + optional string nfc_tag = 27; + optional string nvram = 28; + optional string power = 29; + optional string radio = 30; + optional string sensors = 31; + optional string sound_trigger = 32; + optional string thermal = 33; + optional string tv_input = 34; + optional string type = 35; + optional string vehicle = 36; + optional string vibrator = 37; + optional string virtual_device = 38; + optional string vulkan = 39; + + // Next Tag: 40 + } + optional Hardware hardware = 27; + + optional int32 kernel_qemu = 28; + optional int32 kernel_qemu_gles = 29; + optional int32 oem_unlock_supported = 30; + optional int32 opengles_version = 31; + + message Product { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string board = 1; + optional string brand = 2; + optional string cpu_abi = 3; + optional string cpu_abilist = 4; + optional string cpu_abilist32 = 5; + optional string cpu_abilist64 = 6; + optional string device = 7; + optional int32 first_api_level = 8; + optional string manufacturer = 9; + optional string model = 10; + optional string name = 11; + optional string vendor_brand = 12; + optional string vendor_device = 13; + optional string vendor_manufacturer = 14; + optional string vendor_model = 15; + optional string vendor_name = 16; + + // Next Tag: 17 + } + optional Product product = 32; + + optional int32 property_service_version = 33; + optional string retaildemo_video_path = 34; + optional string revision = 35; + optional int32 sf_lcd_density = 36; + optional bool storage_manager_enabled = 37; + optional bool telephony_call_ring_multiple = 38; + optional int32 telephony_default_cdma_sub = 39; + optional int32 telephony_default_network = 40; + optional string url_legal = 41; + optional string url_legal_android_privacy = 42; + optional string vendor_build_date = 43; + optional int32 vendor_build_date_utc = 44; + optional string vendor_build_fingerprint = 45; + optional string vndk_version = 46; + optional int32 vts_coverage = 47; + optional string zygote = 48; + + // Next Tag: 49 + } + optional Ro ro = 25; + + optional string sendbug_preferred_domain = 26; + optional int32 service_bootanim_exit = 27; + + message Sys { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional int32 boot_completed = 1; + optional int32 boot_from_charger_mode = 2; + optional int32 retaildemo_enabled = 3; + optional string shutdown_requested = 4; + + message Usb { + option (stream_proto.stream_msg).enable_fields_mapping = true; + + optional string config = 1; + optional int32 configfs = 2; + optional string controller = 3; + optional int32 ffs_max_read = 4; + optional int32 ffs_max_write = 5; + optional int32 ffs_mtp_ready = 6; + optional int32 ffs_ready = 7; + optional int32 mtp_device_type = 8; + optional string state = 9; + + // Next Tag: 10 + } + optional Usb usb = 5; + + // Next Tag: 6 + } + optional Sys sys = 28; + + optional int32 telephony_lteOnCdmaDevice = 29; + optional int32 tombstoned_max_tombstone_count = 30; + optional string vold_decrypt = 31; + optional int32 vold_post_fs_data_done = 32; + optional int32 vts_native_server_on = 33; + optional string wifi_direct_interface = 34; + optional string wifi_interface = 35; + + // Next Tag: 36 } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 0228edb03a83..915fe9d5ce41 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -200,6 +200,7 @@ message ScreenRotationAnimationProto { message WindowContainerProto { optional ConfigurationContainerProto configuration_container = 1; optional int32 orientation = 2; + optional bool visible = 3; } /* represents ConfigurationContainer */ diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index b4240cd408d8..440b26904cb2 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -79,8 +79,7 @@ <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"কোনো ভয়েস/জরুরী পরিষেবা নেই"</string> <string name="RestrictedStateContent" msgid="4278821484643362350">"সাময়িকভাবে মোবাইল নেটওয়ার্ক আপনার অবস্থানে এই পরিষেবা দিচ্ছে না"</string> <string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"নেটওয়ার্কের সিগন্যাল নেই"</string> - <!-- no translation found for NetworkPreferenceSwitchSummary (7056776609127756440) --> - <skip /> + <string name="NetworkPreferenceSwitchSummary" msgid="7056776609127756440">"আরও ভাল সিগন্যাল পাওয়ার জন্য সেটিংস > নেটওয়ার্ক এবং ইন্টারনেট > মোবাইল নেটওয়ার্ক > পছন্দের নেটওয়ার্কের ধরন বিকল্পে গিয়ে অন্য ধরনের নেটওয়ার্ক বেছে নিয়ে দেখুন।"</string> <string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"ওয়াই-ফাই কলিং সক্রিয় আছে"</string> <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"জরুরি কলের জন্য মোবাইল নেটওয়ার্ক থাকতে হবে।"</string> <string name="notification_channel_network_alert" msgid="4427736684338074967">"সতর্কতা"</string> @@ -213,6 +212,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"পাওয়ার বন্ধ করুন"</string> <string name="global_action_emergency" msgid="7112311161137421166">"জরুরী"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"ত্রুটির প্রতিবেদন"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"ত্রুটির অভিযোগ করুন"</string> <string name="bugreport_message" msgid="398447048750350456">"এটি একটি ই-মেল মেসেজ পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; অনুগ্রহ করে ধৈর্য রাখুন৷"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ইন্টারেক্টিভ প্রতিবেদন"</string> @@ -1052,12 +1053,9 @@ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"সিস্টেম সেটিংস> অ্যাপ্স> ডাউনলোড করাগুলি এ এটি পুনঃসক্ষম করুন৷"</string> <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>, বর্তমান প্রদর্শনের আকারের সেটিংস সমর্থন করে না এবং অপ্রত্যাশিত আচরণ করতে পারে৷"</string> <string name="unsupported_display_size_show" msgid="7969129195360353041">"সর্বদা দেখান"</string> - <!-- no translation found for unsupported_compile_sdk_message (5030433583092006591) --> - <skip /> - <!-- no translation found for unsupported_compile_sdk_show (2681877855260970231) --> - <skip /> - <!-- no translation found for unsupported_compile_sdk_check_update (3312723623323216101) --> - <skip /> + <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS এর একটি সামঞ্জস্যহীন ভার্সনের জন্য তৈরি করা হয়েছিল, তাই এখানে সেটি অস্বাভাবিক আচরণ করতে পারে। মনে হয় অ্যাপটির আপডেট করা ভার্সনও আছে।"</string> + <string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"সবসময় দেখুন"</string> + <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string> <string name="smv_application" msgid="3307209192155442829">"অ্যাপ্লিকেশানটি <xliff:g id="APPLICATION">%1$s</xliff:g> (প্রক্রিয়া <xliff:g id="PROCESS">%2$s</xliff:g>) তার স্ব-প্রয়োগ করা কঠোর মোড নীতি লঙ্ঘন করেছে৷"</string> <string name="smv_process" msgid="5120397012047462446">"প্রক্রিয়াটি <xliff:g id="PROCESS">%1$s</xliff:g> তার স্ব-প্রয়োগ করা কঠোর মোড নীতি লঙ্ঘন করেছে৷"</string> <string name="android_upgrading_title" msgid="1584192285441405746">"Android আপগ্রেড করা হচ্ছে..."</string> @@ -1123,12 +1121,10 @@ <string name="network_available_sign_in" msgid="1848877297365446605">"নেটওয়ার্কে সাইন-ইন করুন"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> <skip /> - <!-- no translation found for wifi_no_internet (8938267198124654938) --> - <skip /> + <string name="wifi_no_internet" msgid="8938267198124654938">"ওয়াই-ফাই এ ইন্টারনেট অ্যাক্সেস নেই"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"বিকল্পগুলির জন্য আলতো চাপুন"</string> <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string> - <!-- no translation found for network_switch_metered_detail (775163331794506615) --> - <skip /> + <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string> <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string> <string-array name="network_switch_type_name"> <item msgid="3979506840912951943">"মোবাইল ডেটা"</item> @@ -1505,6 +1501,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"শর্টকাটটি চালু থাকলে দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি বৈশিষ্ট্য চালু হবে।\n\n বর্তমান অ্যাকসেসিবিলিটি বৈশিষ্ট্য:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n আপনি এই বৈশিষ্ট্যটি সেটিংস > অ্যাকসেসিবিলিটিতে গিয়ে পরিবর্তন করতে পারবেন।"</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"শর্টকাট বন্ধ করুন"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"শর্টকাট ব্যবহার করুন"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে চালু করেছে"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে বন্ধ করেছে"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"অ্যাক্সেসযোগ্যতা বোতামের সাহায্যে যে বৈশিষ্ট্যটি নিয়ন্ত্রণ করতে চান, সেটি বেছে নিন:"</string> @@ -1644,7 +1644,7 @@ <string name="package_installed_device_owner" msgid="6875717669960212648">"আপনার প্রশাসক ইনস্টল করেছেন"</string> <string name="package_updated_device_owner" msgid="1847154566357862089">"আপনার প্রশাসক আপডেট করেছেন"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"আপনার প্রশাসক মুছে দিয়েছেন"</string> - <string name="battery_saver_description" msgid="1960431123816253034">"ব্যাটরির লাইফ উন্নত করতে, ব্যাটারি সাশ্রয়কারী আপনার ডিভাইসের কার্যসম্পাদনা হ্রাস করে এবং কম্পন, লোকেশন পরিষেবাগুলি এবং অধিকাংশ ব্যাকগ্রাউন্ড ডেটা সীমিত করে৷ ইমেল, মেসেজিং এবং অন্যান্য অ্যাপ্লিকেশনগুলিকে যেগুলি সিঙ্কের উপর নির্ভর করে সেগুলিকে আপনি না খোলা পর্যন্ত নাও আপডেট হতে পারে৷\n\nআপনার ডিভাইসটিকে যখন চার্জ করা হয় তখন ব্যাটারি সাশ্রয়কারী নিজে থেকে বন্ধ হয়ে যায়৷"</string> + <string name="battery_saver_description" msgid="5394663545060026162">"ব্যাটরির লাইফ উন্নত করে সাহায্য করতে, ব্যাটারি সেভার আপনার ডিভাইসের পারফরম্যান্স কমিয়ে দেয় এবং কম্পন, লোকেশন পরিষেবা এবং অধিকাংশ ব্যাকগ্রাউন্ড ডেটা সীমিত করে। ইমেল, মেসেজিং এবং সিঙ্কের উপর নির্ভর করে এমন অন্যান্য অ্যাপগুলিকে আপনি না খোলা পর্যন্ত নাও আপডেট হতে পারে।\n\nআপনার ডিভাইসটিকে যখন চার্জ করা হয় তখন ব্যাটারি সেভার নিজে থেকে বন্ধ হয়ে যায়।"</string> <string name="data_saver_description" msgid="6015391409098303235">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার পটভূমিতে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবিগুলিতে আলতো চাপ না দেওয়া পর্যন্ত সেগুলি প্রদর্শিত হবে না৷"</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"ডেটা সেভার চালু করবেন?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"চালু করুন"</string> @@ -1691,6 +1691,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"সপ্তাহান্তের রাত্রি"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"সপ্তাহান্ত"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"ইভেন্ট"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> দ্বারা নিঃশব্দ করা হয়েছে"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে৷ বিস্তারিত জানার জন্য প্রস্তুতকারকের সাথে যোগাযোগ করুন৷"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index ef30b9128861..c9c98eb8ee68 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -214,6 +214,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"Isključi telefon"</string> <string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Izvještaj o greškama"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Kreirajte izvještaj o greškama"</string> <string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupljati informacije o trenutnom stanju uređaja, koji će biti poslani kao e-poruka. Može malo potrajati dok se izvještaj o greškama ne kreira i bude spreman za slanje. Budite strpljivi."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivni izvještaj"</string> @@ -1026,9 +1028,9 @@ <string name="whichEditApplication" msgid="144727838241402655">"Uredi koristeći"</string> <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Uredi koristeći %1$s"</string> <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Uredi"</string> - <string name="whichSendApplication" msgid="6902512414057341668">"Podijeli koristeći"</string> - <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Podijeli koristeći %1$s"</string> - <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Podijeli"</string> + <string name="whichSendApplication" msgid="6902512414057341668">"Dijeli koristeći"</string> + <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Dijeli koristeći %1$s"</string> + <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Dijeli"</string> <string name="whichSendToApplication" msgid="8272422260066642057">"Pošalji koristeći"</string> <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Pošalji koristeći %1$s"</string> <string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Pošalji"</string> @@ -1353,7 +1355,7 @@ <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje SD kartice..."</string> <string name="share" msgid="1778686618230011964">"Podijelite"</string> <string name="find" msgid="4808270900322985960">"Pronađi"</string> - <string name="websearch" msgid="4337157977400211589">"Internet pretraga"</string> + <string name="websearch" msgid="4337157977400211589">"Internet pretraživanje"</string> <string name="find_next" msgid="5742124618942193978">"Nađi sljedeći"</string> <string name="find_previous" msgid="2196723669388360506">"Nađi prethodni"</string> <string name="gpsNotifTicker" msgid="5622683912616496172">"Korisnik <xliff:g id="NAME">%s</xliff:g> je poslao zahtjev za utvrđivanje lokacije"</string> @@ -1397,7 +1399,7 @@ <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Odaberite aplikaciju"</string> <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Aplikacija <xliff:g id="APPLICATION_NAME">%s</xliff:g> se ne može pokrenuti."</string> <string name="shareactionprovider_share_with" msgid="806688056141131819">"Podijeliti sa"</string> - <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Podijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> + <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Dijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="content_description_sliding_handle" msgid="415975056159262248">"Klizni regulator. Dodirnite & držite."</string> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Prevucite za otključavanje ekrana."</string> <string name="action_bar_home_description" msgid="5293600496601490216">"Vratite se na početnu stranicu"</string> @@ -1524,6 +1526,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kada je prečica uključena, pritiskom na oba dugmeta za podešavanje jačine zvuka u trajanju od 3 sekunde pokrenut će se funkcija za pristupačnost.\n\n Trenutna funkcija za pristupačnost je:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciju možete promijeniti ako odete u Postavke > Pristupačnost."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Isključi prečicu"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string> @@ -1719,6 +1725,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Radni dan uvečer"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Vikend"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Događaj"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"Ton isključila aplikacija <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Postoji problem u vašem uređaju. Za više informacija obratite se proizvođaču."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 7d9e5312174f..e1b4e0d2d6b9 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -212,6 +212,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string> <string name="global_action_emergency" msgid="7112311161137421166">"اضطراری"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string> <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمعآوری میکند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان میبرد؛ لطفاً شکیبا باشید."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"گزارش تعاملی"</string> @@ -1498,6 +1500,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"وقتی میانبر روشن است، اگر هر دو دکمه صدا را ۳ ثانیه فشار دهید یکی از قابلیتهای دسترسپذیری شروع میشود.\n\n قابلیت دسترسپذیری کنونی:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n میتوانید در «تنظیمات > دسترسپذیری»، قابلیت را تغییر دهید."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"خاموش کردن میانبر"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"استفاده از میانبر"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را روشن کرد"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را خاموش کرد"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"قابلیتی را انتخاب کنید که هنگام ضربه زدن روی دکمه «دسترسپذیری» استفاده میشود:"</string> @@ -1684,6 +1690,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"شب آخر هفته"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"آخر هفته"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"رویداد"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> آن را بیصدا کرد"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی دادههای کارخانه انجام نگیرد، بیثبات بماند."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"دستگاهتان یک مشکل داخلی دارد. برای جزئیات آن با سازندهتان تماس بگیرید."</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 19e7b346f7e4..c499ea726d07 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -212,6 +212,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"Matikan perangkat"</string> <string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string> <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string> @@ -1498,6 +1500,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas.\n\n Fitur aksesibilitas saat ini:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda dapat mengubah fitur di Setelan > Aksesibilitas."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Nonaktifkan Pintasan"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gunakan Pintasan"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan Aksesibilitas mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Aksesibilitas menonaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih fitur yang akan digunakan saat menge-tap tombol Aksesibilitas:"</string> @@ -1684,6 +1690,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Malam hari kerja"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Akhir pekan"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Acara"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"Dinonaktifkan oleh <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Ada masalah dengan perangkat. Hubungi produsen perangkat untuk informasi selengkapnya."</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 00bc9c2c8f1d..ce07805f2836 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -212,6 +212,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"კვების გამორთვა"</string> <string name="global_action_emergency" msgid="7112311161137421166">"საგანგებო სამსახურები"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"ხარვეზის შესახებ ანგარიში"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"შექმენით შეცდომის ანგარიში"</string> <string name="bugreport_message" msgid="398447048750350456">"იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ინტერაქტიული ანგარიში"</string> @@ -1051,7 +1053,7 @@ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ხელახალი გააქტიურება განყოფილებაში: სისტემის პარამეტრები > აპები > ჩამოტვირთულები."</string> <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ ეკრანის ამჟამინდელი პარამეტრები მხარდაუჭერელია და შეიძლება არასათანადოდ იმუშაოს."</string> <string name="unsupported_display_size_show" msgid="7969129195360353041">"ყოველთვის ჩვენება"</string> - <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> შექმნილია Android-ის ოპერატიული სისტემის არათავსებადი ვერსიისთვის და შეიძლება გაუმართავად იმუშაოს. შესაძლოა ხელმისაწვდომი იყოს აპის განახლებული ვერსია."</string> + <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> შექმნილია Android-ის ოპერაციული სისტემის არათავსებადი ვერსიისთვის და შეიძლება გაუმართავად იმუშაოს. შესაძლოა ხელმისაწვდომი იყოს აპის განახლებული ვერსია."</string> <string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"ყოველთვის ჩვენება"</string> <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"განახლების შემოწმება"</string> <string name="smv_application" msgid="3307209192155442829">"აპმა <xliff:g id="APPLICATION">%1$s</xliff:g> (პროცესი <xliff:g id="PROCESS">%2$s</xliff:g>) დაარღვია საკუთარი StrictMode დებულება."</string> @@ -1498,6 +1500,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"მალსახმობის ჩართვის შემთხვევაში, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება.\n\n მარტივი წვდომის ამჟამინდელი ფუნქციაა:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ამ ფუნქციის შეცვლა შეგიძლიათ აქ: პარამეტრები > მარტივი წვდომა."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"მალსახმობის გამორთვა"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"მალსახმობის გამოყენება"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"მარტივი წვდომის მალსახმობმა ჩართო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"მარტივი წვდომის მალსახმობმა გამორთო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string> @@ -1684,6 +1690,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"სამუშაო კვირის ღამე"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"შაბათ-კვირა"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"მოვლენა"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"დადუმებულია <xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ის მიერ"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"ფიქსირდება თქვენი მოწყობილობის შიდა პრობლემა. დეტალებისათვის, მიმართეთ თქვენს მწარმოებელს."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 8d022834b16d..3329961c4e26 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -47,8 +47,8 @@ <string name="needPuk2" msgid="4526033371987193070">"Introduza o PUK2 para desbloquear o cartão SIM."</string> <string name="enablePin" msgid="209412020907207950">"Ação sem êxito. Ative o bloqueio do SIM/RUIM."</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582"> - <item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item> <item quantity="other">Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar bloqueado.</item> + <item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item> </plurals> <string name="imei" msgid="2625429890869005782">"IMEI"</string> <string name="meid" msgid="4841221237681254195">"MEID"</string> @@ -162,8 +162,8 @@ <string name="low_memory" product="tv" msgid="516619861191025923">"O armazenamento da TV está cheio. Elimine alguns ficheiros para libertar espaço."</string> <string name="low_memory" product="default" msgid="3475999286680000541">"O armazenamento do telemóvel está cheio. Elimine alguns ficheiros para libertar espaço."</string> <plurals name="ssl_ca_cert_warning" formatted="false" msgid="5106721205300213569"> - <item quantity="one">Autoridade de certificação instalada</item> <item quantity="other">Autoridades de certificação instaladas</item> + <item quantity="one">Autoridade de certificação instalada</item> </plurals> <string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Por um terceiro desconhecido"</string> <string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"Pelo gestor do seu perfil de trabalho"</string> @@ -212,6 +212,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string> <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string> <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string> @@ -219,8 +221,8 @@ <string name="bugreport_option_full_title" msgid="6354382025840076439">"Relatório completo"</string> <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Utilize esta opção para uma interferência mínima do sistema quando o dispositivo não responder ou estiver demasiado lento, ou quando precisar de todas as secções de relatório. Não permite introduzir mais detalhes ou tirar capturas de ecrã adicionais."</string> <plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368"> - <item quantity="one">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_0">%d</xliff:g> segundo…</item> <item quantity="other">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item> + <item quantity="one">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_0">%d</xliff:g> segundo…</item> </plurals> <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som desativado"</string> @@ -864,8 +866,8 @@ <string name="oneMonthDurationPast" msgid="7396384508953779925">"Há 1 mês"</string> <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Há mais de 1 mês"</string> <plurals name="last_num_days" formatted="false" msgid="5104533550723932025"> - <item quantity="one">Último <xliff:g id="COUNT_0">%d</xliff:g> dia</item> <item quantity="other">Últimos <xliff:g id="COUNT_1">%d</xliff:g> dias</item> + <item quantity="one">Último <xliff:g id="COUNT_0">%d</xliff:g> dia</item> </plurals> <string name="last_month" msgid="3959346739979055432">"Último mês"</string> <string name="older" msgid="5211975022815554840">"Mais antiga"</string> @@ -886,68 +888,68 @@ <string name="years" msgid="6881577717993213522">"anos"</string> <string name="now_string_shortest" msgid="8912796667087856402">"agora"</string> <plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495"> - <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> m</item> <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> m</item> + <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> m</item> </plurals> <plurals name="duration_hours_shortest" formatted="false" msgid="3552182110578602356"> - <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item> <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item> + <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item> </plurals> <plurals name="duration_days_shortest" formatted="false" msgid="5213655532597081640"> - <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item> <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> d</item> + <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item> </plurals> <plurals name="duration_years_shortest" formatted="false" msgid="7848711145196397042"> - <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item> <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item> + <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item> </plurals> <plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> min</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> min</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> min</item> </plurals> <plurals name="duration_hours_shortest_future" formatted="false" msgid="2152452368397489370"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> h</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> h</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> h</item> </plurals> <plurals name="duration_days_shortest_future" formatted="false" msgid="8088331502820295701"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> d</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> d</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> d</item> </plurals> <plurals name="duration_years_shortest_future" formatted="false" msgid="2317006667145250301"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> a</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> a</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> a</item> </plurals> <plurals name="duration_minutes_relative" formatted="false" msgid="3178131706192980192"> - <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> minuto</item> <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> minutos</item> + <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> minuto</item> </plurals> <plurals name="duration_hours_relative" formatted="false" msgid="676894109982008411"> - <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> hora</item> <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> horas</item> + <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> hora</item> </plurals> <plurals name="duration_days_relative" formatted="false" msgid="2203515825765397130"> - <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> dia</item> <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> dias</item> + <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> dia</item> </plurals> <plurals name="duration_years_relative" formatted="false" msgid="4820062134188885734"> - <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> ano</item> <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> anos</item> + <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> ano</item> </plurals> <plurals name="duration_minutes_relative_future" formatted="false" msgid="4655043589817680966"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> minuto</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> minutos</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> minuto</item> </plurals> <plurals name="duration_hours_relative_future" formatted="false" msgid="8084579714205223891"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> hora</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> horas</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> hora</item> </plurals> <plurals name="duration_days_relative_future" formatted="false" msgid="333215369363433992"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> dia</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> dias</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> dia</item> </plurals> <plurals name="duration_years_relative_future" formatted="false" msgid="8644862986413104011"> - <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> ano</item> <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> anos</item> + <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> ano</item> </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão em fluxo contínuo neste aparelho."</string> @@ -1101,12 +1103,12 @@ <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"Sons de notificação"</string> <string name="ringtone_unknown" msgid="3914515995813061520">"Desconhecido"</string> <plurals name="wifi_available" formatted="false" msgid="7900333017752027322"> - <item quantity="one">Rede Wi-Fi disponível</item> <item quantity="other">Redes Wi-Fi disponíveis</item> + <item quantity="one">Rede Wi-Fi disponível</item> </plurals> <plurals name="wifi_available_detailed" formatted="false" msgid="1140699367193975606"> - <item quantity="one">Rede Wi-Fi aberta disponível</item> <item quantity="other">Redes Wi-Fi abertas disponíveis</item> + <item quantity="one">Rede Wi-Fi aberta disponível</item> </plurals> <string name="wifi_available_title" msgid="3817100557900599505">"Ligar à rede Wi-Fi aberta"</string> <string name="wifi_available_title_connecting" msgid="1557292688310330032">"A ligar à rede Wi-Fi aberta…"</string> @@ -1320,8 +1322,8 @@ <string name="no_matches" msgid="8129421908915840737">"Sem correspondências"</string> <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string> <plurals name="matches_found" formatted="false" msgid="1210884353962081884"> - <item quantity="one">1 correspondência</item> <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g></item> + <item quantity="one">1 correspondência</item> </plurals> <string name="action_mode_done" msgid="7217581640461922289">"Concluído"</string> <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"A apagar memória de armazenamento USB..."</string> @@ -1455,8 +1457,8 @@ <string name="kg_wrong_password" msgid="2333281762128113157">"Palavra-passe Incorreta"</string> <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Incorreto"</string> <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="8790651267324125694"> - <item quantity="one">Tente novamente dentro de 1 segundo.</item> <item quantity="other">Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item> + <item quantity="one">Tente novamente dentro de 1 segundo.</item> </plurals> <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe a sua sequência"</string> <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduzir PIN do cartão SIM"</string> @@ -1498,6 +1500,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade.\n\n Funcionalidade de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pode alterar a funcionalidade em Definições > Acessibilidade."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desativar atalho"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizar atalho"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O Atalho de acessibilidade ativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O Atalho de acessibilidade desativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string> @@ -1610,8 +1616,8 @@ <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"Os PINs não correspondem. Tente novamente."</string> <string name="restr_pin_error_too_short" msgid="8173982756265777792">"O PIN é demasiado pequeno. Deve ter, no mínimo, 4 dígitos."</string> <plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688"> - <item quantity="one">Tente novamente dentro de 1 segundo</item> <item quantity="other">Tente novamente dentro de <xliff:g id="COUNT">%d</xliff:g> segundos</item> + <item quantity="one">Tente novamente dentro de 1 segundo</item> </plurals> <string name="restr_pin_try_later" msgid="973144472490532377">"Tente novamente mais tarde"</string> <string name="immersive_cling_title" msgid="8394201622932303336">"Visualização de ecrã inteiro"</string> @@ -1642,36 +1648,36 @@ <string name="data_saver_enable_title" msgid="4674073932722787417">"Ativar a Poupança de dados?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Ativar"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848"> - <item quantity="one">Durante um minuto (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> <item quantity="other">Durante %1$d minutos (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> + <item quantity="one">Durante um minuto (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> </plurals> <plurals name="zen_mode_duration_minutes_summary_short" formatted="false" msgid="6830154222366042597"> - <item quantity="one">Durante 1 min (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> <item quantity="other">Durante %1$d min (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> + <item quantity="one">Durante 1 min (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> </plurals> <plurals name="zen_mode_duration_hours_summary" formatted="false" msgid="8152974162096743862"> - <item quantity="one">Durante uma hora (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> <item quantity="other">Durante %1$d horas (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> + <item quantity="one">Durante uma hora (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> </plurals> <plurals name="zen_mode_duration_hours_summary_short" formatted="false" msgid="4787552595253082371"> - <item quantity="one">Durante 1 h (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> <item quantity="other">Durante %1$d h (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> + <item quantity="one">Durante 1 h (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item> </plurals> <plurals name="zen_mode_duration_minutes" formatted="false" msgid="5127407202506485571"> - <item quantity="one">Durante um minuto</item> <item quantity="other">Durante %d minutos</item> + <item quantity="one">Durante um minuto</item> </plurals> <plurals name="zen_mode_duration_minutes_short" formatted="false" msgid="2199350154433426128"> - <item quantity="one">Durante 1 min</item> <item quantity="other">Durante %d min</item> + <item quantity="one">Durante 1 min</item> </plurals> <plurals name="zen_mode_duration_hours" formatted="false" msgid="3938821308277433854"> - <item quantity="one">Durante uma hora</item> <item quantity="other">Durante %d horas</item> + <item quantity="one">Durante uma hora</item> </plurals> <plurals name="zen_mode_duration_hours_short" formatted="false" msgid="6748277774662434217"> - <item quantity="one">Durante 1 h</item> <item quantity="other">Durante %d h</item> + <item quantity="one">Durante 1 h</item> </plurals> <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string> @@ -1684,6 +1690,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Dias da semana à noite"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fim de semana"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"Som desativado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Existe um problema interno no seu dispositivo. Contacte o fabricante para obter mais informações."</string> @@ -1706,8 +1714,8 @@ <string name="close_button_text" msgid="3937902162644062866">"Fechar"</string> <string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> <plurals name="selected_count" formatted="false" msgid="7187339492915744615"> - <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item> + <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item> </plurals> <string name="default_notification_channel_label" msgid="5929663562028088222">"Sem categoria"</string> <string name="importance_from_user" msgid="7318955817386549931">"Definiu a importância destas notificações."</string> @@ -1764,8 +1772,8 @@ <string name="autofill_error_cannot_autofill" msgid="7402758580060110371">"Não é possível preencher automaticamente o conteúdo"</string> <string name="autofill_picker_no_suggestions" msgid="3908514303773350735">"Sem sugestões do preenchimento automático"</string> <plurals name="autofill_picker_some_suggestions" formatted="false" msgid="5506565809835815274"> - <item quantity="one">Uma sugestão do preenchimento automático</item> <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestões do preenchimento automático</item> + <item quantity="one">Uma sugestão do preenchimento automático</item> </plurals> <string name="autofill_save_title" msgid="3345527308992082601">"Pretende guardar no <b><xliff:g id="LABEL">%1$s</xliff:g></b>?"</string> <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Pretende guardar <xliff:g id="TYPE">%1$s</xliff:g> no <b><xliff:g id="LABEL">%2$s</xliff:g></b>?"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 20b1bbe38b1d..ad6c6bce6800 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -216,6 +216,8 @@ <string name="global_action_power_off" msgid="4471879440839879722">"Вимкнути"</string> <string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string> + <!-- no translation found for global_action_logout (935179188218826050) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string> <string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтерактивний звіт"</string> @@ -1546,6 +1548,10 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Коли ярлик увімкнено, після натискання обох клавіш гучності й утримування їх протягом 3 секунд увімкнеться функція спеціальних можливостей.\n\n Поточна функція спеціальних можливостей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Цю функцію можна змінити в меню \"Налаштування\" > \"Спеціальні можливості\"."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Вимкнути ярлик"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Використовувати ярлик"</string> + <!-- no translation found for color_inversion_feature_name (4231186527799958644) --> + <skip /> + <!-- no translation found for color_correction_feature_name (6779391426096954933) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Виберіть функцію для кнопки спеціальних можливостей:"</string> @@ -1750,6 +1756,8 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Увечері в будні"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"На вихідних"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Подія"</string> + <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) --> + <skip /> <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає звук"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"На пристрої сталася внутрішня помилка. Зв’яжіться з виробником пристрою, щоб дізнатися більше."</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ef8f6afc5313..5ccaf5cf2f43 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4490,7 +4490,9 @@ <string name="stk_cc_ussd_to_dial">USSD request is modified to DIAL request.</string> <string name="stk_cc_ussd_to_ss">USSD request is modified to SS request.</string> <string name="stk_cc_ussd_to_ussd">USSD request is modified to new USSD request.</string> + <string name="stk_cc_ussd_to_dial_video">USSD request is modified to Video DIAL request.</string> <string name="stk_cc_ss_to_dial">SS request is modified to DIAL request.</string> + <string name="stk_cc_ss_to_dial_video">SS request is modified to Video DIAL request.</string> <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string> <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6da24bd2657b..f659360199ef 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1048,9 +1048,11 @@ <java-symbol type="string" name="stk_cc_ss_to_dial" /> <java-symbol type="string" name="stk_cc_ss_to_ss" /> <java-symbol type="string" name="stk_cc_ss_to_ussd" /> + <java-symbol type="string" name="stk_cc_ss_to_dial_video" /> <java-symbol type="string" name="stk_cc_ussd_to_dial" /> <java-symbol type="string" name="stk_cc_ussd_to_ss" /> <java-symbol type="string" name="stk_cc_ussd_to_ussd" /> + <java-symbol type="string" name="stk_cc_ussd_to_dial_video" /> <java-symbol type="string" name="safe_media_volume_warning" /> <java-symbol type="string" name="media_route_status_scanning" /> <java-symbol type="string" name="media_route_status_connecting" /> diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk index 39cf4a49ffbf..5ed93f317b3f 100644 --- a/core/tests/ConnectivityManagerTest/Android.mk +++ b/core/tests/ConnectivityManagerTest/Android.mk @@ -18,8 +18,8 @@ include $(CLEAR_VARS) # We only want this apk build for tests. LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk index 2af92dfe6104..ff9a0fe60f34 100644 --- a/core/tests/bandwidthtests/Android.mk +++ b/core/tests/bandwidthtests/Android.mk @@ -22,8 +22,12 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + org.apache.http.legacy \ + android.test.base \ + +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BandwidthTests include $(BUILD_PACKAGE) diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk index f53419ab53cd..744e5b04974e 100644 --- a/core/tests/bluetoothtests/Android.mk +++ b/core/tests/bluetoothtests/Android.mk @@ -8,8 +8,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BluetoothTests LOCAL_CERTIFICATE := platform diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index bec862ab7d2f..c14dc90dbacd 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -19,12 +19,17 @@ package android.app; import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.Intent; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Icon; import android.media.session.MediaSession; import android.os.Parcel; +import android.os.Parcelable; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -142,6 +147,36 @@ public class NotificationTest { } @Test + public void largeIconMultipleReferences_keptAfterParcelling() { + Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( + mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); + + Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build(); + assertSame(n.getLargeIcon(), originalIcon); + + Notification q = writeAndReadParcelable(n); + assertNotSame(q.getLargeIcon(), n.getLargeIcon()); + + assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap())); + assertSame(q.getLargeIcon(), q.extras.getParcelable(Notification.EXTRA_LARGE_ICON)); + } + + @Test + public void largeIconReferenceInExtrasOnly_keptAfterParcelling() { + Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( + mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); + + Notification n = new Notification.Builder(mContext).build(); + n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, originalIcon); + assertSame(n.getLargeIcon(), null); + + Notification q = writeAndReadParcelable(n); + assertSame(q.getLargeIcon(), null); + assertTrue(((Icon) q.extras.getParcelable(Notification.EXTRA_LARGE_ICON)).getBitmap() + .sameAs(originalIcon.getBitmap())); + } + + @Test public void allPendingIntents_recollectedAfterReusingBuilder() { PendingIntent intent1 = PendingIntent.getActivity(mContext, 0, new Intent("test1"), 0); PendingIntent intent2 = PendingIntent.getActivity(mContext, 0, new Intent("test2"), 0); @@ -187,4 +222,15 @@ public class NotificationTest { .setContentText("Text") .setStyle(new Notification.MediaStyle().setMediaSession(session.getSessionToken())); } + + /** + * Writes an arbitrary {@link Parcelable} into a {@link Parcel} using its writeToParcel + * method before reading it out again to check that it was sent properly. + */ + private static <T extends Parcelable> T writeAndReadParcelable(T original) { + Parcel p = Parcel.obtain(); + p.writeParcelable(original, /* flags */ 0); + p.setDataPosition(0); + return p.readParcelable(/* classLoader */ null); + } } diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java index ea2dd59a4313..b1f855246320 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java @@ -42,7 +42,7 @@ public class ClientTransactionTests { ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(callback1); transaction.addCallback(callback2); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java new file mode 100644 index 000000000000..aefc47e95512 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -0,0 +1,287 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.servertransaction; + +import static android.app.servertransaction.TestUtils.config; +import static android.app.servertransaction.TestUtils.referrerIntentList; +import static android.app.servertransaction.TestUtils.resultInfoList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class ObjectPoolTests { + + // 1. Check if two obtained objects from pool are not the same. + // 2. Check if the state of the object is cleared after recycling. + // 3. Check if the same object is obtained from pool after recycling. + + @Test + public void testRecycleActivityConfigurationChangeItem() { + ActivityConfigurationChangeItem emptyItem = ActivityConfigurationChangeItem.obtain(null); + ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ActivityConfigurationChangeItem item2 = ActivityConfigurationChangeItem.obtain(config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleActivityResultItem() { + ActivityResultItem emptyItem = ActivityResultItem.obtain(null); + ActivityResultItem item = ActivityResultItem.obtain(resultInfoList()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ActivityResultItem item2 = ActivityResultItem.obtain(resultInfoList()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleConfigurationChangeItem() { + ConfigurationChangeItem emptyItem = ConfigurationChangeItem.obtain(null); + ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ConfigurationChangeItem item2 = ConfigurationChangeItem.obtain(config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleDestroyActivityItem() { + DestroyActivityItem emptyItem = DestroyActivityItem.obtain(false, 0); + DestroyActivityItem item = DestroyActivityItem.obtain(true, 117); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + DestroyActivityItem item2 = DestroyActivityItem.obtain(true, 14); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleLaunchActivityItem() { + Intent intent = new Intent("action"); + int ident = 57; + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.flags = 42; + activityInfo.maxAspectRatio = 2.4f; + activityInfo.launchToken = "token"; + activityInfo.applicationInfo = new ApplicationInfo(); + activityInfo.packageName = "packageName"; + activityInfo.name = "name"; + Configuration overrideConfig = new Configuration(); + overrideConfig.assetsSeq = 5; + CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + String referrer = "referrer"; + int procState = 4; + Bundle bundle = new Bundle(); + bundle.putString("key", "value"); + PersistableBundle persistableBundle = new PersistableBundle(); + persistableBundle.putInt("k", 4); + + LaunchActivityItem emptyItem = LaunchActivityItem.obtain(null, 0, null, null, null, null, + null, null, 0, null, null, null, null, false, null); + LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, + config(), overrideConfig, compat, referrer, null /* voiceInteractor */, + procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), + true /* isForward */, null /* profilerInfo */); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + LaunchActivityItem item2 = LaunchActivityItem.obtain(intent, ident, activityInfo, + config(), overrideConfig, compat, referrer, null /* voiceInteractor */, + procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), + true /* isForward */, null /* profilerInfo */); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleMoveToDisplayItem() { + MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, null); + MoveToDisplayItem item = MoveToDisplayItem.obtain(4, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + MoveToDisplayItem item2 = MoveToDisplayItem.obtain(3, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleMultiWindowModeChangeItem() { + MultiWindowModeChangeItem emptyItem = MultiWindowModeChangeItem.obtain(false, null); + MultiWindowModeChangeItem item = MultiWindowModeChangeItem.obtain(true, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + MultiWindowModeChangeItem item2 = MultiWindowModeChangeItem.obtain(true, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleNewIntentItem() { + NewIntentItem emptyItem = NewIntentItem.obtain(null, false); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList(), true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecyclePauseActivityItemItem() { + PauseActivityItem emptyItem = PauseActivityItem.obtain(false, false, 0, false); + PauseActivityItem item = PauseActivityItem.obtain(true, true, 5, true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + PauseActivityItem item2 = PauseActivityItem.obtain(true, false, 5, true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecyclePipModeChangeItem() { + PipModeChangeItem emptyItem = PipModeChangeItem.obtain(false, null); + PipModeChangeItem item = PipModeChangeItem.obtain(true, config()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + PipModeChangeItem item2 = PipModeChangeItem.obtain(true, config()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleResumeActivityItem() { + ResumeActivityItem emptyItem = ResumeActivityItem.obtain(false); + ResumeActivityItem item = ResumeActivityItem.obtain(3, true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ResumeActivityItem item2 = ResumeActivityItem.obtain(2, true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleStopItem() { + StopActivityItem emptyItem = StopActivityItem.obtain(false, 0); + StopActivityItem item = StopActivityItem.obtain(true, 4); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + StopActivityItem item2 = StopActivityItem.obtain(true, 3); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleWindowVisibleItem() { + WindowVisibilityItem emptyItem = WindowVisibilityItem.obtain(false); + WindowVisibilityItem item = WindowVisibilityItem.obtain(true); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + WindowVisibilityItem item2 = WindowVisibilityItem.obtain(true); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } + + @Test + public void testRecycleClientTransaction() { + ClientTransaction emptyItem = ClientTransaction.obtain(null, null); + ClientTransaction item = ClientTransaction.obtain(null, new Binder()); + assertNotSame(item, emptyItem); + assertFalse(item.equals(emptyItem)); + + item.recycle(); + assertEquals(item, emptyItem); + + ClientTransaction item2 = ClientTransaction.obtain(null, new Binder()); + assertSame(item, item2); + assertFalse(item2.equals(emptyItem)); + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java new file mode 100644 index 000000000000..e92351609256 --- /dev/null +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.servertransaction; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + +import android.app.ResultInfo; +import android.content.Intent; +import android.content.res.Configuration; + +import com.android.internal.content.ReferrerIntent; + +import java.util.ArrayList; +import java.util.List; + +class TestUtils { + + static Configuration config() { + Configuration config = new Configuration(); + config.densityDpi = 10; + config.fontScale = 0.3f; + config.screenHeightDp = 15; + config.orientation = ORIENTATION_LANDSCAPE; + return config; + } + + static List<ResultInfo> resultInfoList() { + String resultWho1 = "resultWho1"; + int requestCode1 = 7; + int resultCode1 = 4; + Intent data1 = new Intent("action1"); + ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1); + + String resultWho2 = "resultWho2"; + int requestCode2 = 8; + int resultCode2 = 6; + Intent data2 = new Intent("action2"); + ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2); + + List<ResultInfo> resultInfoList = new ArrayList<>(); + resultInfoList.add(resultInfo1); + resultInfoList.add(resultInfo2); + + return resultInfoList; + } + + static List<ReferrerIntent> referrerIntentList() { + Intent intent1 = new Intent("action1"); + ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1"); + + Intent intent2 = new Intent("action2"); + ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2"); + + List<ReferrerIntent> referrerIntents = new ArrayList<>(); + referrerIntents.add(referrerIntent1); + referrerIntents.add(referrerIntent2); + + return referrerIntents; + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index d2830047505b..e575650393f0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -51,6 +52,7 @@ import java.util.ArrayList; /** Test {@link TransactionExecutor} logic. */ @RunWith(AndroidJUnit4.class) @SmallTest +@Presubmit public class TransactionExecutorTests { private TransactionExecutor mExecutor; @@ -171,7 +173,7 @@ public class TransactionExecutorTests { ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(callback1); transaction.addCallback(callback2); @@ -188,10 +190,10 @@ public class TransactionExecutorTests { @Test public void testRequiredStateResolution() { - ActivityResultItem activityResultItem = new ActivityResultItem(new ArrayList<>()); + ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>()); IBinder token = mock(IBinder.class); - ClientTransaction transaction = new ClientTransaction(null /* client */, + ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token /* activityToken */); transaction.addCallback(activityResultItem); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f6c656d5f17c..4b1f2dab61b9 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -16,7 +16,9 @@ package android.app.servertransaction; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.app.servertransaction.TestUtils.config; +import static android.app.servertransaction.TestUtils.referrerIntentList; +import static android.app.servertransaction.TestUtils.resultInfoList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -57,7 +59,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -77,7 +78,7 @@ public class TransactionParcelTests { @Test public void testConfigurationChange() { // Write to parcel - ConfigurationChangeItem item = new ConfigurationChangeItem(config()); + ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -90,7 +91,7 @@ public class TransactionParcelTests { @Test public void testActivityConfigChange() { // Write to parcel - ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(config()); + ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -104,7 +105,7 @@ public class TransactionParcelTests { @Test public void testMoveToDisplay() { // Write to parcel - MoveToDisplayItem item = new MoveToDisplayItem(4 /* targetDisplayId */, config()); + MoveToDisplayItem item = MoveToDisplayItem.obtain(4 /* targetDisplayId */, config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -117,7 +118,7 @@ public class TransactionParcelTests { @Test public void testNewIntent() { // Write to parcel - NewIntentItem item = new NewIntentItem(referrerIntentList(), true /* pause */); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), true /* pause */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -130,7 +131,7 @@ public class TransactionParcelTests { @Test public void testActivityResult() { // Write to parcel - ActivityResultItem item = new ActivityResultItem(resultInfoList()); + ActivityResultItem item = ActivityResultItem.obtain(resultInfoList()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -143,7 +144,7 @@ public class TransactionParcelTests { @Test public void testPipModeChange() { // Write to parcel - PipModeChangeItem item = new PipModeChangeItem(true /* isInPipMode */, config()); + PipModeChangeItem item = PipModeChangeItem.obtain(true /* isInPipMode */, config()); writeAndPrepareForReading(item); // Read from parcel and assert @@ -156,7 +157,7 @@ public class TransactionParcelTests { @Test public void testMultiWindowModeChange() { // Write to parcel - MultiWindowModeChangeItem item = new MultiWindowModeChangeItem( + MultiWindowModeChangeItem item = MultiWindowModeChangeItem.obtain( true /* isInMultiWindowMode */, config()); writeAndPrepareForReading(item); @@ -171,7 +172,7 @@ public class TransactionParcelTests { @Test public void testWindowVisibilityChange() { // Write to parcel - WindowVisibilityItem item = new WindowVisibilityItem(true /* showWindow */); + WindowVisibilityItem item = WindowVisibilityItem.obtain(true /* showWindow */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -181,7 +182,7 @@ public class TransactionParcelTests { assertTrue(item.equals(result)); // Check different value - item = new WindowVisibilityItem(false); + item = WindowVisibilityItem.obtain(false); mParcel = Parcel.obtain(); writeAndPrepareForReading(item); @@ -195,7 +196,7 @@ public class TransactionParcelTests { @Test public void testDestroy() { - DestroyActivityItem item = new DestroyActivityItem(true /* finished */, + DestroyActivityItem item = DestroyActivityItem.obtain(true /* finished */, 135 /* configChanges */); writeAndPrepareForReading(item); @@ -228,7 +229,7 @@ public class TransactionParcelTests { PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putInt("k", 4); - LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo, + LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo, config(), overrideConfig, compat, referrer, null /* voiceInteractor */, procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(), true /* isForward */, null /* profilerInfo */); @@ -244,8 +245,8 @@ public class TransactionParcelTests { @Test public void testPause() { // Write to parcel - PauseActivityItem item = new PauseActivityItem(true /* finished */, true /* userLeaving */, - 135 /* configChanges */, true /* dontReport */); + PauseActivityItem item = PauseActivityItem.obtain(true /* finished */, + true /* userLeaving */, 135 /* configChanges */, true /* dontReport */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -258,7 +259,8 @@ public class TransactionParcelTests { @Test public void testResume() { // Write to parcel - ResumeActivityItem item = new ResumeActivityItem(27 /* procState */, true /* isForward */); + ResumeActivityItem item = ResumeActivityItem.obtain(27 /* procState */, + true /* isForward */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -271,7 +273,8 @@ public class TransactionParcelTests { @Test public void testStop() { // Write to parcel - StopActivityItem item = new StopActivityItem(true /* showWindow */, 14 /* configChanges */); + StopActivityItem item = StopActivityItem.obtain(true /* showWindow */, + 14 /* configChanges */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -284,16 +287,17 @@ public class TransactionParcelTests { @Test public void testClientTransaction() { // Write to parcel - WindowVisibilityItem callback1 = new WindowVisibilityItem(true); - ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( + config()); - StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, 78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.addCallback(callback1); transaction.addCallback(callback2); transaction.setLifecycleStateRequest(lifecycleRequest); @@ -310,13 +314,14 @@ public class TransactionParcelTests { @Test public void testClientTransactionCallbacksOnly() { // Write to parcel - WindowVisibilityItem callback1 = new WindowVisibilityItem(true); - ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config()); + WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( + config()); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.addCallback(callback1); transaction.addCallback(callback2); @@ -332,13 +337,13 @@ public class TransactionParcelTests { @Test public void testClientTransactionLifecycleOnly() { // Write to parcel - StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */, + StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, 78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); - ClientTransaction transaction = new ClientTransaction(appThread, activityToken); + ClientTransaction transaction = ClientTransaction.obtain(appThread, activityToken); transaction.setLifecycleStateRequest(lifecycleRequest); writeAndPrepareForReading(transaction); @@ -350,49 +355,6 @@ public class TransactionParcelTests { assertTrue(transaction.equals(result)); } - private static List<ResultInfo> resultInfoList() { - String resultWho1 = "resultWho1"; - int requestCode1 = 7; - int resultCode1 = 4; - Intent data1 = new Intent("action1"); - ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1); - - String resultWho2 = "resultWho2"; - int requestCode2 = 8; - int resultCode2 = 6; - Intent data2 = new Intent("action2"); - ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2); - - List<ResultInfo> resultInfoList = new ArrayList<>(); - resultInfoList.add(resultInfo1); - resultInfoList.add(resultInfo2); - - return resultInfoList; - } - - private static List<ReferrerIntent> referrerIntentList() { - Intent intent1 = new Intent("action1"); - ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1"); - - Intent intent2 = new Intent("action2"); - ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2"); - - List<ReferrerIntent> referrerIntents = new ArrayList<>(); - referrerIntents.add(referrerIntent1); - referrerIntents.add(referrerIntent2); - - return referrerIntents; - } - - private static Configuration config() { - Configuration config = new Configuration(); - config.densityDpi = 10; - config.fontScale = 0.3f; - config.screenHeightDp = 15; - config.orientation = ORIENTATION_LANDSCAPE; - return config; - } - /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */ private void writeAndPrepareForReading(Parcelable parcelable) { parcelable.writeToParcel(mParcel, 0 /* flags */); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 77c6c3e1c193..0982a4b58c99 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -194,6 +194,7 @@ public class SettingsBackupTest { Settings.Global.DROPBOX_RESERVE_PERCENT, Settings.Global.DROPBOX_TAG_PREFIX, Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, + Settings.Global.EMULATE_DISPLAY_CUTOUT, Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION, Settings.Global.ENABLE_CELLULAR_ON_BOOT, diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java new file mode 100644 index 000000000000..d85d3b898c3a --- /dev/null +++ b/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java @@ -0,0 +1,154 @@ +/* + * 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.security.recoverablekeystore; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.security.keystore.AndroidKeyStoreSecretKey; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyProtection; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.security.KeyStore; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RecoverableKeyGeneratorTest { + private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; + private static final String KEY_ALGORITHM = "AES"; + private static final String TEST_ALIAS = "karlin"; + private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey"; + + @Mock RecoverableKeyStorage mRecoverableKeyStorage; + + @Captor ArgumentCaptor<KeyProtection> mKeyProtectionArgumentCaptor; + + private AndroidKeyStoreSecretKey mPlatformKey; + private SecretKey mKeyHandle; + private RecoverableKeyGenerator mRecoverableKeyGenerator; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mPlatformKey = generateAndroidKeyStoreKey(); + mKeyHandle = generateKey(); + mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance( + mPlatformKey, mRecoverableKeyStorage); + + when(mRecoverableKeyStorage.loadFromAndroidKeyStore(any())).thenReturn(mKeyHandle); + } + + @After + public void tearDown() throws Exception { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + keyStore.load(/*param=*/ null); + keyStore.deleteEntry(WRAPPING_KEY_ALIAS); + } + + @Test + public void generateAndStoreKey_setsKeyInKeyStore() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + verify(mRecoverableKeyStorage, times(1)) + .importIntoAndroidKeyStore(eq(TEST_ALIAS), any(), any()); + } + + @Test + public void generateAndStoreKey_storesKeyEnabledForEncryptDecrypt() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + KeyProtection keyProtection = getKeyProtectionUsed(); + assertEquals(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT, + keyProtection.getPurposes()); + } + + @Test + public void generateAndStoreKey_storesKeyEnabledForGCM() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + KeyProtection keyProtection = getKeyProtectionUsed(); + assertArrayEquals(new String[] { KeyProperties.BLOCK_MODE_GCM }, + keyProtection.getBlockModes()); + } + + @Test + public void generateAndStoreKey_storesKeyEnabledForNoPadding() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + KeyProtection keyProtection = getKeyProtectionUsed(); + assertArrayEquals(new String[] { KeyProperties.ENCRYPTION_PADDING_NONE }, + keyProtection.getEncryptionPaddings()); + } + + @Test + public void generateAndStoreKey_storesWrappedKey() throws Exception { + mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + verify(mRecoverableKeyStorage, times(1)).persistToDisk(eq(TEST_ALIAS), any()); + } + + @Test + public void generateAndStoreKey_returnsKeyHandle() throws Exception { + SecretKey secretKey = mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS); + + assertEquals(mKeyHandle, secretKey); + } + + private KeyProtection getKeyProtectionUsed() throws Exception { + verify(mRecoverableKeyStorage, times(1)).importIntoAndroidKeyStore( + any(), any(), mKeyProtectionArgumentCaptor.capture()); + return mKeyProtectionArgumentCaptor.getValue(); + } + + private SecretKey generateKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(/*keySize=*/ 256); + return keyGenerator.generateKey(); + } + + private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KEY_ALGORITHM, + ANDROID_KEY_STORE_PROVIDER); + keyGenerator.init(new KeyGenParameterSpec.Builder( + WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + return (AndroidKeyStoreSecretKey) keyGenerator.generateKey(); + } +} diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java new file mode 100644 index 000000000000..233c8211dcb8 --- /dev/null +++ b/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java @@ -0,0 +1,90 @@ +/* + * 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.security.recoverablekeystore; + +import static org.junit.Assert.assertEquals; + +import android.security.keystore.AndroidKeyStoreSecretKey; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.security.KeyStore; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WrappedKeyTest { + private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; + private static final String KEY_ALGORITHM = "AES"; + private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding"; + private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias"; + private static final int GCM_TAG_LENGTH_BYTES = 16; + private static final int BITS_PER_BYTE = 8; + private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE; + + @After + public void tearDown() throws Exception { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + keyStore.load(/*param=*/ null); + keyStore.deleteEntry(WRAPPING_KEY_ALIAS); + } + + @Test + public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception { + SecretKey wrappingKey = generateAndroidKeyStoreKey(); + SecretKey rawKey = generateKey(); + + WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey); + + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init( + Cipher.UNWRAP_MODE, + wrappingKey, + new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce())); + SecretKey unwrappedKey = (SecretKey) cipher.unwrap( + wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY); + assertEquals(rawKey, unwrappedKey); + } + + private SecretKey generateKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); + keyGenerator.init(/*keySize=*/ 256); + return keyGenerator.generateKey(); + } + + private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KEY_ALGORITHM, + ANDROID_KEY_STORE_PROVIDER); + keyGenerator.init(new KeyGenParameterSpec.Builder( + WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + return (AndroidKeyStoreSecretKey) keyGenerator.generateKey(); + } +} diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java index 14705add258f..1b936c7b1c1d 100644 --- a/core/tests/coretests/src/android/text/BidiFormatterTest.java +++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java @@ -18,12 +18,14 @@ package android.text; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class BidiFormatterTest { diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java index 08e1b5bdcd6d..c69f4f58b44e 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java @@ -21,6 +21,7 @@ import static android.text.Layout.Alignment.ALIGN_NORMAL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -33,6 +34,7 @@ import org.junit.runner.RunWith; * Requires disabling access checks in the vm since this calls package-private APIs. * */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class DynamicLayoutBlocksTest { diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java index 639cefbcf26b..aa9aed8e2fcc 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Canvas; import android.graphics.Paint; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.ReplacementSpan; @@ -33,6 +34,7 @@ import android.text.style.ReplacementSpan; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class DynamicLayoutTest { diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 9cf8e1ee363d..2448b0b58f9c 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -29,6 +29,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; @@ -42,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class LayoutTest { diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java index 9df0f8911893..d9dc6fc44495 100644 --- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java +++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java @@ -18,6 +18,7 @@ package android.text; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -27,6 +28,7 @@ import org.junit.runner.RunWith; /** * PackedIntVectorTest tests the features of android.util.PackedIntVector. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class PackedIntVectorTest { diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java index 5ed6250fbc60..307ac62f9d07 100644 --- a/core/tests/coretests/src/android/text/SpannableTest.java +++ b/core/tests/coretests/src/android/text/SpannableTest.java @@ -16,14 +16,16 @@ package android.text; -import android.support.test.filters.MediumTest; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.test.MoreAsserts; import org.junit.Test; import org.junit.runner.RunWith; -@MediumTest +@Presubmit +@SmallTest @RunWith(AndroidJUnit4.class) public abstract class SpannableTest { diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java index 60cddb08e0d7..573757165101 100644 --- a/core/tests/coretests/src/android/text/SpannedTest.java +++ b/core/tests/coretests/src/android/text/SpannedTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import android.graphics.Typeface; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.CharacterStyle; @@ -33,6 +34,7 @@ import org.junit.runner.RunWith; /** * SpannedTest tests some features of Spanned */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class SpannedTest { diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java index 8092203033c2..bf0d42788b72 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java @@ -18,6 +18,7 @@ package android.text; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.Log; @@ -28,6 +29,7 @@ import org.junit.runner.RunWith; /** * Quick check of native bidi implementation. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class StaticLayoutBidiTest { diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java index e0b47765a625..5cf54268e004 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java @@ -18,16 +18,19 @@ package android.text; import static org.junit.Assert.fail; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Directions; import android.text.StaticLayoutTest.LayoutBuilder; -import java.util.Arrays; -import java.util.Formatter; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; +import java.util.Formatter; + +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class StaticLayoutDirectionsTest { diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java index 078ed76844ac..f4514b56ec8d 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Canvas; import android.graphics.Paint.FontMetricsInt; import android.os.LocaleList; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; @@ -43,6 +44,7 @@ import java.util.Locale; /** * Tests StaticLayout vertical metrics behavior. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class StaticLayoutTest { diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java index 896318987d94..24020ceb0cd4 100644 --- a/core/tests/coretests/src/android/text/TextLayoutTest.java +++ b/core/tests/coretests/src/android/text/TextLayoutTest.java @@ -16,6 +16,7 @@ package android.text; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -23,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TextLayoutTest { diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index f8d688498b5f..d9c09c8b03bf 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -19,6 +19,7 @@ package android.text; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.filters.Suppress; import android.support.test.runner.AndroidJUnit4; @@ -26,6 +27,7 @@ import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TextLineTest { diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 79619d9a49db..e70b8412ac65 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.LargeTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -46,6 +47,7 @@ import java.util.Locale; /** * TextUtilsTest tests {@link TextUtils}. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TextUtilsTest { diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java index ec2c96cdeeff..fbd2412a35cc 100644 --- a/core/tests/coretests/src/android/text/VariationParserTest.java +++ b/core/tests/coretests/src/android/text/VariationParserTest.java @@ -20,12 +20,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.graphics.fonts.FontVariationAxis; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class VariationParserTest { diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java index 0f08d183c14b..9000ed07d606 100644 --- a/core/tests/coretests/src/android/text/format/DateFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -19,6 +19,7 @@ package android.text.format; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -27,6 +28,7 @@ import org.junit.runner.RunWith; import java.util.Locale; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class DateFormatTest { diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java index f21c9297dde9..f8e3b4dfec56 100644 --- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java +++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import android.content.res.Configuration; import android.content.res.Resources; import android.os.LocaleList; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -35,6 +36,7 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class DateUtilsTest { diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java index 8983d15f0d19..d563f2e2383b 100644 --- a/core/tests/coretests/src/android/text/format/TimeTest.java +++ b/core/tests/coretests/src/android/text/format/TimeTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.filters.Suppress; import android.support.test.runner.AndroidJUnit4; @@ -29,6 +30,7 @@ import android.util.TimeFormatException; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TimeTest { diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java index 8b5cc60ddaea..df4609f5a728 100644 --- a/core/tests/coretests/src/android/text/method/BackspaceTest.java +++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java @@ -16,6 +16,7 @@ package android.text.method; +import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -24,6 +25,7 @@ import android.util.KeyUtils; import android.view.KeyEvent; import android.widget.EditText; import android.widget.TextView.BufferType; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +36,8 @@ import org.junit.runner.RunWith; * Only contains edge cases. For normal cases, see {@see android.text.method.cts.BackspaceTest}. * TODO: introduce test cases for surrogate pairs and replacement span. */ + +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class BackspaceTest { diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java index c3a5f800daf9..15dbbc8de150 100644 --- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java +++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java @@ -16,6 +16,7 @@ package android.text.method; +import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -35,6 +36,7 @@ import org.junit.runner.RunWith; * Only contains edge cases. For normal cases, see {@see android.text.method.cts.ForwardDeleteTest}. * TODO: introduce test cases for surrogate pairs and replacement span. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class ForwardDeleteTest { diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java index d1f4f7e84c25..684586387255 100644 --- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java +++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -31,6 +32,7 @@ import java.text.BreakIterator; import java.util.Locale; // TODO(Bug: 24062099): Add more tests for non-ascii text. +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class WordIteratorTest { diff --git a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java index e5c4b821c1cf..3d7e5bab15f3 100644 --- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java +++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java @@ -19,6 +19,7 @@ package android.text.style; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.SpannableString; @@ -29,7 +30,7 @@ import android.text.TextPaint; import org.junit.Test; import org.junit.runner.RunWith; - +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class UnderlineSpanTest { diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java index b5140e356175..e9eb978805bf 100644 --- a/core/tests/coretests/src/android/util/ScrollViewScenario.java +++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java @@ -269,6 +269,6 @@ public abstract class ScrollViewScenario extends Activity { mScrollView.setSmoothScrollingEnabled(false); setContentView(mScrollView); - mScrollView.restoreDefaultFocus(); + mScrollView.post(() -> mScrollView.restoreDefaultFocus()); } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java index 8a5fc2d11195..c1b2309ae64a 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.support.test.runner.AndroidJUnit4; +import libcore.util.EmptyArray; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,7 +74,7 @@ public class AccessibilityInteractionClientTest { List<AccessibilityNodeInfo> mInfosToReturn; @Override - public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, Bundle arguments) { @@ -82,7 +83,7 @@ public class AccessibilityInteractionClientTest { } catch (RemoteException e) { throw new RuntimeException(e); } - return true; + return EmptyArray.STRING; } } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index d3bbee71d564..44b1f088111a 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -31,35 +31,35 @@ import java.util.List; public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub { public void setServiceInfo(AccessibilityServiceInfo info) {} - public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, Bundle arguments) { - return false; + return null; } - public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId) { - return false; + return null; } - public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId) { - return false; + return null; } - public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, + public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId) { - return false; + return null; } - public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, + public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId) { - return false; + return null; } public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java index e0664d940f4e..0fd0136a85ed 100644 --- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java @@ -16,21 +16,33 @@ package android.view.inputmethod; +import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; +import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; +import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import android.graphics.Matrix; import android.graphics.RectF; import android.os.Parcel; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.view.inputmethod.CursorAnchorInfo.Builder; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Objects; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; -import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL; +@SmallTest +@RunWith(AndroidJUnit4.class) +public class CursorAnchorInfoTest { + private static final float EPSILON = 0.0000001f; -public class CursorAnchorInfoTest extends InstrumentationTestCase { private static final RectF[] MANY_BOUNDS = new RectF[] { new RectF(101.0f, 201.0f, 301.0f, 401.0f), new RectF(102.0f, 202.0f, 302.0f, 402.0f), @@ -74,7 +86,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL, }; - @SmallTest + @Test public void testBuilder() throws Exception { final int SELECTION_START = 30; final int SELECTION_END = 40; @@ -109,10 +121,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart()); assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText())); assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal()); - assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop()); - assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline()); - assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON); + assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON); + assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON); + assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON); assertEquals(TRANSFORM_MATRIX, info.getMatrix()); for (int i = 0; i < MANY_BOUNDS.length; i++) { final RectF expectedBounds = MANY_BOUNDS[i]; @@ -134,10 +146,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart()); assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText())); assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal()); - assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop()); - assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline()); - assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON); + assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON); + assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON); + assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON); assertEquals(TRANSFORM_MATRIX, info2.getMatrix()); for (int i = 0; i < MANY_BOUNDS.length; i++) { final RectF expectedBounds = MANY_BOUNDS[i]; @@ -161,10 +173,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart()); assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText())); assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags()); - assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal()); - assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop()); - assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline()); - assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom()); + assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON); + assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON); + assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON); + assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON); assertEquals(TRANSFORM_MATRIX, info3.getMatrix()); for (int i = 0; i < MANY_BOUNDS.length; i++) { final RectF expectedBounds = MANY_BOUNDS[i]; @@ -187,10 +199,10 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertEquals(-1, uninitializedInfo.getComposingTextStart()); assertNull(uninitializedInfo.getComposingText()); assertEquals(0, uninitializedInfo.getInsertionMarkerFlags()); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal()); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop()); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline()); - assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom()); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON); + assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON); assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix()); } @@ -199,7 +211,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertFalse(Objects.equals(reference, actual)); } - @SmallTest + @Test public void testEquality() throws Exception { final Matrix MATRIX1 = new Matrix(); MATRIX1.setTranslate(10.0f, 20.0f); @@ -357,7 +369,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { INSERTION_MARKER_FLAGS2).build()); } - @SmallTest + @Test public void testMatrixIsCopied() throws Exception { final Matrix MATRIX1 = new Matrix(); MATRIX1.setTranslate(10.0f, 20.0f); @@ -385,7 +397,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { assertEquals(MATRIX2, secondInstance.getMatrix()); } - @SmallTest + @Test public void testMatrixIsRequired() throws Exception { final int SELECTION_START = 30; final int SELECTION_END = 40; @@ -434,7 +446,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testBuilderAddCharacterBounds() throws Exception { // A negative index should be rejected. try { diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index a339f61fbe4e..8df18480ed5a 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,16 +16,24 @@ package android.view.inputmethod; +import static org.junit.Assert.assertEquals; + import android.os.Parcel; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.ArrayList; -public class InputMethodSubtypeArrayTest extends InstrumentationTestCase { - @SmallTest - public void testInstanciate() throws Exception { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodSubtypeArrayTest { + + @Test + public void testInstantiate() throws Exception { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummySubtype(0, "en_US")); subtypes.add(createDummySubtype(1, "en_US")); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java index d203465577da..c76359ee39bc 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java @@ -16,15 +16,26 @@ package android.view.inputmethod; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import android.os.Parcel; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Locale; import java.util.Objects; -public class InputMethodSubtypeTest extends InstrumentationTestCase { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodSubtypeTest { public void verifyLocale(final String localeString) { // InputMethodSubtype#getLocale() returns exactly the same string that is passed to the @@ -48,7 +59,7 @@ public class InputMethodSubtypeTest extends InstrumentationTestCase { cloneViaParcel(cloneViaParcel(createDummySubtype(localeString))).hashCode()); } - @SmallTest + @Test public void testLocaleObj_locale() { final InputMethodSubtype usSubtype = createDummySubtype("en_US"); Locale localeObject = usSubtype.getLocaleObject(); @@ -59,7 +70,7 @@ public class InputMethodSubtypeTest extends InstrumentationTestCase { assertTrue(localeObject == usSubtype.getLocaleObject()); } - @SmallTest + @Test public void testLocaleObj_languageTag() { final InputMethodSubtype usSubtype = createDummySubtypeUsingLanguageTag("en-US"); Locale localeObject = usSubtype.getLocaleObject(); @@ -71,7 +82,7 @@ public class InputMethodSubtypeTest extends InstrumentationTestCase { assertTrue(localeObject == usSubtype.getLocaleObject()); } - @SmallTest + @Test public void testLocaleObj_emptyLocale() { final InputMethodSubtype emptyLocaleSubtype = createDummySubtype(""); assertNull(emptyLocaleSubtype.getLocaleObject()); @@ -80,7 +91,7 @@ public class InputMethodSubtypeTest extends InstrumentationTestCase { assertNull(emptyLocaleSubtype.getLocaleObject()); } - @SmallTest + @Test public void testLocaleString() throws Exception { // The locale string in InputMethodSubtype has accepted an arbitrary text actually, // regardless of the validity of the text as a locale string. @@ -95,7 +106,7 @@ public class InputMethodSubtypeTest extends InstrumentationTestCase { verifyLocale("fil_PH"); } - @SmallTest + @Test public void testDeprecatedLocaleString() throws Exception { // Make sure "iw" is not automatically replaced with "he". final InputMethodSubtype subtypeIw = createDummySubtype("iw"); diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java index f9fa017281b6..8c96b588d08f 100644 --- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java @@ -16,15 +16,25 @@ package android.view.inputmethod; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import android.graphics.RectF; import android.os.Parcel; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Objects; -public class SparseRectFArrayTest extends InstrumentationTestCase { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SparseRectFArrayTest { // A test data for {@link SparseRectFArray}. null represents the gap of indices. private static final RectF[] MANY_RECTS = new RectF[] { null, @@ -49,7 +59,7 @@ public class SparseRectFArrayTest extends InstrumentationTestCase { new RectF(118.0f, 218.0f, 318.0f, 418.0f), }; - @SmallTest + @Test public void testBuilder() throws Exception { final RectF TEMP_RECT = new RectF(10.0f, 20.0f, 30.0f, 40.0f); final int TEMP_FLAGS = 0x1234; @@ -128,7 +138,7 @@ public class SparseRectFArrayTest extends InstrumentationTestCase { assertNull(builder.build().get(0)); } - @SmallTest + @Test public void testEquality() throws Exception { // Empty array should be equal. assertEqualRects(new SparseRectFArrayBuilder().build(), @@ -225,7 +235,7 @@ public class SparseRectFArrayTest extends InstrumentationTestCase { .build()); } - @SmallTest + @Test public void testBuilderAppend() throws Exception { // Key should be appended in ascending order. try { diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java index 9061c4407685..13d6749cbc2c 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java @@ -16,11 +16,15 @@ package com.android.internal.inputmethod; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; @@ -28,11 +32,16 @@ import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodSubtypeSwitchingControllerTest { private static final String DUMMY_PACKAGE_NAME = "dummy package name"; private static final String DUMMY_IME_LABEL = "dummy ime label"; private static final String DUMMY_SETTING_ACTIVITY_NAME = ""; @@ -190,7 +199,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe controller.onUserActionLocked(subtypeListItem.mImi, subtype); } - @SmallTest + @Test public void testControllerImpl() throws Exception { final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes(); final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0); @@ -250,7 +259,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe disabledSubtypeUnawareIme, null, null); } - @SmallTest + @Test public void testControllerImplWithUserAction() throws Exception { final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); @@ -331,7 +340,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP); } - @SmallTest + @Test public void testImeSubtypeListItem() throws Exception { final List<ImeSubtypeListItem> items = new ArrayList<>(); addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", @@ -360,7 +369,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe assertFalse(item_EN_US.mIsSystemLocale); } - @SmallTest + @Test public void testImeSubtypeListComparator() throws Exception { { final List<ImeSubtypeListItem> items = Arrays.asList( diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java index d89dc637f5e0..33cd328a6d80 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java @@ -16,6 +16,15 @@ package com.android.internal.inputmethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.in; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; @@ -24,30 +33,31 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.LocaleList; import android.os.Parcel; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.util.ArrayMap; import android.util.ArraySet; import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import android.view.inputmethod.InputMethodSubtype; +import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.isIn; -import static org.hamcrest.Matchers.not; - -public class InputMethodUtilsTest extends InstrumentationTestCase { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodUtilsTest { private static final boolean IS_AUX = true; private static final boolean IS_DEFAULT = true; private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true; private static final boolean IS_ASCII_CAPABLE = true; private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true; - private static final boolean IS_SYSTEM_READY = true; private static final Locale LOCALE_EN = new Locale("en"); private static final Locale LOCALE_EN_US = new Locale("en", "US"); private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); @@ -77,7 +87,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = "EnabledWhenDefaultIsNotAsciiCapable"; - @SmallTest + @Test public void testVoiceImes() throws Exception { // locale: en_US assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, @@ -101,7 +111,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { "DummyNonDefaultAutoVoiceIme1"); } - @SmallTest + @Test public void testKeyboardImes() throws Exception { // locale: en_US assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, @@ -136,7 +146,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); } - @SmallTest + @Test public void testParcelable() throws Exception { final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS"); final List<InputMethodInfo> clonedList = cloneViaParcel(originalList); @@ -153,7 +163,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testGetImplicitlyApplicableSubtypesLocked() throws Exception { final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, @@ -434,8 +444,8 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { InputMethodUtils.getImplicitlyApplicableSubtypesLocked( getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi); assertEquals(2, result.size()); - assertThat(nonAutoSrLatn, isIn(result)); - assertThat(nonAutoHandwritingSrLatn, isIn(result)); + assertThat(nonAutoSrLatn, is(in(result))); + assertThat(nonAutoHandwritingSrLatn, is(in(result))); } { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); @@ -454,8 +464,8 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { InputMethodUtils.getImplicitlyApplicableSubtypesLocked( getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi); assertEquals(2, result.size()); - assertThat(nonAutoSrCyrl, isIn(result)); - assertThat(nonAutoHandwritingSrCyrl, isIn(result)); + assertThat(nonAutoSrCyrl, is(in(result))); + assertThat(nonAutoHandwritingSrCyrl, is(in(result))); } // Make sure that secondary locales are taken into account to find the best matching @@ -486,12 +496,12 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { Locale.forLanguageTag("en-US")), imi); assertEquals(6, result.size()); - assertThat(nonAutoEnGB, isIn(result)); - assertThat(nonAutoFr, isIn(result)); - assertThat(nonAutoSrLatn, isIn(result)); - assertThat(nonAutoHandwritingEn, isIn(result)); - assertThat(nonAutoHandwritingFr, isIn(result)); - assertThat(nonAutoHandwritingSrLatn, isIn(result)); + assertThat(nonAutoEnGB, is(in(result))); + assertThat(nonAutoFr, is(in(result))); + assertThat(nonAutoSrLatn, is(in(result))); + assertThat(nonAutoHandwritingEn, is(in(result))); + assertThat(nonAutoHandwritingFr, is(in(result))); + assertThat(nonAutoHandwritingSrLatn, is(in(result))); } // Make sure that 3-letter language code can be handled. @@ -604,16 +614,16 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi); - assertThat(nonAutoFrCA, isIn(result)); - assertThat(nonAutoEnUS, isIn(result)); - assertThat(nonAutoJa, isIn(result)); - assertThat(nonAutoIn, not(isIn(result))); - assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result))); - assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result))); + assertThat(nonAutoFrCA, is(in(result))); + assertThat(nonAutoEnUS, is(in(result))); + assertThat(nonAutoJa, is(in(result))); + assertThat(nonAutoIn, not(is(in(result)))); + assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result)))); + assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result)))); } } - @SmallTest + @Test public void testContainsSubtypeOf() throws Exception { final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, @@ -788,7 +798,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { private Context createTargetContextWithLocales(final LocaleList locales) { final Configuration resourceConfiguration = new Configuration(); resourceConfiguration.setLocales(locales); - return getInstrumentation() + return InstrumentationRegistry.getInstrumentation() .getTargetContext() .createConfigurationContext(resourceConfiguration); } @@ -1043,7 +1053,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { return preinstalledImes; } - @SmallTest + @Test public void testGetSuitableLocalesForSpellChecker() throws Exception { { final ArrayList<Locale> locales = @@ -1138,7 +1148,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testParseInputMethodsAndSubtypesString() { // Trivial cases. { @@ -1264,7 +1274,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testbuildInputMethodsAndSubtypesString() { { ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); @@ -1272,7 +1282,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } { ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); - map.put("ime0", new ArraySet<String>()); + map.put("ime0", new ArraySet<>()); assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map)); } { @@ -1300,8 +1310,8 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } { ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); - map.put("ime0", new ArraySet<String>()); - map.put("ime1", new ArraySet<String>()); + map.put("ime0", new ArraySet<>()); + map.put("ime1", new ArraySet<>()); ArraySet<String> validSequences = new ArraySet<>(); validSequences.add("ime0:ime1"); @@ -1314,7 +1324,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { ArraySet<String> subtypes1 = new ArraySet<>(); subtypes1.add("subtype0"); map.put("ime0", subtypes1); - map.put("ime1", new ArraySet<String>()); + map.put("ime1", new ArraySet<>()); ArraySet<String> validSequences = new ArraySet<>(); validSequences.add("ime0;subtype0:ime1"); @@ -1328,7 +1338,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { subtypes1.add("subtype0"); subtypes1.add("subtype1"); map.put("ime0", subtypes1); - map.put("ime1", new ArraySet<String>()); + map.put("ime1", new ArraySet<>()); ArraySet<String> validSequences = new ArraySet<>(); validSequences.add("ime0;subtype0;subtype1:ime1"); @@ -1380,7 +1390,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testConstructLocaleFromString() throws Exception { assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en")); assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US")); diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java index cd339fb95509..549511a420eb 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java @@ -16,24 +16,25 @@ package com.android.internal.inputmethod; +import static org.junit.Assert.assertEquals; + import android.os.LocaleList; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Locale; -public class LocaleUtilsTest extends InstrumentationTestCase { +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LocaleUtilsTest { - private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper = - new LocaleUtils.LocaleExtractor<Locale>() { - @Override - public Locale get(Locale source) { - return source; - } - }; + private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper = source -> source; - @SmallTest + @Test public void testFilterByLanguageEmptyLanguageList() throws Exception { final ArrayList<Locale> availableLocales = new ArrayList<>(); availableLocales.add(Locale.forLanguageTag("en-US")); @@ -49,7 +50,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { assertEquals(0, dest.size()); } - @SmallTest + @Test public void testFilterDoesNotMatchAnything() throws Exception { final ArrayList<Locale> availableLocales = new ArrayList<>(); availableLocales.add(Locale.forLanguageTag("en-US")); @@ -65,7 +66,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { assertEquals(0, dest.size()); } - @SmallTest + @Test public void testFilterByLanguageEmptySource() throws Exception { final ArrayList<Locale> availableLocales = new ArrayList<>(); @@ -76,7 +77,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { assertEquals(0, dest.size()); } - @SmallTest + @Test public void testFilterByLanguageNullAvailableLocales() throws Exception { { final LocaleList preferredLocales = @@ -138,7 +139,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testFilterByLanguage() throws Exception { { final ArrayList<Locale> availableLocales = new ArrayList<>(); @@ -172,7 +173,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testFilterByLanguageTheSameLanguage() throws Exception { { final LocaleList preferredLocales = @@ -223,7 +224,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { } } - @SmallTest + @Test public void testFilterByLanguageFallbackRules() throws Exception { { final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS"); @@ -355,6 +356,7 @@ public class LocaleUtilsTest extends InstrumentationTestCase { } } + @Test public void testFilterKnownLimitation() throws Exception { // Following test cases are not for intentional behavior but checks for preventing the // behavior from becoming worse. diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java index b8c41233f6f5..0b1b333a1ddc 100644 --- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java +++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java @@ -18,6 +18,7 @@ package android.util; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import android.content.Context; @@ -59,7 +60,7 @@ public class FeatureFlagUtilsTest { @Test public void testGetFlag_enabled_shouldReturnTrue() { - SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "true"); + FeatureFlagUtils.getAllFeatureFlags().put(TEST_FEATURE_NAME, "true"); assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)); } @@ -96,7 +97,11 @@ public class FeatureFlagUtilsTest { @Test public void testGetFlag_notSet_shouldReturnFalse() { - assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)); + assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME + "does_not_exist")); } + @Test + public void getAllFeatureFlags_shouldNotBeNull() { + assertNotNull(FeatureFlagUtils.getAllFeatureFlags()); + } } diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk index 47ee2cfe6e90..1d9f62442d3f 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk @@ -20,8 +20,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := DownloadManagerTestApp diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk index b35cbae58621..193e6010cef3 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk +++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk @@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SDK_VERSION := current diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk index 06812b511686..9f44767eed49 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk @@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SDK_VERSION := current diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk index 48bceafac26b..b4d354c6f359 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk @@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SDK_VERSION := current diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk index 569d102358da..969e58871903 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk @@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SDK_VERSION := current diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk index 0551eb6b75ad..30c2dcaaacaa 100644 --- a/core/tests/notificationtests/Android.mk +++ b/core/tests/notificationtests/Android.mk @@ -8,12 +8,11 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := NotificationStressTests LOCAL_STATIC_JAVA_LIBRARIES := \ junit \ - legacy-android-test \ ub-uiautomator include $(BUILD_PACKAGE) diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index d6b1cf1da8a2..a250d1f01ba1 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -39,13 +39,12 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - value = { - PURPOSE_ENCRYPT, - PURPOSE_DECRYPT, - PURPOSE_SIGN, - PURPOSE_VERIFY, - }) + @IntDef(flag = true, prefix = { "PURPOSE_" }, value = { + PURPOSE_ENCRYPT, + PURPOSE_DECRYPT, + PURPOSE_SIGN, + PURPOSE_VERIFY, + }) public @interface PurposeEnum {} /** @@ -126,7 +125,7 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ + @StringDef(prefix = { "KEY_" }, value = { KEY_ALGORITHM_RSA, KEY_ALGORITHM_EC, KEY_ALGORITHM_AES, @@ -267,7 +266,7 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ + @StringDef(prefix = { "BLOCK_MODE_" }, value = { BLOCK_MODE_ECB, BLOCK_MODE_CBC, BLOCK_MODE_CTR, @@ -354,7 +353,7 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ + @StringDef(prefix = { "ENCRYPTION_PADDING_" }, value = { ENCRYPTION_PADDING_NONE, ENCRYPTION_PADDING_PKCS7, ENCRYPTION_PADDING_RSA_PKCS1, @@ -437,7 +436,7 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ + @StringDef(prefix = { "SIGNATURE_PADDING_" }, value = { SIGNATURE_PADDING_RSA_PKCS1, SIGNATURE_PADDING_RSA_PSS, }) @@ -497,7 +496,7 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef({ + @StringDef(prefix = { "DIGEST_" }, value = { DIGEST_NONE, DIGEST_MD5, DIGEST_SHA1, @@ -647,11 +646,12 @@ public abstract class KeyProperties { * @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef({ - ORIGIN_GENERATED, - ORIGIN_IMPORTED, - ORIGIN_UNKNOWN, - }) + @IntDef(prefix = { "ORIGIN_" }, value = { + ORIGIN_GENERATED, + ORIGIN_IMPORTED, + ORIGIN_UNKNOWN, + }) + public @interface OriginEnum {} /** Key was generated inside AndroidKeyStore. */ diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index b15e0a221c6f..7cb8e375a969 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -81,12 +81,16 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { } out.writeByteArray(mSpec.getCertificateSubject().getEncoded()); out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray()); - writeOptionalDate(out, mSpec.getCertificateNotBefore()); - writeOptionalDate(out, mSpec.getCertificateNotAfter()); + out.writeLong(mSpec.getCertificateNotBefore().getTime()); + out.writeLong(mSpec.getCertificateNotAfter().getTime()); writeOptionalDate(out, mSpec.getKeyValidityStart()); writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd()); writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd()); - out.writeStringArray(mSpec.getDigests()); + if (mSpec.isDigestsSpecified()) { + out.writeStringArray(mSpec.getDigests()); + } else { + out.writeStringArray(null); + } out.writeStringArray(mSpec.getEncryptionPaddings()); out.writeStringArray(mSpec.getSignaturePaddings()); out.writeStringArray(mSpec.getBlockModes()); @@ -111,9 +115,15 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { private ParcelableKeyGenParameterSpec(Parcel in) { String keystoreAlias = in.readString(); int purposes = in.readInt(); - KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes); + KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder( + keystoreAlias, purposes); builder.setUid(in.readInt()); - builder.setKeySize(in.readInt()); + // KeySize is -1 by default, if the KeyGenParameterSpec previously parcelled had the default + // value, do not set it as this will cause setKeySize to throw. + int keySize = in.readInt(); + if (keySize >= 0) { + builder.setKeySize(keySize); + } int keySpecType = in.readInt(); AlgorithmParameterSpec algorithmSpec = null; @@ -128,17 +138,22 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { algorithmSpec = new ECGenParameterSpec(stdName); } else { throw new IllegalArgumentException( - String.format("Unknown algorithm parameter spec: %d", algorithmSpec)); + String.format("Unknown algorithm parameter spec: %d", keySpecType)); + } + if (algorithmSpec != null) { + builder.setAlgorithmParameterSpec(algorithmSpec); } - builder.setAlgorithmParameterSpec(algorithmSpec); builder.setCertificateSubject(new X500Principal(in.createByteArray())); builder.setCertificateSerialNumber(new BigInteger(in.createByteArray())); - builder.setCertificateNotBefore(readDateOrNull(in)); - builder.setCertificateNotAfter(readDateOrNull(in)); + builder.setCertificateNotBefore(new Date(in.readLong())); + builder.setCertificateNotAfter(new Date(in.readLong())); builder.setKeyValidityStart(readDateOrNull(in)); builder.setKeyValidityForOriginationEnd(readDateOrNull(in)); builder.setKeyValidityForConsumptionEnd(readDateOrNull(in)); - builder.setDigests(in.createStringArray()); + String[] digests = in.createStringArray(); + if (digests != null) { + builder.setDigests(digests); + } builder.setEncryptionPaddings(in.createStringArray()); builder.setSignaturePaddings(in.createStringArray()); builder.setBlockModes(in.createStringArray()); diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk index 51adde4d68af..1167f76725f1 100644 --- a/keystore/tests/Android.mk +++ b/keystore/tests/Android.mk @@ -21,8 +21,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - legacy-android-test + android-support-test LOCAL_PACKAGE_NAME := KeystoreTests diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 96e4fcf37fa3..7cacaf6a16ad 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -122,7 +122,7 @@ cc_defaults { instrumentation: true, profile_file: "hwui/hwui.profdata", benchmarks: ["hwui"], - enable_profile_use: false, + enable_profile_use: true, }, } diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h index 8f866f50e450..db9509fff378 100644 --- a/libs/hwui/NinePatchUtils.h +++ b/libs/hwui/NinePatchUtils.h @@ -53,10 +53,13 @@ static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) { return xRects * yRects; } -static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags, - int numFlags, const Res_png_9patch& chunk) { - lattice->fFlags = flags; - sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags)); +static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, + SkCanvas::Lattice::RectType* flags, int numFlags, const Res_png_9patch& chunk, + SkColor* colors) { + lattice->fRectTypes = flags; + lattice->fColors = colors; + sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::RectType)); + sk_bzero(colors, numFlags * sizeof(SkColor)); bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0]; bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0]; @@ -65,6 +68,7 @@ static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice if (needPadRow) { // Skip flags for the degenerate first row of rects. flags += lattice->fXCount + 1; + colors += lattice->fXCount + 1; yCount--; } @@ -75,20 +79,28 @@ static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice if (0 == x && needPadCol) { // First rect of each column is degenerate, skip the flag. flags++; + colors++; continue; } - if (0 == chunk.getColors()[i++]) { - *flags = SkCanvas::Lattice::kTransparent_Flags; + uint32_t currentColor = chunk.getColors()[i++]; + if (Res_png_9patch::TRANSPARENT_COLOR == currentColor) { + *flags = SkCanvas::Lattice::kTransparent; + setFlags = true; + } else if (Res_png_9patch::NO_COLOR != currentColor) { + *flags = SkCanvas::Lattice::kFixedColor; + *colors = currentColor; setFlags = true; } flags++; + colors++; } } if (!setFlags) { - lattice->fFlags = nullptr; + lattice->fRectTypes = nullptr; + lattice->fColors = nullptr; } } diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 77d7bef9c9e1..4ddacc8a922d 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -42,8 +42,15 @@ public: mBounds.set(left, top, right, bottom); mRadius = radius; + + // Reuse memory if previous outline was the same shape (rect or round rect). + if ( mPath.countVerbs() > 10) { + mPath.reset(); + } else { + mPath.rewind(); + } + // update mPath to reflect new outline - mPath.reset(); if (MathUtils::isPositive(radius)) { mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radius, radius); } else { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index eb0d161d71d1..13e1ebec943e 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -699,7 +699,8 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); - lattice.fFlags = nullptr; + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; int numFlags = 0; if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) { // We can expect the framework to give us a color for every distinct rect. @@ -707,9 +708,10 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); } - SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags); + SkAutoSTMalloc<25, SkColor> colors(numFlags); if (numFlags > 0) { - NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } lattice.fBounds = nullptr; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp index 288d0395370c..107890e57a19 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -45,8 +45,7 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); LOG_ALWAYS_FATAL_IF(!glInterface.get()); - grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend, - (GrBackendContext)glInterface.get())); + grContext = GrContext::MakeGL(std::move(glInterface)); } else { grContext->resetContext(); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 91b35c281b04..035cea3f61b0 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -218,7 +218,8 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); - lattice.fFlags = nullptr; + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; int numFlags = 0; if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) { // We can expect the framework to give us a color for every distinct rect. @@ -226,9 +227,10 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); } - SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags); + SkAutoSTMalloc<25, SkColor> colors(numFlags); if (numFlags > 0) { - NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } lattice.fBounds = nullptr; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 5e89faec1bab..907f2d2d398f 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -53,13 +53,13 @@ CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display } } -void CacheManager::reset(GrContext* context) { - if (context != mGrContext.get()) { +void CacheManager::reset(sk_sp<GrContext> context) { + if (context != mGrContext) { destroy(); } if (context) { - mGrContext = sk_ref_sp(context); + mGrContext = std::move(context); mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); updateContextCacheSizes(); } diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index d0370453c258..7d733525194f 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -61,7 +61,7 @@ private: CacheManager(const DisplayInfo& display); - void reset(GrContext* grContext); + void reset(sk_sp<GrContext> grContext); void destroy(); void updateContextCacheSizes(); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 4df7cafa4792..848c6a81d2b7 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -141,8 +141,9 @@ void EglManager::initialize() { GrContextOptions options; options.fDisableDistanceFieldPaths = true; mRenderThread.cacheManager().configureContext(&options); - mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend, - (GrBackendContext)glInterface.get(), options)); + sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options)); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + mRenderThread.setGrContext(grContext); } } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 20443ec38c90..79dc09ffbf1d 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -173,12 +173,12 @@ Readback& RenderThread::readback() { return *mReadback; } -void RenderThread::setGrContext(GrContext* context) { +void RenderThread::setGrContext(sk_sp<GrContext> context) { mCacheManager->reset(context); - if (mGrContext.get()) { + if (mGrContext) { mGrContext->releaseResourcesAndAbandonContext(); } - mGrContext.reset(context); + mGrContext = std::move(context); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 970537b69fad..3aa548773b3b 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -88,7 +88,7 @@ public: const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } GrContext* getGrContext() const { return mGrContext.get(); } - void setGrContext(GrContext* cxt); + void setGrContext(sk_sp<GrContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } VulkanManager& vulkanManager() { return *mVkManager; } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index a693e68afd66..9d246ffc9a4a 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -58,6 +58,7 @@ void VulkanManager::initialize() { mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr, &mPresentQueueIndex, canPresent)); + LOG_ALWAYS_FATAL_IF(!mBackendContext.get()); // Get all the addresses of needed vulkan functions VkInstance instance = mBackendContext->fInstance; @@ -110,8 +111,9 @@ void VulkanManager::initialize() { GrContextOptions options; options.fDisableDistanceFieldPaths = true; mRenderThread.cacheManager().configureContext(&options); - mRenderThread.setGrContext( - GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options)); + sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options)); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + mRenderThread.setGrContext(grContext); DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize()); if (Properties::enablePartialUpdates && Properties::useBufferAge) { diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 12e5744d2035..e2f9b47ed482 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -977,7 +977,7 @@ public final class MediaDrm { public static final String PROPERTY_ALGORITHMS = "algorithms"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "PROPERTY_" }, value = { PROPERTY_VENDOR, PROPERTY_VERSION, PROPERTY_DESCRIPTION, @@ -1010,7 +1010,7 @@ public final class MediaDrm { public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "PROPERTY_" }, value = { PROPERTY_DEVICE_UNIQUE_ID, }) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java index 31eb948dcd09..94d4d55639dc 100644 --- a/media/java/android/media/MediaMetadata.java +++ b/media/java/android/media/MediaMetadata.java @@ -45,34 +45,61 @@ public final class MediaMetadata implements Parcelable { /** * @hide */ - @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR, - METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION, - METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI, - METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE, - METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI, - METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI}) + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_TITLE, + METADATA_KEY_ARTIST, + METADATA_KEY_ALBUM, + METADATA_KEY_AUTHOR, + METADATA_KEY_WRITER, + METADATA_KEY_COMPOSER, + METADATA_KEY_COMPILATION, + METADATA_KEY_DATE, + METADATA_KEY_GENRE, + METADATA_KEY_ALBUM_ARTIST, + METADATA_KEY_ART_URI, + METADATA_KEY_ALBUM_ART_URI, + METADATA_KEY_DISPLAY_TITLE, + METADATA_KEY_DISPLAY_SUBTITLE, + METADATA_KEY_DISPLAY_DESCRIPTION, + METADATA_KEY_DISPLAY_ICON_URI, + METADATA_KEY_MEDIA_ID, + METADATA_KEY_MEDIA_URI, + }) @Retention(RetentionPolicy.SOURCE) public @interface TextKey {} /** * @hide */ - @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER, - METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE}) + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_DURATION, + METADATA_KEY_YEAR, + METADATA_KEY_TRACK_NUMBER, + METADATA_KEY_NUM_TRACKS, + METADATA_KEY_DISC_NUMBER, + METADATA_KEY_BT_FOLDER_TYPE, + }) @Retention(RetentionPolicy.SOURCE) public @interface LongKey {} /** * @hide */ - @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON}) + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_ART, + METADATA_KEY_ALBUM_ART, + METADATA_KEY_DISPLAY_ICON, + }) @Retention(RetentionPolicy.SOURCE) public @interface BitmapKey {} /** * @hide */ - @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING}) + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_USER_RATING, + METADATA_KEY_RATING, + }) @Retention(RetentionPolicy.SOURCE) public @interface RatingKey {} diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index 8283c8b967e8..17d16b896679 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -17,6 +17,7 @@ package android.media.session; import android.annotation.DrawableRes; import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.Nullable; import android.media.RemoteControlClient; import android.os.Bundle; @@ -41,7 +42,7 @@ public final class PlaybackState implements Parcelable { /** * @hide */ - @IntDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, + @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 0f4609603eab..3bbc2c4e2304 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -1650,7 +1650,7 @@ public final class TvContract { public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "TYPE_" }, value = { TYPE_OTHER, TYPE_NTSC, TYPE_PAL, @@ -1863,7 +1863,7 @@ public final class TvContract { public static final String TYPE_PREVIEW = "TYPE_PREVIEW"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "SERVICE_TYPE_" }, value = { SERVICE_TYPE_OTHER, SERVICE_TYPE_AUDIO_VIDEO, SERVICE_TYPE_AUDIO, @@ -1881,7 +1881,7 @@ public final class TvContract { public static final String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "VIDEO_FORMAT_" }, value = { VIDEO_FORMAT_240P, VIDEO_FORMAT_360P, VIDEO_FORMAT_480I, @@ -1930,7 +1930,7 @@ public final class TvContract { public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P"; /** @hide */ - @StringDef({ + @StringDef(prefix = { "VIDEO_RESOLUTION_" }, value = { VIDEO_RESOLUTION_SD, VIDEO_RESOLUTION_ED, VIDEO_RESOLUTION_HD, diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 21410eac3d8d..b4ca88cd2bcf 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -24,7 +24,6 @@ #define USE_SHARED_MEM_BUFFER #include <media/AudioTrack.h> -#include <media/mediaplayer.h> #include "SoundPool.h" #include "SoundPoolThread.h" #include <media/AudioPolicyHelper.h> diff --git a/media/mca/tests/Android.mk b/media/mca/tests/Android.mk index eb451f72e49e..394f5422c215 100644 --- a/media/mca/tests/Android.mk +++ b/media/mca/tests/Android.mk @@ -4,8 +4,8 @@ include $(CLEAR_VARS) # We only want this apk build for tests. LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk index 0d9f42b93007..145cde68a4d5 100644 --- a/media/tests/MediaFrameworkTest/Android.mk +++ b/media/tests/MediaFrameworkTest/Android.mk @@ -5,13 +5,12 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-target-minus-junit4 \ android-support-test \ - android-ex-camera2 \ - legacy-android-test + android-ex-camera2 LOCAL_PACKAGE_NAME := mediaframeworktest diff --git a/nfc-extras/tests/Android.mk b/nfc-extras/tests/Android.mk index d8fe5a6cca30..be2bb532b956 100644 --- a/nfc-extras/tests/Android.mk +++ b/nfc-extras/tests/Android.mk @@ -22,7 +22,7 @@ LOCAL_JAVA_LIBRARIES := \ android.test.runner \ com.android.nfc_extras -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk index 63bd0b13c61f..6e120a6355e8 100644 --- a/packages/CarrierDefaultApp/tests/unit/Android.mk +++ b/packages/CarrierDefaultApp/tests/unit/Android.mk @@ -19,9 +19,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4 legacy-android-test +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4 # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk index d4c1e7094b99..a8255813985f 100644 --- a/packages/EasterEgg/Android.mk +++ b/packages/EasterEgg/Android.mk @@ -2,17 +2,23 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional + LOCAL_STATIC_JAVA_LIBRARIES := \ + jsr305 + +LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v4 \ android-support-v13 \ android-support-dynamic-animation \ android-support-v7-recyclerview \ android-support-v7-preference \ android-support-v7-appcompat \ - android-support-v14-preference \ - jsr305 + android-support-v14-preference + +LOCAL_USE_AAPT2 := true LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME := EasterEgg LOCAL_CERTIFICATE := platform diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk index 92afbeb98abd..1eb5847459ff 100644 --- a/packages/ExtServices/tests/Android.mk +++ b/packages/ExtServices/tests/Android.mk @@ -5,14 +5,13 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ mockito-target-minus-junit4 \ espresso-core \ truth-prebuilt \ - legacy-android-test \ testables # Include all test java files. diff --git a/packages/InputDevices/res/raw/keyboard_layout_azerbaijani.kcm b/packages/InputDevices/res/raw/keyboard_layout_azerbaijani.kcm new file mode 100644 index 000000000000..69490cc343e6 --- /dev/null +++ b/packages/InputDevices/res/raw/keyboard_layout_azerbaijani.kcm @@ -0,0 +1,312 @@ +# 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. + +# +# Azerbaijan (AZ) keyboard layout. +# + +type OVERLAY + +### ROW 1 + +key GRAVE { + label: '`' + base: '`' + shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '"' + ralt: '@' +} + +key 3 { + label: '3' + base: '3' + shift: '\u2166' +} + +key 4 { + label: '4' + base: '4' + shift: ';' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: ':' + shift+ralt: '^' +} + +key 7 { + label: '7' + base: '7' + shift: '?' + ralt: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key W { + label: '\u00dc' + base: '\u00fc' + shift, capslock: '\u00dc' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: '\u0130' + base: 'i' + shift, capslock: '\u0130' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u00d6' + base: '\u00f6' + shift: '\u00d6' +} + +key RIGHT_BRACKET { + label: '\u011e' + base: '\u011f' + shift: '\u011e' +} + +key BACKSLASH { + label: '\\' + base: '\\' + shift: '|' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: 'I' + base: '\u0131' + shift: 'I' +} + +key APOSTROPHE { + label: '\u018f' + base: '\u0259' + shift: '\u018f' +} + +### ROW 4 + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key COMMA { + label: '\u00c7' + base: '\u00e7' + shift: '\u00c7' +} + +key PERIOD { + label: '\u015e' + base: '\u015f' + shift: '\u015e' +} + +key SLASH { + label: '.' + base: '.' + shift: ',' +} diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index 33b9ae35a122..f2409d4345dd 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -25,7 +25,7 @@ <string name="keyboard_layout_swedish" msgid="732959109088479351">"Swedia"</string> <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandia"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroasia"</string> - <string name="keyboard_layout_czech" msgid="1349256901452975343">"Cheska"</string> + <string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceko"</string> <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonia"</string> <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungaria"</string> <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string> diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml index 1e262260a6c7..61d3234a1f44 100644 --- a/packages/InputDevices/res/values/strings.xml +++ b/packages/InputDevices/res/values/strings.xml @@ -122,4 +122,7 @@ <!-- Persian keyboard layout label. [CHAR LIMIT=35] --> <string name="keyboard_layout_persian">Persian</string> + + <!-- Azerbaijani keyboard layout label. [CHAR LIMIT=35] --> + <string name="keyboard_layout_azerbaijani">Azerbaijani</string> </resources> diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index c55711a4e716..c6bfc1f65be2 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -155,4 +155,8 @@ <keyboard-layout android:name="keyboard_layout_persian" android:label="@string/keyboard_layout_persian" android:keyboardLayout="@raw/keyboard_layout_persian" /> + + <keyboard-layout android:name="keyboard_layout_azerbaijani" + android:label="@string/keyboard_layout_azerbaijani" + android:keyboardLayout="@raw/keyboard_layout_azerbaijani" /> </keyboard-layouts> diff --git a/packages/MtpDocumentsProvider/tests/Android.mk b/packages/MtpDocumentsProvider/tests/Android.mk index 148cd0d930ab..ba346f424829 100644 --- a/packages/MtpDocumentsProvider/tests/Android.mk +++ b/packages/MtpDocumentsProvider/tests/Android.mk @@ -3,8 +3,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := MtpDocumentsProviderTests LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider LOCAL_CERTIFICATE := media diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 49c5467d03e0..9d1c4ca8ec7e 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -21,6 +21,10 @@ LOCAL_STATIC_JAVA_LIBRARIES += \ LOCAL_STATIC_ANDROID_LIBRARIES += \ android-support-v4 \ apptoolkit-lifecycle-runtime \ + android-support-v7-recyclerview \ + android-support-v7-preference \ + android-support-v7-appcompat \ + android-support-v14-preference \ SettingsLib else LOCAL_RESOURCE_DIR += $(call my-dir)/res diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index dd420b3c4315..ed0958c54282 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -225,7 +225,7 @@ <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži opcije za certifikaciju Bežičnog prikaza"</string> <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećajte nivo Wi-Fi zapisivanja, pokazati po SSID RSSI Wi-Fi Picker"</string> <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kada je omogućeno, Wi-Fi veza će u slučaju slabog signala agresivnije predavati vezu za prijenos podataka na mobilnu vezu"</string> - <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog prometa prisutnog na sučelju"</string> + <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog prometa prisutnog na interfejsu"</string> <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera za zapisnik"</string> <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Odaberite veličine za Logger prema međumemoriji evidencije"</string> <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite li izbrisati trajnu pohranu zapisivača?"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index aeb0ddbe1c4e..bb0a337d3cfb 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -97,7 +97,7 @@ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"कनेक्ट केल्यावर पेअरींग तुमचे संपर्क आणि कॉल इतिहास यामध्ये अॅक्सेस देते."</string> <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी जोडू शकलो नाही."</string> <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"अयोग्य पिन किंवा पासकीमुळे <xliff:g id="DEVICE_NAME">%1$s</xliff:g> सह जोडू शकलो नाही."</string> - <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी संप्रेषण करू शकत नाही."</string> + <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी संवाद प्रस्थापित करू शकत नाही."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारे पेअरींग नाकारले."</string> <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"संगणक"</string> <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"हेडसेट"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java index a966e8244ba3..3a03644b6226 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java @@ -75,23 +75,27 @@ public class DashboardCategory implements Parcelable { * Note: the returned list serves as a read-only list. If tiles needs to be added or removed * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}. */ - public List<Tile> getTiles() { - return Collections.unmodifiableList(mTiles); + public synchronized List<Tile> getTiles() { + final List<Tile> result = new ArrayList<>(mTiles.size()); + for (Tile tile : mTiles) { + result.add(tile); + } + return result; } - public void addTile(Tile tile) { + public synchronized void addTile(Tile tile) { mTiles.add(tile); } - public void addTile(int n, Tile tile) { + public synchronized void addTile(int n, Tile tile) { mTiles.add(n, tile); } - public void removeTile(Tile tile) { + public synchronized void removeTile(Tile tile) { mTiles.remove(tile); } - public void removeTile(int n) { + public synchronized void removeTile(int n) { mTiles.remove(n); } @@ -103,7 +107,7 @@ public class DashboardCategory implements Parcelable { return mTiles.get(n); } - public boolean containsComponent(ComponentName component) { + public synchronized boolean containsComponent(ComponentName component) { for (Tile tile : mTiles) { if (TextUtils.equals(tile.intent.getComponent().getClassName(), component.getClassName())) { @@ -129,7 +133,7 @@ public class DashboardCategory implements Parcelable { /** * Sort priority value and package name for tiles in this category. */ - public void sortTiles(String skipPackageName) { + public synchronized void sortTiles(String skipPackageName) { // Sort mTiles based on [priority, package within priority] Collections.sort(mTiles, (tile1, tile2) -> { final String package1 = tile1.intent.getComponent().getPackageName(); diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk index 7ace048324c3..cfb3519df77c 100644 --- a/packages/SettingsLib/tests/integ/Android.mk +++ b/packages/SettingsLib/tests/integ/Android.mk @@ -20,18 +20,19 @@ LOCAL_CERTIFICATE := platform LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base LOCAL_JACK_FLAGS := --multi-dex native LOCAL_PACKAGE_NAME := SettingsLibTests LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_USE_AAPT2 := true + LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ espresso-core \ mockito-target-minus-junit4 \ - legacy-android-test \ truth-prebuilt # Code coverage puts us over the dex limit, so enable multi-dex for coverage-enabled builds diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk index bc1a834535a6..273802754bbb 100644 --- a/packages/SettingsLib/tests/robotests/Android.mk +++ b/packages/SettingsLib/tests/robotests/Android.mk @@ -28,6 +28,8 @@ LOCAL_PRIVILEGED_MODULE := true LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/res +LOCAL_USE_AAPT2 := true + include frameworks/base/packages/SettingsLib/common.mk include $(BUILD_PACKAGE) diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk index 069e83a8d2d6..0f2c5ab45c1a 100644 --- a/packages/SettingsProvider/Android.mk +++ b/packages/SettingsProvider/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \ src/com/android/providers/settings/EventLogTags.logtags LOCAL_JAVA_LIBRARIES := telephony-common ims-common -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := SettingsProvider LOCAL_CERTIFICATE := platform diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index 9374d522707b..287a888a23b2 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -11,6 +11,8 @@ android:icon="@mipmap/ic_launcher_settings" android:defaultToDeviceProtectedStorage="true" android:directBootAware="true"> + <uses-library android:name="android.test.runner" /> + <provider android:name="SettingsProvider" android:authorities="settings" diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml index 75e7760b2628..d4bb3c6dc1f5 100644 --- a/packages/Shell/res/values-fa/strings.xml +++ b/packages/Shell/res/values-fa/strings.xml @@ -23,8 +23,8 @@ <string name="bugreport_updating_title" msgid="4423539949559634214">"اضافه کردن جزئیات به گزارش اشکال"</string> <string name="bugreport_updating_wait" msgid="3322151947853929470">"لطفاً منتظر بمانید..."</string> <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"گزارش مشکل بهزودی در تلفن نشان داده میشود"</string> - <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"برای به اشتراک گذاشتن گزارش اشکالتان، انتخاب کنید"</string> - <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای به اشتراک گذاشتن گزارش اشکال، ضربه بزنید"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"برای همرسانی گزارش اشکالتان، انتخاب کنید"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای همرسانی گزارش اشکال، ضربه بزنید"</string> <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"انتخاب کنید تا گزارش اشکالتان بدون عکس صفحهنمایش به اشتراک گذاشته شود یا منتظر بمانید گرفتن عکس از صفحهنمایش تمام شود"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"برای اشتراکگذاری گزارش مشکل بدون عکس صفحهنمایش، ضربه بزنید یا صبر کنید تا عکس صفحهنمایش گرفته شود."</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"برای اشتراکگذاری گزارش مشکل بدون عکس صفحهنمایش، ضربه بزنید یا صبر کنید تا عکس صفحهنمایش گرفته شود."</string> diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk index 48b757c30cd1..7f24a386e734 100644 --- a/packages/Shell/tests/Android.mk +++ b/packages/Shell/tests/Android.mk @@ -6,14 +6,13 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ mockito-target-minus-junit4 \ ub-uiautomator \ junit \ - legacy-android-test \ LOCAL_PACKAGE_NAME := ShellTests LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 8cc2ce260066..5b8881c1f946 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -15,6 +15,7 @@ -keep class com.android.systemui.statusbar.tv.TvStatusBar -keep class com.android.systemui.car.CarSystemUIFactory -keep class com.android.systemui.SystemUIFactory +-keep class * extends com.android.systemui.SystemUI -keepclassmembers class ** { public void onBusEvent(**); diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 4f6fcb956f0a..70d13e67145f 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -59,8 +59,8 @@ <string name="kg_wrong_password" msgid="4580683060277329277">"Palavra-passe incorreta"</string> <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorreto"</string> <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755"> - <item quantity="one">Tente novamente dentro de 1 segundo.</item> <item quantity="other">Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item> + <item quantity="one">Tente novamente dentro de 1 segundo.</item> </plurals> <string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe o seu padrão"</string> <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introduza o PIN do cartão SIM."</string> @@ -97,13 +97,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do cartão SIM incorreto. Tem de contactar o seu operador para desbloquear o dispositivo."</string> <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967"> - <item quantity="one">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de precisar de contactar o seu operador para desbloquear o dispositivo.</item> <item quantity="other">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item> + <item quantity="one">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de precisar de contactar o seu operador para desbloquear o dispositivo.</item> </plurals> <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"Cartão SIM inutilizável. Contacte o seu operador."</string> <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513"> - <item quantity="one">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável.</item> <item quantity="other">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável.</item> + <item quantity="one">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável.</item> </plurals> <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha ao introduzir o PIN do cartão SIM!"</string> <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha ao introduzir o PUK do cartão SIM!"</string> @@ -123,16 +123,16 @@ <string name="kg_prompt_reason_device_admin" msgid="3452168247888906179">"Dispositivo bloqueado pelo gestor"</string> <string name="kg_prompt_reason_user_request" msgid="8236951765212462286">"O dispositivo foi bloqueado manualmente"</string> <plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="71299470072448533"> - <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o padrão.</item> <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme o padrão.</item> + <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o padrão.</item> </plurals> <plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="34586942088144385"> - <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o PIN.</item> <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme o PIN.</item> + <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o PIN.</item> </plurals> <plurals name="kg_prompt_reason_time_password" formatted="false" msgid="257297696215346527"> - <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item> <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a palavra-passe.</item> + <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item> </plurals> <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string> </resources> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 3dd0e6c222f7..3a41681f53e9 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -367,4 +367,20 @@ <!-- Fingerprint hint message when finger was not recognized.--> <string name="fingerprint_not_recognized">Not recognized</string> + <!-- Instructions telling the user remaining times when enter SIM PIN view. --> + <plurals name="kg_password_default_pin_message"> + <item quantity="one">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining +attempt before you must contact your carrier to unlock your device.</item> + <item quantity="other">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining +attempts.</item> + </plurals> + + <!-- Instructions telling the user remaining times when enter SIM PUK view. --> + <plurals name="kg_password_default_puk_message"> + <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id=" +number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item> + <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id=" +number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item> + </plurals> + </resources> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index fac254a0083a..bab1e5e4698f 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.systemui.HardwareUiLayout +<com.android.systemui.volume.VolumeUiLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" @@ -21,18 +21,20 @@ android:clipChildren="false" > <RelativeLayout android:id="@+id/volume_dialog" - android:layout_width="@dimen/volume_dialog_panel_width" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" android:paddingTop="@dimen/volume_row_padding_bottom" - android:layout_margin="12dp" - android:background="?android:attr/actionBarItemBackground" - android:translationZ="8dp" > + android:background="@drawable/rounded_full_bg_bottom" + android:translationZ="8dp" + android:clipChildren="false" > <LinearLayout android:id="@+id/volume_dialog_content" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_toStartOf="@id/expand" + android:clipChildren="false" + android:clipToPadding="false" android:orientation="vertical" > <LinearLayout @@ -42,7 +44,84 @@ android:orientation="vertical" > <!-- volume rows added and removed here! :-) --> </LinearLayout> + + </LinearLayout> + <LinearLayout + android:id="@+id/expand" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_alignParentEnd="true" + android:layout_alignParentTop="true" + android:layout_marginEnd="@dimen/volume_expander_margin_end" > + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:textAppearance="@style/TextAppearance.Volume.Header" /> + <com.android.keyguard.AlphaOptimizedImageButton + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/volume_expand_button" + style="@style/VolumeButtons" + android:layout_width="@dimen/volume_button_size" + android:layout_height="@dimen/volume_button_size" + android:clickable="true" + android:soundEffectsEnabled="false" + android:src="@drawable/ic_volume_collapse_animation" + android:background="@drawable/ripple_drawable" + tools:ignore="RtlHardcoded" /> + </LinearLayout> + <!-- special row for ringer mode --> + <RelativeLayout + android:id="@+id/ringer_mode" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/rounded_bg_full" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_below="@id/volume_dialog_content" + android:layout_margin="10dp"> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/ringer_icon" + style="@style/VolumeButtons" + android:background="?android:selectableItemBackgroundBorderless" + android:layout_width="@dimen/volume_button_size" + android:layout_height="@dimen/volume_button_size" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:soundEffectsEnabled="false" /> + + <TextView + android:id="@+id/ringer_title" + android:text="@string/stream_ring" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:layout_toEndOf="@+id/ringer_icon" + android:layout_marginStart="64dp" + android:textColor="?android:attr/colorControlNormal" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingStart="@dimen/volume_row_header_padding_start" /> + + <TextView + android:id="@+id/ringer_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:layout_marginEnd="14dp" + android:maxLines="1" + android:textColor="?android:attr/colorControlNormal" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> </RelativeLayout> -</com.android.systemui.HardwareUiLayout> +</com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 581ac735887f..577077929352 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -34,14 +34,14 @@ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তিগুলি"</string> <string name="battery_low_title" msgid="6456385927409742437">"ব্যাটারি কম"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে"</string> - <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে৷ ব্যাটারি সাশ্রয়কারী চালু আছে৷"</string> + <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে। ব্যাটারি সেভার চালু আছে।"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB চার্জিং সমর্থিত নয়৷\nকেবলমাত্র সরবহারকৃত চার্জার ব্যবহার করুন৷"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB চার্জিং সমর্থিত নয়।"</string> <string name="invalid_charger_text" msgid="5474997287953892710">"শুধুমাত্র সরবরাহকৃত চার্জার ব্যবহার করুন।"</string> <string name="battery_low_why" msgid="4553600287639198111">"সেটিংস"</string> - <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"ব্যাটারি সঞ্চয়কারী চালু করবেন?"</string> + <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"ব্যাটারি সেভার চালু করবেন?"</string> <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"চালু করুন"</string> - <string name="battery_saver_start_action" msgid="5576697451677486320">"ব্যাটারি সঞ্চয়কারী চালু"</string> + <string name="battery_saver_start_action" msgid="8187820911065797519">"ব্যাটারি সেভার চালু করুন"</string> <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"সেটিংস"</string> <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"ওয়াই-ফাই"</string> <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"স্বতঃ-ঘূর্ণায়মান স্ক্রিন"</string> @@ -243,8 +243,6 @@ <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ডেট বিরতি দেওয়া হয়েছে"</string> <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"আপনার সেট করা ডেটার সীমা ফুরিয়ে গেছে। আপনি এখন আর মোবাইল ডেটা ব্যবহার করতে পারবেন না।\n\nযদি আপনি আবার শুরু করেন, ডেটা ব্যবহারের জন্য মূল্য প্রযোজ্য হতে পারে।"</string> <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"পুনঃসূচনা করুন"</string> - <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"কোনো ইন্টারনেট সংযোগ নেই"</string> - <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ওয়াই-ফাই সংযুক্ত হয়েছে"</string> <string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS এর জন্য অনুসন্ধান করা হচ্ছে"</string> <string name="gps_notification_found_text" msgid="4619274244146446464">"GPS এর দ্বারা সেট করা অবস্থান"</string> <string name="accessibility_location_active" msgid="2427290146138169014">"অবস্থান অনুরোধ সক্রিয় রয়েছে"</string> @@ -401,9 +399,9 @@ <string name="user_remove_user_title" msgid="4681256956076895559">"ব্যবহারকারী সরাবেন?"</string> <string name="user_remove_user_message" msgid="1453218013959498039">"এই ব্যবহারকারীর সমস্ত অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string> <string name="user_remove_user_remove" msgid="7479275741742178297">"সরান"</string> - <string name="battery_saver_notification_title" msgid="237918726750955859">"ব্যাটারি সেভার চালু রয়েছে"</string> + <string name="battery_saver_notification_title" msgid="8614079794522291840">"ব্যাটারি সেভার চালু আছে"</string> <string name="battery_saver_notification_text" msgid="820318788126672692">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string> - <string name="battery_saver_notification_action_text" msgid="109158658238110382">"ব্যাটারি সঞ্চয়কারী বন্ধ করুন"</string> + <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ব্যাটারি সেভার বন্ধ করুন"</string> <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> আপনার স্ক্রীনে দেখানো সব কিছু ক্যাপচার করা শুরু করবে।"</string> <string name="media_projection_remember_text" msgid="3103510882172746752">"আর দেখাবেন না"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"সবকিছু সাফ করুন"</string> @@ -582,8 +580,8 @@ <item quantity="other">%d মিনিট</item> </plurals> <string name="battery_panel_title" msgid="7944156115535366613">"ব্যাটারির ব্যবহার"</string> - <string name="battery_detail_charging_summary" msgid="4055327085770378335">"চার্জ করার সময় ব্যাটারি সেভার কাজ করে না"</string> - <string name="battery_detail_switch_title" msgid="8763441006881907058">"ব্যাটারি সেভার"</string> + <string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string> + <string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string> <string name="battery_detail_switch_summary" msgid="9049111149407626804">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string> <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string> <string name="keyboard_key_home" msgid="2243500072071305073">"হোম"</string> @@ -732,10 +730,10 @@ <string name="pip_skip_to_prev" msgid="1955311326688637914">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string> <string name="thermal_shutdown_title" msgid="4458304833443861111">"আপনার ফোন গরম হওয়ার জন্য বন্ধ হয়ে গেছে"</string> <string name="thermal_shutdown_message" msgid="9006456746902370523">"আপনার ফোন এখন ঠিক-ঠাক চলছে"</string> - <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপনার ফোন খুব বেশি গরম হয়েছিল বলে ঠান্ডা হওয়ার জন্য বন্ধ হয়ে গেছে। আপনার ফোন ঠিক-ঠাক ভাবে চলছে না।\n\nআপনার ফোন খুব বেশি গরম হয়ে যাবে যদি আপনি:\n •এমন অ্যাপ ব্যবহার করলে যেটি আপনার ডিভাইসের রিসোর্স বেশি ব্যবহার করে (যেমন গেমিং, ভিডিও বা নেভিগেশন অ্যাপ)\n • বড় ফাইল ডাউনলোড বা আপলোড করলে\n • বেশি তাপমাত্রায় আপনার ফোন ব্যবহার করলে"</string> + <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপনার ফোন খুব বেশি গরম হয়েছিল বলে ঠাণ্ডা হওয়ার জন্য বন্ধ হয়ে গেছে। আপনার ফোন ঠিক-ঠাক ভাবে চলছে না।\n\nআপনার ফোন খুব বেশি গরম হয়ে যাবে যদি আপনি:\n •এমন অ্যাপ ব্যবহার করলে যেটি আপনার ডিভাইসের রিসোর্স বেশি ব্যবহার করে (যেমন গেমিং, ভিডিও বা নেভিগেশন অ্যাপ)\n • বড় ফাইল ডাউনলোড বা আপলোড করলে\n • বেশি তাপমাত্রায় আপনার ফোন ব্যবহার করলে"</string> <string name="high_temp_title" msgid="4589508026407318374">"ফোনটি গরম হচ্ছে"</string> - <string name="high_temp_notif_message" msgid="5642466103153429279">"ফোনটি ঠান্ডা হওয়ার সময় কিছু বৈশিষ্ট্য সীমিত হতে পারে"</string> - <string name="high_temp_dialog_message" msgid="6840700639374113553">"আপনার ফোনটি নিজে থেকেই ঠান্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠান্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string> + <string name="high_temp_notif_message" msgid="5642466103153429279">"ফোনটি ঠাণ্ডা হওয়ার সময় কিছু বৈশিষ্ট্য সীমিত হতে পারে"</string> + <string name="high_temp_dialog_message" msgid="6840700639374113553">"আপনার ফোনটি নিজে থেকেই ঠাণ্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠাণ্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string> <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"বাঁদিকের শর্টকাট"</string> <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"ডানদিকের শর্টকাট"</string> <string name="lockscreen_unlock_left" msgid="2043092136246951985">"বাঁদিকের শর্টকাট দিয়েও আনলক করা যায়"</string> @@ -758,7 +756,8 @@ <string name="instant_apps" msgid="6647570248119804907">"ঝটপট অ্যাপ"</string> <string name="instant_apps_message" msgid="8116608994995104836">"ঝটপট অ্যাপ ইনস্টল করার প্রয়োজন হয় না।"</string> <string name="app_info" msgid="6856026610594615344">"অ্যাপের তথ্য"</string> - <string name="go_to_web" msgid="1106022723459948514">"ওয়েবে যান"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"মোবাইল ডেটা"</string> <string name="wifi_is_off" msgid="1838559392210456893">"ওয়াই ফাই বন্ধ আছে"</string> <string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ বন্ধ আছে"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 601d8e33bcdb..530c1a8b56aa 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -339,9 +339,9 @@ <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string> <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string> <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string> - <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podijeli ekran nagore"</string> - <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podijeli ekran nalijevo"</string> - <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podijeli ekran nadesno"</string> + <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dijeli ekran nagore"</string> + <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dijeli ekran nalijevo"</string> + <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dijeli ekran nadesno"</string> <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string> <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string> <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Do kraja punjenja preostalo <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string> @@ -764,7 +764,8 @@ <string name="instant_apps" msgid="6647570248119804907">"Instant-aplikacije"</string> <string name="instant_apps_message" msgid="8116608994995104836">"Za instant aplikacije nije potrebna instalacija"</string> <string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string> - <string name="go_to_web" msgid="1106022723459948514">"Idite na internet"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi veza je isključena"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index cd1f057fa86a..d107fea93d3c 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -756,7 +756,8 @@ <string name="instant_apps" msgid="6647570248119804907">"برنامههای فوری"</string> <string name="instant_apps_message" msgid="8116608994995104836">"برنامههای فوری نیاز به نصب ندارند."</string> <string name="app_info" msgid="6856026610594615344">"اطلاعات برنامه"</string> - <string name="go_to_web" msgid="1106022723459948514">"رفتن به وب"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"داده تلفن همراه"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi خاموش است"</string> <string name="bt_is_off" msgid="2640685272289706392">"بلوتوث خاموش است"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 075edf108e78..b4709bb4c77d 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -756,7 +756,8 @@ <string name="instant_apps" msgid="6647570248119804907">"Aplikasi Instan"</string> <string name="instant_apps_message" msgid="8116608994995104836">"Aplikasi instan tidak perlu diinstal."</string> <string name="app_info" msgid="6856026610594615344">"Info aplikasi"</string> - <string name="go_to_web" msgid="1106022723459948514">"Buka di web"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"Data seluler"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi nonaktif"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth nonaktif"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 1b80410a7fe7..e871c6abe9e3 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -756,7 +756,8 @@ <string name="instant_apps" msgid="6647570248119804907">"მყისიერი აპები"</string> <string name="instant_apps_message" msgid="8116608994995104836">"მყისიერი აპები ინსტალაციას არ საჭიროებს."</string> <string name="app_info" msgid="6856026610594615344">"აპის შესახებ"</string> - <string name="go_to_web" msgid="1106022723459948514">"ვებზე გადასვლა"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"მობილური ინტერნეტი"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi გამორთულია"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth გამორთულია"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index ec4e0824d274..b5b3475b6ca1 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -26,8 +26,8 @@ <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Os ecrãs recentes aparecem aqui"</string> <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignorar aplicações recentes"</string> <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759"> - <item quantity="one">1 ecrã na Vista geral</item> <item quantity="other">%d ecrãs na Vista geral</item> + <item quantity="one">1 ecrã na Vista geral</item> </plurals> <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string> @@ -249,8 +249,8 @@ <string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string> <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string> <plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404"> - <item quantity="one">Mais <xliff:g id="NUMBER_0">%s</xliff:g> notificação no grupo.</item> <item quantity="other">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item> + <item quantity="one">Mais <xliff:g id="NUMBER_0">%s</xliff:g> notificação no grupo.</item> </plurals> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Definições de notificação"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Definições do <xliff:g id="APP_NAME">%s</xliff:g>"</string> @@ -551,13 +551,13 @@ <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicação não tem categorias de notificação"</string> <string name="notification_unblockable_desc" msgid="3561016061737896906">"Não é possível desativar as notificações desta aplicação"</string> <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item> <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação desta aplicação</item> + <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item> </plurals> <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item> <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e mais <xliff:g id="NUMBER_5">%3$d</xliff:g></item> + <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item> </plurals> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string> @@ -572,12 +572,12 @@ <string name="snooze_undo" msgid="6074877317002985129">"ANULAR"</string> <string name="snoozed_for_time" msgid="2390718332980204462">"Suspensa por <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string> <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030"> - <item quantity="one">%d hora</item> <item quantity="other">%d horas</item> + <item quantity="one">%d hora</item> </plurals> <plurals name="snoozeMinuteOptions" formatted="false" msgid="4127251700591510196"> - <item quantity="one">%d minuto</item> <item quantity="other">%d minutos</item> + <item quantity="one">%d minuto</item> </plurals> <string name="battery_panel_title" msgid="7944156115535366613">"Utiliz. da bateria"</string> <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string> @@ -756,7 +756,8 @@ <string name="instant_apps" msgid="6647570248119804907">"Aplicações instantâneas"</string> <string name="instant_apps_message" msgid="8116608994995104836">"As Aplicações instantâneas não requerem instalação."</string> <string name="app_info" msgid="6856026610594615344">"Informações da aplicação"</string> - <string name="go_to_web" msgid="1106022723459948514">"Aceder à Web"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desativado"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desativado"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 5b9ff78d9dc5..95cd2d078817 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -770,7 +770,8 @@ <string name="instant_apps" msgid="6647570248119804907">"Додатки з миттєвим запуском"</string> <string name="instant_apps_message" msgid="8116608994995104836">"Додатки з миттєвим запуском не потрібно встановлювати."</string> <string name="app_info" msgid="6856026610594615344">"Про додаток"</string> - <string name="go_to_web" msgid="1106022723459948514">"Перейти на веб-сайт"</string> + <!-- no translation found for go_to_web (2650669128861626071) --> + <skip /> <string name="mobile_data" msgid="7094582042819250762">"Мобільний трафік"</string> <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi вимкнено"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth вимкнено"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 8e065d1bf06b..5e7f9c6a40e5 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -316,6 +316,40 @@ <!-- SystemUIFactory component --> <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string> + <!-- SystemUI Services: The classes of the stuff to start. --> + <string-array name="config_systemUIServiceComponents" translatable="false"> + <item>com.android.systemui.Dependency</item> + <item>com.android.systemui.util.NotificationChannels</item> + <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item> + <item>com.android.systemui.keyguard.KeyguardViewMediator</item> + <item>com.android.systemui.recents.Recents</item> + <item>com.android.systemui.volume.VolumeUI</item> + <item>com.android.systemui.stackdivider.Divider</item> + <item>com.android.systemui.SystemBars</item> + <item>com.android.systemui.usb.StorageNotification</item> + <item>com.android.systemui.power.PowerUI</item> + <item>com.android.systemui.media.RingtonePlayer</item> + <item>com.android.systemui.keyboard.KeyboardUI</item> + <item>com.android.systemui.pip.PipUI</item> + <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> + <item>@string/config_systemUIVendorServiceComponent</item> + <item>com.android.systemui.util.leak.GarbageMonitor$Service</item> + <item>com.android.systemui.LatencyTester</item> + <item>com.android.systemui.globalactions.GlobalActionsComponent</item> + <item>com.android.systemui.RoundedCorners</item> + <item>com.android.systemui.EmulatedDisplayCutout</item> + </string-array> + + <!-- SystemUI vender service, used in config_systemUIServiceComponents. --> + <string name="config_systemUIVendorServiceComponent" translatable="false">com.android.systemui.VendorServices</string> + + <!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents --> + <string-array name="config_systemUIServiceComponentsPerUser" translatable="false"> + <item>com.android.systemui.Dependency</item> + <item>com.android.systemui.util.NotificationChannels</item> + <item>com.android.systemui.recents.Recents</item> + </string-array> + <!-- Nav bar button default ordering/layout --> <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 659c5a8b03c0..62ab74d50713 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1248,6 +1248,10 @@ <string name="stream_tts" translatable="false">Transmitted Through Speaker</string> <!-- STREAM_TTS --> <string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY --> + <string name="volume_ringer_status_normal">Ring</string> + <string name="volume_ringer_status_vibrate">Vibrate</string> + <string name="volume_ringer_status_silent">Mute</string> + <string name="volume_stream_muted" translatable="false">%s silent</string> <string name="volume_stream_vibrate" translatable="false">%s vibrate</string> <string name="volume_stream_suppressed" translatable="false">%1$s silent — %2$s</string> diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk index ce3b4424de50..239a4e37e832 100644 --- a/packages/SystemUI/shared/tests/Android.mk +++ b/packages/SystemUI/shared/tests/Android.mk @@ -38,7 +38,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-target-minus-junit4 \ SystemUI-proto \ SystemUI-tags \ - legacy-android-test \ testables \ truth-prebuilt \ @@ -50,4 +49,4 @@ LOCAL_CERTIFICATE := platform ifeq ($(EXCLUDE_SYSTEMUI_TESTS),) include $(BUILD_PACKAGE) -endif
\ No newline at end of file +endif diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index 432b4061b5d0..6e0b56e25a3f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -53,8 +53,13 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { private ProgressDialog mSimUnlockProgressDialog = null; private CheckSimPin mCheckSimPinThread; + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PIN attempts left. + private boolean mShowDefaultMessage = true; + private int mRemainingAttempts = -1; private AlertDialog mRemainingAttemptsDialog; - private int mSubId; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private ImageView mSimImageView; KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -91,32 +96,69 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { public void resetState() { super.resetState(); if (DEBUG) Log.v(TAG, "Resetting state"); - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); - mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED); + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); + } boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - if (SubscriptionManager.isValidSubscriptionId(mSubId)) { - int count = TelephonyManager.getDefault().getSimCount(); - Resources rez = getResources(); - String msg; - int color = Color.WHITE; - if (count < 2) { - msg = rez.getString(R.string.kg_sim_pin_instructions); - } else { - SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash - msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } + + KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); + esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); + } + + private void showDefaultMessage() { + if (mRemainingAttempts >= 0) { + mSecurityMessageDisplay.setMessage(getPinPasswordErrorMessage( + mRemainingAttempts, true)); + return; + } + + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + int count = TelephonyManager.getDefault().getSimCount(); + Resources rez = getResources(); + String msg; + int color = Color.WHITE; + if (count < 2) { + msg = rez.getString(R.string.kg_sim_pin_instructions); + } else { + SubscriptionInfo info = KeyguardUpdateMonitor.getInstance(mContext). + getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash + msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); + if (info != null) { + color = info.getIconTint(); } - if (isEsimLocked) { - msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim); + } + + if (isEsimLocked) { + msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim); + } + + mSecurityMessageDisplay.setMessage(msg); + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + + // Sending empty PIN here to query the number of remaining PIN attempts + new CheckSimPin("", mSubId) { + void onSimCheckResponse(final int result, final int attemptsRemaining) { + Log.d(LOG_TAG, "onSimCheckResponse " + " dummy One result" + result + + " attemptsRemaining=" + attemptsRemaining); + if (attemptsRemaining >= 0) { + mRemainingAttempts = attemptsRemaining; + mSecurityMessageDisplay.setMessage( + getPinPasswordErrorMessage(attemptsRemaining, true)); + } } - mSecurityMessageDisplay.setMessage(msg); - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + int subId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; } - KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); } @Override @@ -131,17 +173,19 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { return 0; } - private String getPinPasswordErrorMessage(int attemptsRemaining) { + private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { String displayMessage; - + int msgId; if (attemptsRemaining == 0) { displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked); } else if (attemptsRemaining > 0) { + msgId = isDefault ? R.plurals.kg_password_default_pin_message : + R.plurals.kg_password_wrong_pin_code; displayMessage = getContext().getResources() - .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining, - attemptsRemaining); + .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); } else { - displayMessage = getContext().getString(R.string.kg_password_pin_failed); + msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; + displayMessage = getContext().getString(msgId); } if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:" + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); @@ -252,7 +296,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } private Dialog getSimRemainingAttemptsDialog(int remaining) { - String msg = getPinPasswordErrorMessage(remaining); + String msg = getPinPasswordErrorMessage(remaining, false); if (mRemainingAttemptsDialog == null) { Builder builder = new AlertDialog.Builder(mContext); builder.setMessage(msg); @@ -288,6 +332,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { post(new Runnable() { @Override public void run() { + mRemainingAttempts = attemptsRemaining; if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } @@ -296,8 +341,13 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { if (result == PhoneConstants.PIN_RESULT_SUCCESS) { KeyguardUpdateMonitor.getInstance(getContext()) .reportSimUnlocked(mSubId); - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } } else { + mShowDefaultMessage = false; if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { if (attemptsRemaining <= 2) { // this is getting critical - show dialog @@ -305,7 +355,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } else { // show message mSecurityMessageDisplay.setMessage( - getPinPasswordErrorMessage(attemptsRemaining)); + getPinPasswordErrorMessage(attemptsRemaining, false)); } } else { // "PIN operation failed!" - no idea what this was and no way to diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 7f79008b7c91..876d170e08cd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -52,11 +52,17 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { private ProgressDialog mSimUnlockProgressDialog = null; private CheckSimPuk mCheckSimPukThread; + + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PUK attempts left. + private boolean mShowDefaultMessage = true; + private int mRemainingAttempts = -1; private String mPukText; private String mPinText; private StateMachine mStateMachine = new StateMachine(); private AlertDialog mRemainingAttemptsDialog; - private int mSubId; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private ImageView mSimImageView; KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -132,34 +138,17 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } } + void reset() { mPinText=""; mPukText=""; state = ENTER_PUK; - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); - mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED); - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - if (SubscriptionManager.isValidSubscriptionId(mSubId)) { - int count = TelephonyManager.getDefault().getSimCount(); - Resources rez = getResources(); - String msg; - int color = Color.WHITE; - if (count < 2) { - msg = rez.getString(R.string.kg_puk_enter_puk_hint); - } else { - SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; - msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim); - } - mSecurityMessageDisplay.setMessage(msg); - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); } + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); mPasswordEntry.requestFocus(); @@ -168,23 +157,79 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } + private void showDefaultMessage() { + if (mRemainingAttempts >= 0) { + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + mRemainingAttempts, true)); + return; + } + + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + int count = TelephonyManager.getDefault().getSimCount(); + Resources rez = getResources(); + String msg; + int color = Color.WHITE; + if (count < 2) { + msg = rez.getString(R.string.kg_puk_enter_puk_hint); + } else { + SubscriptionInfo info = KeyguardUpdateMonitor.getInstance(mContext). + getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; + msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim); + } + mSecurityMessageDisplay.setMessage(msg); + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + + // Sending empty PUK here to query the number of remaining PIN attempts + new CheckSimPuk("", "", mSubId) { + void onSimLockChangedResponse(final int result, final int attemptsRemaining) { + Log.d(LOG_TAG, "onSimCheckResponse " + " dummy One result" + result + + " attemptsRemaining=" + attemptsRemaining); + if (attemptsRemaining >= 0) { + mRemainingAttempts = attemptsRemaining; + mSecurityMessageDisplay.setMessage( + getPukPasswordErrorMessage(attemptsRemaining, true)); + } + } + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + int subId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } + } + @Override protected int getPromtReasonStringRes(int reason) { // No message on SIM Puk return 0; } - private String getPukPasswordErrorMessage(int attemptsRemaining) { + private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { String displayMessage; if (attemptsRemaining == 0) { displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead); } else if (attemptsRemaining > 0) { + int msgId = isDefault ? R.plurals.kg_password_default_puk_message : + R.plurals.kg_password_wrong_puk_code; displayMessage = getContext().getResources() - .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining, - attemptsRemaining); + .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); } else { - displayMessage = getContext().getString(R.string.kg_password_puk_failed); + int msgId = isDefault ? R.string.kg_puk_enter_puk_hint : + R.string.kg_password_puk_failed; + displayMessage = getContext().getString(msgId); } if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); @@ -303,7 +348,7 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } private Dialog getPukRemainingAttemptsDialog(int remaining) { - String msg = getPukPasswordErrorMessage(remaining); + String msg = getPukPasswordErrorMessage(remaining, false); if (mRemainingAttemptsDialog == null) { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setMessage(msg); @@ -359,16 +404,25 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (result == PhoneConstants.PIN_RESULT_SUCCESS) { KeyguardUpdateMonitor.getInstance(getContext()) .reportSimUnlocked(mSubId); - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } } else { + mShowDefaultMessage = false; if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { + // show message + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + attemptsRemaining, false)); if (attemptsRemaining <= 2) { // this is getting critical - show dialog getPukRemainingAttemptsDialog(attemptsRemaining).show(); } else { // show message mSecurityMessageDisplay.setMessage( - getPukPasswordErrorMessage(attemptsRemaining)); + getPukPasswordErrorMessage( + attemptsRemaining, false)); } } else { mSecurityMessageDisplay.setMessage(getContext().getString( diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java new file mode 100644 index 000000000000..edd1748c7380 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.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.systemui; + +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.DisplayCutout; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowInsets; +import android.view.WindowManager; + +import java.util.ArrayList; + +/** + * Emulates a display cutout by drawing its shape in an overlay as supplied by + * {@link DisplayCutout}. + */ +public class EmulatedDisplayCutout extends SystemUI { + private View mOverlay; + private boolean mAttached; + private WindowManager mWindowManager; + + @Override + public void start() { + mWindowManager = mContext.getSystemService(WindowManager.class); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.EMULATE_DISPLAY_CUTOUT), + false, mObserver, UserHandle.USER_ALL); + mObserver.onChange(false); + } + + private void setAttached(boolean attached) { + if (attached && !mAttached) { + if (mOverlay == null) { + mOverlay = new CutoutView(mContext); + mOverlay.setLayoutParams(getLayoutParams()); + } + mWindowManager.addView(mOverlay, mOverlay.getLayoutParams()); + mAttached = true; + } else if (!attached && mAttached) { + mWindowManager.removeView(mOverlay); + mAttached = false; + } + } + + private WindowManager.LayoutParams getLayoutParams() { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS + | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; + lp.setTitle("EmulatedDisplayCutout"); + lp.gravity = Gravity.TOP; + return lp; + } + + private ContentObserver mObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + boolean emulateCutout = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.EMULATE_DISPLAY_CUTOUT, + Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF) + != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF; + setAttached(emulateCutout); + } + }; + + private static class CutoutView extends View { + private Paint mPaint = new Paint(); + private Path mPath = new Path(); + private ArrayList<Point> mBoundingPolygon = new ArrayList<>(); + + CutoutView(Context context) { + super(context); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + insets.getDisplayCutout().getBoundingPolygon(mBoundingPolygon); + invalidate(); + return insets.consumeCutout(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (!mBoundingPolygon.isEmpty()) { + mPaint.setColor(Color.DKGRAY); + mPaint.setStyle(Paint.Style.FILL); + + mPath.reset(); + for (int i = 0; i < mBoundingPolygon.size(); i++) { + Point point = mBoundingPolygon.get(i); + if (i == 0) { + mPath.moveTo(point.x, point.y); + } else { + mPath.lineTo(point.x, point.y); + } + } + mPath.close(); + canvas.drawPath(mPath, mPaint); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 9adafda7adf5..3538327130d4 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -37,9 +37,7 @@ import com.android.systemui.keyboard.KeyboardUI; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.media.RingtonePlayer; import com.android.systemui.pip.PipUI; -import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.OverlayPlugin; -import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.power.PowerUI; @@ -54,6 +52,7 @@ import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.volume.VolumeUI; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -66,44 +65,9 @@ public class SystemUIApplication extends Application implements SysUiServiceProv private static final boolean DEBUG = false; /** - * The classes of the stuff to start. - */ - private final Class<?>[] SERVICES = new Class[] { - Dependency.class, - NotificationChannels.class, - CommandQueue.CommandQueueStart.class, - KeyguardViewMediator.class, - Recents.class, - VolumeUI.class, - Divider.class, - SystemBars.class, - StorageNotification.class, - PowerUI.class, - RingtonePlayer.class, - KeyboardUI.class, - PipUI.class, - ShortcutKeyDispatcher.class, - VendorServices.class, - GarbageMonitor.Service.class, - LatencyTester.class, - GlobalActionsComponent.class, - RoundedCorners.class, - }; - - /** - * The classes of the stuff to start for each user. This is a subset of the services listed - * above. - */ - private final Class<?>[] SERVICES_PER_USER = new Class[] { - Dependency.class, - NotificationChannels.class, - Recents.class - }; - - /** * Hold a reference on the stuff we start. */ - private final SystemUI[] mServices = new SystemUI[SERVICES.length]; + private SystemUI[] mServices; private boolean mServicesStarted; private boolean mBootCompleted; private final Map<Class<?>, Object> mComponents = new HashMap<>(); @@ -149,7 +113,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv // been broadcasted on startup for the primary SystemUI process. Instead, for // components which require the SystemUI component to be initialized per-user, we // start those components now for the current non-system user. - startServicesIfNeeded(SERVICES_PER_USER); + startSecondaryUserServicesIfNeeded(); } } @@ -161,7 +125,8 @@ public class SystemUIApplication extends Application implements SysUiServiceProv */ public void startServicesIfNeeded() { - startServicesIfNeeded(SERVICES); + String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); + startServicesIfNeeded(names); } /** @@ -171,13 +136,16 @@ public class SystemUIApplication extends Application implements SysUiServiceProv * <p>This method must only be called from the main thread.</p> */ void startSecondaryUserServicesIfNeeded() { - startServicesIfNeeded(SERVICES_PER_USER); + String[] names = + getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); + startServicesIfNeeded(names); } - private void startServicesIfNeeded(Class<?>[] services) { + private void startServicesIfNeeded(String[] services) { if (mServicesStarted) { return; } + mServices = new SystemUI[services.length]; if (!mBootCompleted) { // check to see if maybe it was already completed long before we began @@ -195,14 +163,16 @@ public class SystemUIApplication extends Application implements SysUiServiceProv log.traceBegin("StartServices"); final int N = services.length; for (int i = 0; i < N; i++) { - Class<?> cl = services[i]; - if (DEBUG) Log.d(TAG, "loading: " + cl); - log.traceBegin("StartServices" + cl.getSimpleName()); + String clsName = services[i]; + if (DEBUG) Log.d(TAG, "loading: " + clsName); + log.traceBegin("StartServices" + clsName); long ti = System.currentTimeMillis(); + Class cls; try { - - Object newService = SystemUIFactory.getInstance().createInstance(cl); - mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService); + cls = Class.forName(clsName); + mServices[i] = (SystemUI) cls.newInstance(); + } catch(ClassNotFoundException ex){ + throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { @@ -218,7 +188,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv // Warn if initialization of component takes too long ti = System.currentTimeMillis() - ti; if (ti > 1000) { - Log.w(TAG, "Initialization of " + cl.getName() + " took " + ti + " ms"); + Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms"); } if (mBootCompleted) { mServices[i].onBootCompleted(); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index f28096f083c2..45b11aac5f35 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -109,10 +109,6 @@ public class SystemUIFactory { return new QSTileHost(context, statusBar, iconController); } - public <T> T createInstance(Class<T> classType) { - return null; - } - public void injectDependencies(ArrayMap<Object, DependencyProvider> providers, Context context) { providers.put(NotificationLockscreenUserManager.class, diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 3b54e11447b7..5c8c3f356fae 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -333,7 +333,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { mItems.add(new RestartAction()); } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) { - if (mDevicePolicyManager.isLogoutButtonEnabled() + if (mDevicePolicyManager.isLogoutEnabled() && getCurrentUser().id != UserHandle.USER_SYSTEM) { mItems.add(new LogoutAction()); mHasLogoutButton = true; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 5fcd0061fa6f..83163316bac0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -161,10 +161,14 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Matrix matrix = new Matrix(); int overlayColor = 0x40FFFFFF; + // Bitmaps created for screenshots are hardware bitmaps. Copy to a software bitmap in order + // to update size for previews. + Bitmap swBitmap = data.image.copy(Bitmap.Config.ARGB_8888, true); + Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2); c.setBitmap(picture); - c.drawBitmap(data.image, matrix, paint); + c.drawBitmap(swBitmap, matrix, paint); c.drawColor(overlayColor); c.setBitmap(null); @@ -175,7 +179,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2, (iconSize - (scale * mImageHeight)) / 2); c.setBitmap(icon); - c.drawBitmap(data.image, matrix, paint); + c.drawBitmap(swBitmap, matrix, paint); c.drawColor(overlayColor); c.setBitmap(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java new file mode 100644 index 000000000000..6bcd174adeae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -0,0 +1,129 @@ +/* + * 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.statusbar; + +import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput; +import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; +import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_CHILD_NOTIFICATIONS; + +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins; + +/** + * This class handles listening to notification updates and passing them along to + * NotificationPresenter to be displayed to the user. + */ +public class NotificationListener extends NotificationListenerWithPlugins { + private static final String TAG = "NotificationListener"; + + private final NotificationPresenter mPresenter; + private final Context mContext; + + public NotificationListener(NotificationPresenter presenter, Context context) { + mPresenter = presenter; + mContext = context; + } + + @Override + public void onListenerConnected() { + if (DEBUG) Log.d(TAG, "onListenerConnected"); + onPluginConnected(); + final StatusBarNotification[] notifications = getActiveNotifications(); + if (notifications == null) { + Log.w(TAG, "onListenerConnected unable to get active notifications."); + return; + } + final RankingMap currentRanking = getCurrentRanking(); + mPresenter.getHandler().post(() -> { + for (StatusBarNotification sbn : notifications) { + mPresenter.addNotification(sbn, currentRanking); + } + }); + } + + @Override + public void onNotificationPosted(final StatusBarNotification sbn, + final RankingMap rankingMap) { + if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); + if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { + mPresenter.getHandler().post(() -> { + processForRemoteInput(sbn.getNotification(), mContext); + String key = sbn.getKey(); + mPresenter.getKeysKeptForRemoteInput().remove(key); + boolean isUpdate = mPresenter.getNotificationData().get(key) != null; + // In case we don't allow child notifications, we ignore children of + // notifications that have a summary, since` we're not going to show them + // anyway. This is true also when the summary is canceled, + // because children are automatically canceled by NoMan in that case. + if (!ENABLE_CHILD_NOTIFICATIONS + && mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) { + if (DEBUG) { + Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); + } + + // Remove existing notification to avoid stale data. + if (isUpdate) { + mPresenter.removeNotification(key, rankingMap); + } else { + mPresenter.getNotificationData().updateRanking(rankingMap); + } + return; + } + if (isUpdate) { + mPresenter.updateNotification(sbn, rankingMap); + } else { + mPresenter.addNotification(sbn, rankingMap); + } + }); + } + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, + final RankingMap rankingMap) { + if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); + if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) { + final String key = sbn.getKey(); + mPresenter.getHandler().post(() -> mPresenter.removeNotification(key, rankingMap)); + } + } + + @Override + public void onNotificationRankingUpdate(final RankingMap rankingMap) { + if (DEBUG) Log.d(TAG, "onRankingUpdate"); + if (rankingMap != null) { + RankingMap r = onPluginRankingUpdate(rankingMap); + mPresenter.getHandler().post(() -> mPresenter.updateNotificationRanking(r)); + } + } + + public void register() { + try { + registerAsSystemService(mContext, + new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), + UserHandle.USER_ALL); + } catch (RemoteException e) { + Log.e(TAG, "Unable to register notification listener", e); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index 867088793f8b..4eca2415d5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -19,6 +19,8 @@ import android.content.Intent; import android.os.Handler; import android.service.notification.NotificationListenerService; +import java.util.Set; + /** * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods * for both querying the state of the system (some modularised piece of functionality may @@ -26,7 +28,8 @@ import android.service.notification.NotificationListenerService; * for affecting the state of the system (e.g. starting an intent, given that the presenter may * want to perform some action before doing so). */ -public interface NotificationPresenter { +public interface NotificationPresenter extends NotificationUpdateHandler, + NotificationData.Environment { /** * Returns true if the presenter is not visible. For example, it may not be necessary to do @@ -66,12 +69,6 @@ public interface NotificationPresenter { */ void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation); - // TODO: Create NotificationUpdateHandler and move this method to there. - /** - * Removes a notification. - */ - void removeNotification(String key, NotificationListenerService.RankingMap ranking); - // TODO: Create NotificationEntryManager and move this method to there. /** * Gets the latest ranking map. @@ -84,6 +81,14 @@ public interface NotificationPresenter { void onWorkChallengeChanged(); /** + * Notifications in this set are kept around when they were canceled in response to a remote + * input interaction. This allows us to show what you replied and allows you to continue typing + * into it. + */ + // TODO: Create NotificationEntryManager and move this method to there. + Set<String> getKeysKeptForRemoteInput(); + + /** * Called when the current user changes. * @param newUserId new user id */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java new file mode 100644 index 000000000000..0044194e886c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java @@ -0,0 +1,58 @@ +/* + * 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.statusbar; + +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; + +/** + * Interface for accepting notification updates from {@link NotificationListener}. + */ +public interface NotificationUpdateHandler { + /** + * Add a new notification and update the current notification ranking map. + * + * @param notification Notification to add + * @param ranking RankingMap to update with + */ + void addNotification(StatusBarNotification notification, + NotificationListenerService.RankingMap ranking); + + /** + * Remove a notification and update the current notification ranking map. + * + * @param key Key identifying the notification to remove + * @param ranking RankingMap to update with + */ + void removeNotification(String key, NotificationListenerService.RankingMap ranking); + + /** + * Update a given notification and the current notification ranking map. + * + * @param notification Updated notification + * @param ranking RankingMap to update with + */ + void updateNotification(StatusBarNotification notification, + NotificationListenerService.RankingMap ranking); + + /** + * Update with a new notification ranking map. + * + * @param ranking RankingMap to update with + */ + void updateNotificationRanking(NotificationListenerService.RankingMap ranking); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java index ff6c775ce7ea..97e3d22c6a46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java @@ -21,17 +21,24 @@ import com.android.systemui.Dependency; import com.android.systemui.statusbar.phone.StatusBarWindowManager; import com.android.systemui.statusbar.policy.RemoteInputView; +import android.app.Notification; +import android.app.RemoteInput; +import android.content.Context; +import android.os.SystemProperties; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; /** * Keeps track of the currently active {@link RemoteInputView}s. */ public class RemoteInputController { + private static final boolean ENABLE_REMOTE_INPUT = + SystemProperties.getBoolean("debug.enable_remote_input", true); private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen = new ArrayList<>(); @@ -45,6 +52,53 @@ public class RemoteInputController { } /** + * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this + * via first-class API. + * + * TODO: Remove once enough apps specify remote inputs on their own. + */ + public static void processForRemoteInput(Notification n, Context context) { + if (!ENABLE_REMOTE_INPUT) { + return; + } + + if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") && + (n.actions == null || n.actions.length == 0)) { + Notification.Action viableAction = null; + Notification.WearableExtender we = new Notification.WearableExtender(n); + + List<Notification.Action> actions = we.getActions(); + final int numActions = actions.size(); + + for (int i = 0; i < numActions; i++) { + Notification.Action action = actions.get(i); + if (action == null) { + continue; + } + RemoteInput[] remoteInputs = action.getRemoteInputs(); + if (remoteInputs == null) { + continue; + } + for (RemoteInput ri : remoteInputs) { + if (ri.getAllowFreeFormInput()) { + viableAction = action; + break; + } + } + if (viableAction != null) { + break; + } + } + + if (viableAction != null) { + Notification.Builder rebuilder = Notification.Builder.recoverBuilder(context, n); + rebuilder.setActions(viableAction); + rebuilder.build(); // will rewrite n + } + } + } + + /** * Adds a currently active remote input. * * @param entry the entry for which a remote input is now active. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 3a36776304f2..168758f64b60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -195,6 +195,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void transitionTo(ScrimState state, Callback callback) { if (state == mState) { + // Call the callback anyway, unless it's already enqueued + if (callback != null && mCallback != callback) { + callback.onFinished(); + } return; } else if (DEBUG) { Log.d(TAG, "State changed to: " + state); @@ -204,12 +208,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, throw new IllegalArgumentException("Cannot change to UNINITIALIZED."); } + final ScrimState oldState = mState; + mState = state; + if (mCallback != null) { mCallback.onCancelled(); } mCallback = callback; - state.prepare(mState); + state.prepare(oldState); mScreenBlankingCallbackCalled = false; mAnimationDelay = 0; mBlankScreen = state.getBlanksScreen(); @@ -228,8 +235,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mKeyguardFadeoutAnimation.cancel(); } - mState = state; - // Do not let the device sleep until we're done with all animations if (!mWakeLockHeld) { if (mWakeLock != null) { @@ -310,7 +315,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mCurrentInFrontAlpha = 0; } } else { - Log.w(TAG, "Invalid state, cannot set panel expansion when: " + mState); return; } 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 c96742368715..0cfad1805dd2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -200,6 +200,7 @@ import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.NotificationGutsManager; import com.android.systemui.statusbar.NotificationInfo; +import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -249,6 +250,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Stack; public class StatusBar extends SystemUI implements DemoMode, @@ -654,13 +656,14 @@ public class StatusBar extends SystemUI implements DemoMode, + "mStatusBarKeyguardViewManager was null"); return; } - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + if (mKeyguardFadingAway) { + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + } } @Override public void onCancelled() { - // Transition was cancelled because another one took over. - // Nothing to do in here but wait. + onFinished(); } }; @@ -816,14 +819,8 @@ public class StatusBar extends SystemUI implements DemoMode, } // Set up the initial notification state. - try { - mNotificationListener.registerAsSystemService(mContext, - new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), - UserHandle.USER_ALL); - } catch (RemoteException e) { - Log.e(TAG, "Unable to register notification listener", e); - } - + mNotificationListener = new NotificationListener(this, mContext); + mNotificationListener.register(); if (DEBUG) { Log.d(TAG, String.format( @@ -1516,13 +1513,19 @@ public class StatusBar extends SystemUI implements DemoMode, SystemServicesProxy.getInstance(mContext).awakenDreamsAsync(); } - public void addNotification(StatusBarNotification notification, RankingMap ranking) - throws InflationException { + @Override + public void addNotification(StatusBarNotification notification, RankingMap ranking) { String key = notification.getKey(); if (DEBUG) Log.d(TAG, "addNotification key=" + key); mNotificationData.updateRanking(ranking); - Entry shadeEntry = createNotificationViews(notification); + Entry shadeEntry = null; + try { + shadeEntry = createNotificationViews(notification); + } catch (InflationException e) { + handleInflationException(notification, e); + return; + } boolean isHeadsUped = shouldPeek(shadeEntry); if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(key)) { @@ -1536,11 +1539,11 @@ public class StatusBar extends SystemUI implements DemoMode, + key); } } else { - // Stop screensaver if the notification has a full-screen intent. + // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) awakenDreams(); - // not immersive & a full-screen alert should be shown + // not immersive & a fullscreen alert should be shown if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); try { @@ -1617,7 +1620,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } - protected void updateNotificationRanking(RankingMap ranking) { + @Override + public void updateNotificationRanking(RankingMap ranking) { mNotificationData.updateRanking(ranking); updateNotifications(); } @@ -1670,7 +1674,7 @@ public class StatusBar extends SystemUI implements DemoMode, newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); boolean updated = false; try { - updateNotification(newSbn, null); + updateNotificationInternal(newSbn, null); updated = true; } catch (InflationException e) { deferRemoval = false; @@ -4137,14 +4141,10 @@ public class StatusBar extends SystemUI implements DemoMode, .setStartDelay(0) .setDuration(FADE_KEYGUARD_DURATION_PULSING) .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - hideKeyguard(); - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); - } - }) - .start(); + .withEndAction(()-> { + hideKeyguard(); + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + }).start(); } /** @@ -5705,90 +5705,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - private final NotificationListenerWithPlugins mNotificationListener = - new NotificationListenerWithPlugins() { - @Override - public void onListenerConnected() { - if (DEBUG) Log.d(TAG, "onListenerConnected"); - onPluginConnected(); - final StatusBarNotification[] notifications = getActiveNotifications(); - if (notifications == null) { - Log.w(TAG, "onListenerConnected unable to get active notifications."); - return; - } - final RankingMap currentRanking = getCurrentRanking(); - mHandler.post(() -> { - for (StatusBarNotification sbn : notifications) { - try { - addNotification(sbn, currentRanking); - } catch (InflationException e) { - handleInflationException(sbn, e); - } - } - }); - } - - @Override - public void onNotificationPosted(final StatusBarNotification sbn, - final RankingMap rankingMap) { - if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); - if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { - mHandler.post(() -> { - processForRemoteInput(sbn.getNotification()); - String key = sbn.getKey(); - mKeysKeptForRemoteInput.remove(key); - boolean isUpdate = mNotificationData.get(key) != null; - // In case we don't allow child notifications, we ignore children of - // notifications that have a summary, since we're not going to show them - // anyway. This is true also when the summary is canceled, - // because children are automatically canceled by NoMan in that case. - if (!ENABLE_CHILD_NOTIFICATIONS - && mGroupManager.isChildInGroupWithSummary(sbn)) { - if (DEBUG) { - Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); - } - - // Remove existing notification to avoid stale data. - if (isUpdate) { - removeNotification(key, rankingMap); - } else { - mNotificationData.updateRanking(rankingMap); - } - return; - } - try { - if (isUpdate) { - updateNotification(sbn, rankingMap); - } else { - addNotification(sbn, rankingMap); - } - } catch (InflationException e) { - handleInflationException(sbn, e); - } - }); - } - } - - @Override - public void onNotificationRemoved(StatusBarNotification sbn, - final RankingMap rankingMap) { - if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); - if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) { - final String key = sbn.getKey(); - mHandler.post(() -> removeNotification(key, rankingMap)); - } - } - - @Override - public void onNotificationRankingUpdate(final RankingMap rankingMap) { - if (DEBUG) Log.d(TAG, "onRankingUpdate"); - if (rankingMap != null) { - RankingMap r = onPluginRankingUpdate(rankingMap); - mHandler.post(() -> updateNotificationRanking(r)); - } - } - - }; + protected NotificationListener mNotificationListener; protected void notifyUserAboutHiddenNotifications() { if (0 != Settings.Secure.getInt(mContext.getContentResolver(), @@ -6075,51 +5992,6 @@ public class StatusBar extends SystemUI implements DemoMode, row.updateNotification(entry); } - /** - * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this - * via first-class API. - * - * TODO: Remove once enough apps specify remote inputs on their own. - */ - private void processForRemoteInput(Notification n) { - if (!ENABLE_REMOTE_INPUT) return; - - if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") && - (n.actions == null || n.actions.length == 0)) { - Notification.Action viableAction = null; - Notification.WearableExtender we = new Notification.WearableExtender(n); - - List<Notification.Action> actions = we.getActions(); - final int numActions = actions.size(); - - for (int i = 0; i < numActions; i++) { - Notification.Action action = actions.get(i); - if (action == null) { - continue; - } - RemoteInput[] remoteInputs = action.getRemoteInputs(); - if (remoteInputs == null) { - continue; - } - for (RemoteInput ri : remoteInputs) { - if (ri.getAllowFreeFormInput()) { - viableAction = action; - break; - } - } - if (viableAction != null) { - break; - } - } - - if (viableAction != null) { - Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n); - rebuilder.setActions(viableAction); - rebuilder.build(); // will rewrite n - } - } - } - public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { if (!isDeviceProvisioned()) return; @@ -6508,8 +6380,9 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount()); } - public void updateNotification(StatusBarNotification notification, RankingMap ranking) - throws InflationException { + // TODO: Move this to NotificationEntryManager once it is created. + private void updateNotificationInternal(StatusBarNotification notification, + RankingMap ranking) throws InflationException { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); @@ -6559,6 +6432,15 @@ public class StatusBar extends SystemUI implements DemoMode, setAreThereNotifications(); } + @Override + public void updateNotification(StatusBarNotification notification, RankingMap ranking) { + try { + updateNotificationInternal(notification, ranking); + } catch (InflationException e) { + handleInflationException(notification, e); + } + } + protected void notifyHeadsUpGoingToSleep() { maybeEscalateHeadsUp(); } @@ -6755,6 +6637,11 @@ public class StatusBar extends SystemUI implements DemoMode, return mLatestRankingMap; } + @Override + public Set<String> getKeysKeptForRemoteInput() { + return mKeysKeptForRemoteInput; + } + private final NotificationInfo.CheckSaveListener mCheckSaveListener = (Runnable saveImportance, StatusBarNotification sbn) -> { // If the user has security enabled, show challenge if the setting is changed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index dacd3d935d0a..8504d8e5fee5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -380,8 +380,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBar.fadeKeyguardWhilePulsing(); wakeAndUnlockDejank(); } else { - mFingerprintUnlockController.startKeyguardFadingAway(); - mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); boolean staying = mStatusBar.hideKeyguard(); if (!staying) { mStatusBarWindowManager.setKeyguardFadingAway(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 195607dce81f..7374f115a19b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -448,7 +448,7 @@ public class StackScrollAlgorithm { childState.height = Math.max(row.getIntrinsicHeight(), childState.height); childState.hidden = false; ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry); - if (!isTopEntry && (!mIsExpanded + if (topState != null && !isTopEntry && (!mIsExpanded || unmodifiedEndLocation < topState.yTranslation + topState.height)) { // Ensure that a headsUp doesn't vertically extend further than the heads-up at // the top most z-position diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 0d41e2029086..7959b72ce7b3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -29,12 +29,15 @@ import android.app.Dialog; import android.app.KeyguardManager; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.PackageManager; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Debug; @@ -43,6 +46,8 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.provider.Settings.Global; +import android.transition.AutoTransition; +import android.transition.TransitionManager; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -67,7 +72,6 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.systemui.Dependency; -import com.android.systemui.HardwareUiLayout; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.plugins.VolumeDialog; @@ -97,11 +101,12 @@ public class VolumeDialogImpl implements VolumeDialog { private final VolumeDialogController mController; private Window mWindow; - private HardwareUiLayout mHardwareLayout; private CustomDialog mDialog; private ViewGroup mDialogView; private ViewGroup mDialogRowsView; - private ViewGroup mDialogContentView; + private ImageButton mExpandButton; + private ImageButton mRingerIcon; + private TextView mRingerStatus; private final List<VolumeRow> mRows = new ArrayList<>(); private ConfigurableTexts mConfigurableTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); @@ -113,6 +118,8 @@ public class VolumeDialogImpl implements VolumeDialog { private final ColorStateList mInactiveSliderTint; private boolean mShowing; + private boolean mExpanded; + private boolean mExpandButtonAnimationRunning; private boolean mShowA11yStream; private int mActiveStream; @@ -160,54 +167,74 @@ public class VolumeDialogImpl implements VolumeDialog { mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - mWindow.addFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + mWindow.setTitle(VolumeDialogImpl.class.getSimpleName()); mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast); mDialog.setContentView(R.layout.volume_dialog); - mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog); - mDialogView.setOnHoverListener(new View.OnHoverListener() { - @Override - public boolean onHover(View v, MotionEvent event) { - int action = event.getActionMasked(); - mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) - || (action == MotionEvent.ACTION_HOVER_MOVE); - rescheduleTimeoutH(); - return true; - } + mDialog.setOnShowListener(dialog -> { + mDialogView.setTranslationY(-mDialogView.getHeight()); + mDialogView.setAlpha(0); + mDialogView.animate() + .alpha(1) + .translationY(0) + .setDuration(300) + .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) + .withEndAction(() -> { + mWindow.getDecorView().requestAccessibilityFocus(); + }) + .start(); + }); + mDialogView = mDialog.findViewById(R.id.volume_dialog); + mDialogView.setOnHoverListener((v, event) -> { + int action = event.getActionMasked(); + mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) + || (action == MotionEvent.ACTION_HOVER_MOVE); + rescheduleTimeoutH(); + return true; }); - mHardwareLayout = HardwareUiLayout.get(mDialogView); - mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); + VolumeUiLayout hardwareLayout = VolumeUiLayout.get(mDialogView); + hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); + + ViewGroup dialogContentView = mDialog.findViewById(R.id.volume_dialog_content); + mDialogRowsView = dialogContentView.findViewById(R.id.volume_dialog_rows); + mRingerIcon = mDialog.findViewById(R.id.ringer_icon); + mRingerStatus = mDialog.findViewById(R.id.ringer_status); - mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content); - mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows); + mExpanded = false; + mExpandButton = mDialogView.findViewById(R.id.volume_expand_button); + mExpandButton.setOnClickListener(mClickExpand); + mExpandButton.setVisibility( + AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE); if (mRows.isEmpty()) { addRow(AudioManager.STREAM_MUSIC, - R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true); + R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true); if (!AudioSystem.isSingleVolume(mContext)) { addRow(AudioManager.STREAM_RING, - R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true); + R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false); addRow(AudioManager.STREAM_ALARM, - R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, false); + R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false); addRow(AudioManager.STREAM_VOICE_CALL, - R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false); + R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false, false); addRow(AudioManager.STREAM_BLUETOOTH_SCO, - R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false); - addRow(AudioManager.STREAM_SYSTEM, - R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false); + R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false); + addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system, + R.drawable.ic_volume_system_mute, false, false); addRow(AudioManager.STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, - R.drawable.ic_volume_accessibility, true); + R.drawable.ic_volume_accessibility, true, false); } } else { addExistingRows(); } updateRowsH(getActiveRow()); + initRingerH(); } private ColorStateList loadColorStateList(int colorResId) { @@ -230,14 +257,15 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.sendEmptyMessage(H.RECHECK_ALL); } - private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) { - addRow(stream, iconRes, iconMuteRes, important, false); + private void addRow(int stream, int iconRes, int iconMuteRes, boolean important, + boolean defaultStream) { + addRow(stream, iconRes, iconMuteRes, important, defaultStream, false); } private void addRow(int stream, int iconRes, int iconMuteRes, boolean important, - boolean dynamic) { + boolean defaultStream, boolean dynamic) { VolumeRow row = new VolumeRow(); - initRow(row, stream, iconRes, iconMuteRes, important); + initRow(row, stream, iconRes, iconMuteRes, important, defaultStream); int rowSize; int viewSize; if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1 @@ -255,7 +283,8 @@ public class VolumeDialogImpl implements VolumeDialog { int N = mRows.size(); for (int i = 0; i < N; i++) { final VolumeRow row = mRows.get(i); - initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important); + initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important, + row.defaultStream); mDialogRowsView.addView(row.view); updateVolumeRowH(row); } @@ -280,6 +309,7 @@ public class VolumeDialogImpl implements VolumeDialog { public void dump(PrintWriter writer) { writer.println(VolumeDialogImpl.class.getSimpleName() + " state:"); writer.print(" mShowing: "); writer.println(mShowing); + writer.print(" mExpanded: "); writer.println(mExpanded); writer.print(" mActiveStream: "); writer.println(mActiveStream); writer.print(" mDynamic: "); writer.println(mDynamic); writer.print(" mAutomute: "); writer.println(mAutomute); @@ -298,11 +328,12 @@ public class VolumeDialogImpl implements VolumeDialog { @SuppressLint("InflateParams") private void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes, - boolean important) { + boolean important, boolean defaultStream) { row.stream = stream; row.iconRes = iconRes; row.iconMuteRes = iconMuteRes; row.important = important; + row.defaultStream = defaultStream; row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null); row.view.setId(row.stream); row.view.setTag(row); @@ -340,40 +371,61 @@ public class VolumeDialogImpl implements VolumeDialog { row.icon = row.view.findViewById(R.id.volume_row_icon); row.icon.setImageResource(iconRes); if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) { - row.icon.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState); - mController.setActiveStream(row.stream); - if (row.stream == AudioManager.STREAM_RING) { - final boolean hasVibrator = mController.hasVibrator(); - if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { - if (hasVibrator) { - mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); - } else { - final boolean wasZero = row.ss.level == 0; - mController.setStreamVolume(stream, - wasZero ? row.lastAudibleLevel : 0); - } + row.icon.setOnClickListener(v -> { + Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState); + mController.setActiveStream(row.stream); + if (row.stream == AudioManager.STREAM_RING) { + final boolean hasVibrator = mController.hasVibrator(); + if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { + if (hasVibrator) { + mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); } else { - mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); - if (row.ss.level == 0) { - mController.setStreamVolume(stream, 1); - } + final boolean wasZero = row.ss.level == 0; + mController.setStreamVolume(stream, + wasZero ? row.lastAudibleLevel : 0); } } else { - final boolean vmute = row.ss.level == row.ss.levelMin; - mController.setStreamVolume(stream, - vmute ? row.lastAudibleLevel : row.ss.levelMin); + mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); + if (row.ss.level == 0) { + mController.setStreamVolume(stream, 1); + } } - row.userAttempt = 0; // reset the grace period, slider updates immediately + } else { + final boolean vmute = row.ss.level == row.ss.levelMin; + mController.setStreamVolume(stream, + vmute ? row.lastAudibleLevel : row.ss.levelMin); } + row.userAttempt = 0; // reset the grace period, slider updates immediately }); } else { row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); } } + public void initRingerH() { + mRingerIcon.setOnClickListener(v -> { + Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING, + mRingerIcon.getTag()); + final StreamState ss = mState.states.get(AudioManager.STREAM_RING); + final boolean hasVibrator = mController.hasVibrator(); + if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { + if (hasVibrator) { + mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); + } else { + final boolean wasZero = ss.level == 0; + mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0); + } + } else { + mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); + if (ss.level == 0) { + mController.setStreamVolume(AudioManager.STREAM_RING, 1); + } + } + updateRingerH(); + }); + updateRingerH(); + } + public void show(int reason) { mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget(); } @@ -389,27 +441,12 @@ public class VolumeDialogImpl implements VolumeDialog { rescheduleTimeoutH(); if (mShowing) return; mShowing = true; - mHardwareLayout.setTranslationX(getAnimTranslation()); - mHardwareLayout.setAlpha(0); - mHardwareLayout.animate() - .alpha(1) - .translationX(0) - .setDuration(300) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .withEndAction(() -> { - mDialog.show(); - mWindow.getDecorView().requestAccessibilityFocus(); - }) - .start(); + + mDialog.show(); Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); mController.notifyVisible(true); } - private float getAnimTranslation() { - return mContext.getResources().getDimension( - R.dimen.volume_dialog_panel_width) / 2; - } - protected void rescheduleTimeoutH() { mHandler.removeMessages(H.DISMISS); final int timeout = computeTimeoutH(); @@ -422,8 +459,8 @@ public class VolumeDialogImpl implements VolumeDialog { private int computeTimeoutH() { if (mAccessibility.mFeedbackEnabled) return 20000; if (mHovering) return 16000; + if (mExpanded) return 5000; if (mSafetyWarning != null) return 5000; - if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500; return 3000; } @@ -431,16 +468,24 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.DISMISS); mHandler.removeMessages(H.SHOW); if (!mShowing) return; + mDialogView.animate().cancel(); mShowing = false; - mHardwareLayout.setTranslationX(0); - mHardwareLayout.setAlpha(1); - mHardwareLayout.animate() + + updateExpandedH(false /* expanding */, true /* dismissing */); + + mDialogView.setTranslationY(0); + mDialogView.setAlpha(1); + mDialogView.animate() .alpha(0) - .translationX(getAnimTranslation()) - .setDuration(300) - .withEndAction(() -> mDialog.dismiss()) + .translationY(-mDialogView.getHeight()) + .setDuration(250) .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) + .withEndAction(() -> mHandler.postDelayed(() -> { + if (D.BUG) Log.d(TAG, "mDialog.dismiss()"); + mDialog.dismiss(); + }, 50)) .start(); + if (mAccessibilityMgr.isEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); @@ -460,6 +505,76 @@ public class VolumeDialogImpl implements VolumeDialog { } } + private void updateExpandedH(final boolean expanded, final boolean dismissing) { + if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded); + + if (mExpanded == expanded) return; + mExpanded = expanded; + mExpandButtonAnimationRunning = isAttached(); + updateExpandButtonH(); + TransitionManager.endTransitions(mDialogView); + final VolumeRow activeRow = getActiveRow(); + if (!dismissing) { + mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT); + TransitionManager.beginDelayedTransition(mDialogView, getTransition()); + } + updateRowsH(activeRow); + rescheduleTimeoutH(); + } + + private AutoTransition getTransition() { + AutoTransition transition = new AutoTransition(); + transition.setDuration(300); + transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + return transition; + } + + private void updateExpandButtonH() { + if (D.BUG) Log.d(TAG, "updateExpandButtonH"); + + mExpandButton.setClickable(!mExpandButtonAnimationRunning); + if (!(mExpandButtonAnimationRunning && isAttached())) { + final int res = mExpanded ? R.drawable.ic_volume_collapse_animation + : R.drawable.ic_volume_expand_animation; + if (hasTouchFeature()) { + mExpandButton.setImageResource(res); + } else { + // if there is no touch feature, show the volume ringer instead + mExpandButton.setImageResource(R.drawable.ic_volume_ringer); + mExpandButton.setBackgroundResource(0); // remove gray background emphasis + } + mExpandButton.setContentDescription(mContext.getString(mExpanded ? + R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand)); + } + if (mExpandButtonAnimationRunning) { + final Drawable d = mExpandButton.getDrawable(); + if (d instanceof AnimatedVectorDrawable) { + // workaround to reset drawable + final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState() + .newDrawable(); + mExpandButton.setImageDrawable(avd); + avd.start(); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mExpandButtonAnimationRunning = false; + updateExpandButtonH(); + rescheduleTimeoutH(); + } + }, 300); + } + } + } + + private boolean isAttached() { + return mDialogView != null && mDialogView.isAttachedToWindow(); + } + + private boolean hasTouchFeature() { + final PackageManager pm = mContext.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN); + } + private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) { boolean isActive = row == activeRow; if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) { @@ -473,7 +588,7 @@ public class VolumeDialogImpl implements VolumeDialog { return true; } - return row.important || isActive; + return row.defaultStream || isActive || (mExpanded && row.important); } private void updateRowsH(final VolumeRow activeRow) { @@ -493,6 +608,53 @@ public class VolumeDialogImpl implements VolumeDialog { } } + protected void updateRingerH() { + if (mState != null) { + final StreamState ss = mState.states.get(AudioManager.STREAM_RING); + switch (mState.ringerModeInternal) { + case AudioManager.RINGER_MODE_VIBRATE: + mRingerStatus.setText(R.string.volume_ringer_status_vibrate); + mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); + mRingerIcon.setTag(Events.ICON_STATE_VIBRATE); + break; + case AudioManager.RINGER_MODE_SILENT: + mRingerStatus.setText(R.string.volume_ringer_status_silent); + mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mRingerIcon.setContentDescription(mContext.getString( + R.string.volume_stream_content_description_unmute, + getStreamLabelH(ss))); + mRingerIcon.setTag(Events.ICON_STATE_MUTE); + break; + case AudioManager.RINGER_MODE_NORMAL: + default: + boolean muted = (mAutomute && ss.level == 0) || ss.muted; + if (muted) { + mRingerStatus.setText(R.string.volume_ringer_status_silent); + mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mRingerIcon.setContentDescription(mContext.getString( + R.string.volume_stream_content_description_unmute, + getStreamLabelH(ss))); + mRingerIcon.setTag(Events.ICON_STATE_MUTE); + } else { + mRingerStatus.setText(R.string.volume_ringer_status_normal); + mRingerIcon.setImageResource(R.drawable.ic_volume_ringer); + if (mController.hasVibrator()) { + mRingerIcon.setContentDescription(mContext.getString( + mShowA11yStream + ? R.string.volume_stream_content_description_vibrate_a11y + : R.string.volume_stream_content_description_vibrate, + getStreamLabelH(ss))); + + } else { + mRingerIcon.setContentDescription(getStreamLabelH(ss)); + } + mRingerIcon.setTag(Events.ICON_STATE_UNMUTE); + } + break; + } + } + } + private void trimObsoleteH() { if (D.BUG) Log.d(TAG, "trimObsoleteH"); for (int i = mRows.size() - 1; i >= 0; i--) { @@ -516,7 +678,7 @@ public class VolumeDialogImpl implements VolumeDialog { mDynamic.put(stream, true); if (findRow(stream) == null) { addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true, - true); + false, true); } } @@ -529,6 +691,7 @@ public class VolumeDialogImpl implements VolumeDialog { for (VolumeRow row : mRows) { updateVolumeRowH(row); } + updateRingerH(); } private void updateVolumeRowH(VolumeRow row) { @@ -762,6 +925,16 @@ public class VolumeDialogImpl implements VolumeDialog { } } + private final OnClickListener mClickExpand = new OnClickListener() { + @Override + public void onClick(View v) { + mExpandButton.animate().cancel(); + final boolean newExpand = !mExpanded; + Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand); + updateExpandedH(newExpand, false /* dismissing */); + } + }; + private final VolumeDialogController.Callbacks mControllerCallbackH = new VolumeDialogController.Callbacks() { @Override @@ -817,8 +990,7 @@ public class VolumeDialogImpl implements VolumeDialog { @Override public void onAccessibilityModeChanged(Boolean showA11yStream) { - boolean show = showA11yStream == null ? false : showA11yStream; - mShowA11yStream = show; + mShowA11yStream = showA11yStream == null ? false : showA11yStream; VolumeRow activeRow = getActiveRow(); if (!mShowA11yStream && AudioManager.STREAM_ACCESSIBILITY == activeRow.stream) { dismissH(Events.DISMISS_STREAM_GONE); @@ -1019,6 +1191,7 @@ public class VolumeDialogImpl implements VolumeDialog { private int iconRes; private int iconMuteRes; private boolean important; + private boolean defaultStream; private ColorStateList cachedSliderTint; private int iconState; // from Events private ObjectAnimator anim; // slider progress animation for non-touch-related updates diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java new file mode 100644 index 000000000000..49ac9b6b7ffe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java @@ -0,0 +1,66 @@ +/* + * 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.volume; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; + +public class VolumeUiLayout extends FrameLayout { + + public VolumeUiLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); + } + + @Override + public ViewOutlineProvider getOutlineProvider() { + return super.getOutlineProvider(); + } + + public void setOutsideTouchListener(OnClickListener onClickListener) { + requestLayout(); + setOnClickListener(onClickListener); + setClickable(true); + setFocusable(true); + } + + public static VolumeUiLayout get(View v) { + if (v instanceof VolumeUiLayout) return (VolumeUiLayout) v; + if (v.getParent() instanceof View) { + return get((View) v.getParent()); + } + return null; + } + + private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> { + inoutInfo.setTouchableInsets( + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); + }; +} diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index b695919dc2b5..9d44895dbe8d 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -54,11 +54,15 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-target-minus-junit4 \ SystemUI-proto \ SystemUI-tags \ - legacy-android-test \ testables \ truth-prebuilt \ -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + telephony-common \ + android.car \ + android.test.base \ + LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java new file mode 100644 index 000000000000..6ecfe3e6be47 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -0,0 +1,114 @@ +/* + * 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.statusbar; + +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashSet; +import java.util.Set; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationListenerTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test"; + private static final int TEST_UID = 0; + + private NotificationPresenter mPresenter; + private Handler mHandler; + private NotificationListener mListener; + private StatusBarNotification mSbn; + private NotificationListenerService.RankingMap mRanking; + private Set<String> mKeysKeptForRemoteInput; + private NotificationData mNotificationData; + + @Before + public void setUp() { + mHandler = new Handler(Looper.getMainLooper()); + mPresenter = mock(NotificationPresenter.class); + mNotificationData = mock(NotificationData.class); + mRanking = mock(NotificationListenerService.RankingMap.class); + mKeysKeptForRemoteInput = new HashSet<>(); + + when(mPresenter.getHandler()).thenReturn(mHandler); + when(mPresenter.getNotificationData()).thenReturn(mNotificationData); + when(mPresenter.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput); + + mListener = new NotificationListener(mPresenter, mContext); + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + } + + @Test + public void testNotificationAddCallsAddNotification() { + mListener.onNotificationPosted(mSbn, mRanking); + waitForIdleSync(mHandler); + verify(mPresenter).addNotification(mSbn, mRanking); + } + + @Test + public void testPostNotificationRemovesKeyKeptForRemoteInput() { + mKeysKeptForRemoteInput.add(mSbn.getKey()); + mListener.onNotificationPosted(mSbn, mRanking); + waitForIdleSync(mHandler); + assertTrue(mKeysKeptForRemoteInput.isEmpty()); + } + + @Test + public void testNotificationUpdateCallsUpdateNotification() { + when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn)); + mListener.onNotificationPosted(mSbn, mRanking); + waitForIdleSync(mHandler); + verify(mPresenter).updateNotification(mSbn, mRanking); + } + + @Test + public void testNotificationRemovalCallsRemoveNotification() { + mListener.onNotificationRemoved(mSbn, mRanking); + waitForIdleSync(mHandler); + verify(mPresenter).removeNotification(mSbn.getKey(), mRanking); + } + + @Test + public void testRankingUpdateCallsNotificationRankingUpdate() { + mListener.onNotificationRankingUpdate(mRanking); + waitForIdleSync(mHandler); + // RankingMap may be modified by plugins. + verify(mPresenter).updateNotificationRanking(any()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index b9f695be90cf..37dcaa8bd55c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -229,6 +229,15 @@ public class ScrimControllerTest extends SysuiTestCase { verify(mWakeLock, times(1)).release(); } + @Test + public void testCallbackInvokedOnSameStateTransition() { + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.finishAnimationsImmediately(); + ScrimController.Callback callback = mock(ScrimController.Callback.class); + mScrimController.transitionTo(ScrimState.UNLOCKED, callback); + verify(callback, times(1)).onFinished(); + } + private void assertScrimTint(ScrimView scrimView, boolean tinted) { final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT; final String name = scrimView == mScrimInFront ? "front" : "back"; diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml index 1862bb7816bc..56812d59e106 100644 --- a/packages/VpnDialogs/res/values-bs/strings.xml +++ b/packages/VpnDialogs/res/values-bs/strings.xml @@ -24,7 +24,7 @@ <string name="data_transmitted" msgid="7988167672982199061">"Poslano:"</string> <string name="data_received" msgid="4062776929376067820">"Primljeno:"</string> <string name="data_value_format" msgid="2192466557826897580">"Broj bajtova: <xliff:g id="NUMBER_0">%1$s</xliff:g>/Broj paketa: <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string> - <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ne može se povezati na stalno uključen VPN"</string> + <string name="always_on_disconnected_title" msgid="1906740176262776166">"Nije se moguće povezati na stalno uključen VPN"</string> <string name="always_on_disconnected_message" msgid="555634519845992917">"Aplikacija <xliff:g id="VPN_APP_0">%1$s</xliff:g> je postavljena da ostane povezana sve vrijeme, ali se trenutno ne može povezati. Telefon će koristiti javnu mrežu dok se ponovo ne mogne povezati s aplikacijom <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string> <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Aplikacija <xliff:g id="VPN_APP">%1$s</xliff:g> je postavljena da ostane povezana sve vrijeme, ali se trenutno ne može povezati. Nećete biti imati vezu dok se VPN ne mogne ponovo povezati."</string> <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string> diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk index 1dea798e83b2..bfc85ab5ca84 100644 --- a/packages/WAPPushManager/tests/Android.mk +++ b/packages/WAPPushManager/tests/Android.mk @@ -18,8 +18,8 @@ include $(CLEAR_VARS) # We only want this apk build for tests. LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index ba6bb23e9dca..935b787250fb 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3953,6 +3953,10 @@ message MetricsEvent { // An autofill session was started // Package: Package of app that is autofilled + // NOTE: starting on OS MR1, it also added the following field: + // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request + // NOTE: starting on OS P, it also added the following field: + // Tag FIELD_FLAGS - Flags used to start the session AUTOFILL_SESSION_STARTED = 906; // An autofill request was processed by a service @@ -3960,7 +3964,11 @@ message MetricsEvent { // Type TYPE_FAILURE: The request failed // Package: Package of app that is autofilled // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request - // Tag FIELD_AUTOFILL_NUM_DATASET: The number of datasets returned (only in success case) + // Tag FIELD_AUTOFILL_NUM_DATASETS: The number of datasets returned (only in success case) + // NOTE: starting on OS P, it also added: + // Type TYPE_CLOSE: Service returned a null response. + // Tag FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS: if service requested field classification, + // number of entries field ids in the request. AUTOFILL_REQUEST = 907; // Tag of a field for a package of an autofill service @@ -4863,7 +4871,6 @@ message MetricsEvent { // Counter: duration (in ms) that autofill will be disabled // OS: P // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request - // Tag FIELD_CLASS_NAME: Name of the Package of service that processed the request AUTOFILL_SERVICE_DISABLED_APP = 1231; // An autofill service asked to disable autofill for a given activity. @@ -5067,6 +5074,26 @@ message MetricsEvent { // OS: P NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270; + // Tag of a field for the number of ids in an autofill field classification request. + FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS = 1271; + + // An autofill service updated its user data + // Package: Package of the autofill service that updated the user data + // Counter: number of fields added (or 0 if reset) + // OS: P + AUTOFILL_USERDATA_UPDATED = 1272; + + // Some data entered by the user matched the field classification requested by the service. + // Package: Package of app that is autofilled + // Counter: number of matches found + // OS: P + // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request + // Tag FIELD_AUTOFILL_MATCH_SCORE: Average score of the matches, in the range of 0 to 100 + AUTOFILL_FIELD_CLASSIFICATION_MATCHES = 1273; + + // Tag used to report autofill field classification scores + FIELD_AUTOFILL_MATCH_SCORE = 1274; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 75d080d70423..d817da53f523 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -187,6 +187,12 @@ message SystemMessage { // to a Wi-Fi network NOTE_WIFI_WRONG_PASSWORD = 42; + // Inform the user that Wifi Wake will re-enable wifi when possible + NOTE_WIFI_WAKE_ONBOARD = 43; + + // Inform the user that Wifi Wake has automatically re-enabled Wifi + NOTE_WIFI_WAKE_TURNED_BACK_ON = 44; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk index d3fbd0569e78..e0e490c146a4 100644 --- a/sax/tests/saxtests/Android.mk +++ b/sax/tests/saxtests/Android.mk @@ -7,8 +7,8 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := FrameworksSaxTests include $(BUILD_PACKAGE) diff --git a/services/Android.bp b/services/Android.bp index 84c45fe7f6bd..5e75c371d171 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -1,3 +1,46 @@ +// merge all required services into one jar +// ============================================================ +java_library { + name: "services", + + dex_preopt: { + app_image: true, + profile: "art-profile", + }, + + srcs: [ + "java/**/*.java", + ], + + // The convention is to name each service module 'services.$(module_name)' + static_libs: [ + "services.core.priorityboosted", + "services.accessibility", + "services.appwidget", + "services.autofill", + "services.backup", + "services.companion", + "services.coverage", + "services.devicepolicy", + "services.midi", + "services.net", + "services.print", + "services.restrictions", + "services.usage", + "services.usb", + "services.voiceinteraction", + "android.hidl.base-V1.0-java", + ], + + libs: [ + "android.hidl.manager-V1.0-java", + ], + + // Uncomment to enable output of certain warnings (deprecated, unchecked) + //javacflags: ["-Xlint"], + +} + // native library // ============================================================= diff --git a/services/Android.mk b/services/Android.mk deleted file mode 100644 index 81d8181077b8..000000000000 --- a/services/Android.mk +++ /dev/null @@ -1,67 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# merge all required services into one jar -# ============================================================ -include $(CLEAR_VARS) - -LOCAL_MODULE := services -LOCAL_DEX_PREOPT_APP_IMAGE := true -LOCAL_DEX_PREOPT_GENERATE_PROFILE := true -LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING := $(LOCAL_PATH)/art-profile - -LOCAL_SRC_FILES := $(call all-java-files-under,java) - -# EventLogTags files. -LOCAL_SRC_FILES += \ - core/java/com/android/server/EventLogTags.logtags - -# Uncomment to enable output of certain warnings (deprecated, unchecked) -# LOCAL_JAVACFLAGS := -Xlint - -# Services that will be built as part of services.jar -# These should map to directory names relative to this -# Android.mk. -services := \ - core \ - accessibility \ - appwidget \ - autofill \ - backup \ - companion \ - coverage\ - devicepolicy \ - midi \ - net \ - print \ - restrictions \ - usage \ - usb \ - voiceinteraction - -# The convention is to name each service module 'services.$(module_name)' -LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services)) \ - android.hidl.base-V1.0-java \ - android.hardware.biometrics.fingerprint-V2.1-java - -LOCAL_JAVA_LIBRARIES := \ - android.hidl.manager-V1.0-java - -ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true) -LOCAL_EMMA_INSTRUMENT := true -endif - -include $(BUILD_JAVA_LIBRARY) - -# ============================================================= - -ifeq (,$(ONE_SHOT_MAKEFILE)) -# A full make is happening, so make everything. -include $(call all-makefiles-under,$(LOCAL_PATH)) -else -# If we ran an mm[m] command, we still want to build the individual -# services that we depend on. This differs from the above condition -# by only including service makefiles and not any tests or other -# modules. -include $(patsubst %,$(LOCAL_PATH)/%/Android.mk,$(services)) -endif - diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp new file mode 100644 index 000000000000..f991d7b50fcb --- /dev/null +++ b/services/accessibility/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.accessibility", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/accessibility/Android.mk b/services/accessibility/Android.mk deleted file mode 100644 index ce89aa7697ae..000000000000 --- a/services/accessibility/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.accessibility - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java index 7e94d7b6f1fe..01679ddad04b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java @@ -20,9 +20,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; -import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; -import android.accessibilityservice.GestureDescription; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; @@ -49,7 +47,6 @@ import android.view.MagnificationSpec; import android.view.View; import android.view.accessibility.AccessibilityCache; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; @@ -58,6 +55,7 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection; import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy; import com.android.server.wm.WindowManagerInternal; @@ -171,13 +169,13 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec @NonNull MagnificationController getMagnificationController(); /** - * Resolve a connection for a window id + * Resolve a connection wrapper for a window id * * @param windowId The id of the window of interest * * @return a connection to the window */ - IAccessibilityInteractionConnection getConnectionLocked(int windowId); + RemoteAccessibilityConnection getConnectionLocked(int windowId); /** * Perform the specified accessibility action @@ -416,28 +414,28 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } @Override - public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewIdResName, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = mSystemSupport.getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -450,12 +448,14 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName, - partialInteractiveRegion, interactionId, callback, mFetchFlags, + connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId, + viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, spec); - return true; + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); @@ -463,36 +463,36 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = mSystemSupport.getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -505,12 +505,14 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, - partialInteractiveRegion, interactionId, callback, mFetchFlags, + connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId, + text, partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, spec); - return true; + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); @@ -518,36 +520,36 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findAccessibilityNodeInfoByAccessibilityId( + public String[] findAccessibilityNodeInfoByAccessibilityId( int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid, Bundle arguments) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = mSystemSupport.getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -560,12 +562,14 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, - partialInteractiveRegion, interactionId, callback, mFetchFlags | flags, - interrogatingPid, interrogatingTid, spec, arguments); - return true; + connection.getRemote().findAccessibilityNodeInfoByAccessibilityId( + accessibilityNodeId, partialInteractiveRegion, interactionId, callback, + mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); @@ -573,36 +577,36 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, + public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked( accessibilityWindowId, focusType); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = mSystemSupport.getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -615,12 +619,14 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, - interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, - spec); - return true; + connection.getRemote().findFocus(accessibilityNodeId, focusType, + partialInteractiveRegion, interactionId, callback, mFetchFlags, + interrogatingPid, interrogatingTid, spec); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findFocus()"); @@ -628,35 +634,35 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, + public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = mSystemSupport.getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -669,12 +675,14 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec final int interrogatingPid = Binder.getCallingPid(); callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, - interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, - spec); - return true; + connection.getRemote().focusSearch(accessibilityNodeId, direction, + partialInteractiveRegion, interactionId, callback, mFetchFlags, + interrogatingPid, interrogatingTid, spec); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); @@ -682,11 +690,11 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 0a21b9e27862..ac0cdd7cf99c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -24,12 +24,12 @@ import android.Manifest; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; -import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.AlertDialog; import android.app.PendingIntent; +import android.appwidget.AppWidgetManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -72,6 +72,7 @@ import android.provider.SettingsStringUtil.ComponentNameSet; import android.provider.SettingsStringUtil.SettingStringHelper; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; +import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -97,6 +98,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.server.LocalServices; @@ -104,6 +106,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo; import com.android.server.wm.WindowManagerInternal; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; @@ -186,6 +189,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final WindowManagerInternal mWindowManagerService; + private AppWidgetManagerInternal mAppWidgetService; + private final SecurityPolicy mSecurityPolicy; private final MainHandler mMainHandler; @@ -218,10 +223,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients = new RemoteCallbackList<>(); - private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections = + private final SparseArray<RemoteAccessibilityConnection> mGlobalInteractionConnections = new SparseArray<>(); - private AccessibilityConnectionWrapper mPictureInPictureActionReplacingConnection; + private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>(); @@ -453,6 +458,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + // If the client is from a process that runs across users such as // the system UI or the system we add it to the global state that // is shared across users. @@ -497,9 +503,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // We treat calls from a profile as if made by its parent as profiles // share the accessibility state of the parent. The call below - // performs the current profile parent resolution.. + // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + + // Make sure the reported package is one the caller has access to. + event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked( + event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId)); + // This method does nothing for a background user. if (resolvedUserId == mCurrentUserId) { if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { @@ -617,30 +628,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public int addAccessibilityInteractionConnection(IWindow windowToken, - IAccessibilityInteractionConnection connection, int userId) throws RemoteException { + IAccessibilityInteractionConnection connection, String packageName, + int userId) throws RemoteException { synchronized (mLock) { // We treat calls from a profile as if made by its parent as profiles // share the accessibility state of the parent. The call below // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId()); + + // Make sure the reported package is one the caller has access to. + packageName = mSecurityPolicy.resolveValidReportedPackageLocked( + packageName, UserHandle.getCallingAppId(), resolvedUserId); + final int windowId = sNextWindowId++; // If the window is from a process that runs across users such as // the system UI or the system we add it to the global state that // is shared across users. if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( - windowId, connection, UserHandle.USER_ALL); + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( + windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL); wrapper.linkToDeath(); mGlobalInteractionConnections.put(windowId, wrapper); mGlobalWindowTokens.put(windowId, windowToken.asBinder()); if (DEBUG) { Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid() - + " with windowId: " + windowId + " and token: " + windowToken.asBinder()); + + " with windowId: " + windowId + " and token: " + + windowToken.asBinder()); } } else { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( - windowId, connection, resolvedUserId); + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( + windowId, connection, packageName, resolvedUid, resolvedUserId); wrapper.linkToDeath(); UserState userState = getUserStateLocked(resolvedUserId); userState.mInteractionConnections.put(windowId, wrapper); @@ -693,13 +712,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private int removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, SparseArray<IBinder> windowTokens, - SparseArray<AccessibilityConnectionWrapper> interactionConnections) { + SparseArray<RemoteAccessibilityConnection> interactionConnections) { final int count = windowTokens.size(); for (int i = 0; i < count; i++) { if (windowTokens.valueAt(i) == windowToken) { final int windowId = windowTokens.keyAt(i); windowTokens.removeAt(i); - AccessibilityConnectionWrapper wrapper = interactionConnections.get(windowId); + RemoteAccessibilityConnection wrapper = interactionConnections.get(windowId); wrapper.unlinkToDeath(); interactionConnections.remove(windowId); return windowId; @@ -719,9 +738,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPictureInPictureActionReplacingConnection = null; } if (connection != null) { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID, - connection, UserHandle.USER_ALL); + connection, "foo.bar.baz", Process.SYSTEM_UID, UserHandle.USER_ALL); mPictureInPictureActionReplacingConnection = wrapper; wrapper.linkToDeath(); } @@ -2264,18 +2283,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private class AccessibilityConnectionWrapper implements DeathRecipient { + class RemoteAccessibilityConnection implements DeathRecipient { + private final int mUid; + private final String mPackageName; private final int mWindowId; private final int mUserId; private final IAccessibilityInteractionConnection mConnection; - public AccessibilityConnectionWrapper(int windowId, - IAccessibilityInteractionConnection connection, int userId) { + RemoteAccessibilityConnection(int windowId, + IAccessibilityInteractionConnection connection, + String packageName, int uid, int userId) { mWindowId = windowId; + mPackageName = packageName; + mUid = uid; mUserId = userId; mConnection = connection; } + public int getUid() { + return mUid; + } + + public String getPackageName() { + return mPackageName; + } + + public IAccessibilityInteractionConnection getRemote() { + return mConnection; + } + public void linkToDeath() throws RemoteException { mConnection.asBinder().linkToDeath(this, 0); } @@ -2532,11 +2568,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, long interrogatingTid) { - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection; IBinder activityToken = null; synchronized (mLock) { connection = getConnectionLocked(resolvedWindowId); - if (connection == null) return false; + if (connection == null) { + return false; + } final boolean isA11yFocusAction = (action == ACTION_ACCESSIBILITY_FOCUS) || (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS); final AccessibilityWindowInfo a11yWindowInfo = @@ -2548,7 +2586,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } if ((a11yWindowInfo != null) && a11yWindowInfo.isInPictureInPictureMode() && (mPictureInPictureActionReplacingConnection != null) && !isA11yFocusAction) { - connection = mPictureInPictureActionReplacingConnection.mConnection; + connection = mPictureInPictureActionReplacingConnection; } } final int interrogatingPid = Binder.getCallingPid(); @@ -2563,8 +2601,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub LocalServices.getService(ActivityManagerInternal.class) .setFocusedActivity(activityToken); } - connection.performAccessibilityAction(accessibilityNodeId, action, arguments, - interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); + connection.mConnection.performAccessibilityAction(accessibilityNodeId, action, + arguments, interactionId, callback, fetchFlags, interrogatingPid, + interrogatingTid); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re); @@ -2577,17 +2616,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override - public IAccessibilityInteractionConnection getConnectionLocked(int windowId) { + public RemoteAccessibilityConnection getConnectionLocked(int windowId) { if (DEBUG) { Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); } - AccessibilityManagerService.AccessibilityConnectionWrapper wrapper = + RemoteAccessibilityConnection connection = mGlobalInteractionConnections.get(windowId); - if (wrapper == null) { - wrapper = getCurrentUserStateLocked().mInteractionConnections.get(windowId); + if (connection == null) { + connection = getCurrentUserStateLocked().mInteractionConnections.get(windowId); } - if (wrapper != null && wrapper.mConnection != null) { - return wrapper.mConnection; + if (connection != null && connection.mConnection != null) { + return connection; } if (DEBUG) { Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); @@ -2620,6 +2659,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private AppWidgetManagerInternal getAppWidgetManager() { + synchronized (mLock) { + if (mAppWidgetService == null + && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) { + mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class); + } + return mAppWidgetService; + } + } + final class WindowsForAccessibilityCallback implements WindowManagerInternal.WindowsForAccessibilityCallback { @@ -2912,6 +2961,78 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private boolean isValidPackageForUid(String packageName, int uid) { + try { + return uid == mPackageManager.getPackageUidAsUser( + packageName, UserHandle.getUserId(uid)); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + String resolveValidReportedPackageLocked(CharSequence packageName, int appId, int userId) { + // Okay to pass no package + if (packageName == null) { + return null; + } + // The system gets to pass any package + if (appId == Process.SYSTEM_UID) { + return packageName.toString(); + } + // Passing a package in your UID is fine + final String packageNameStr = packageName.toString(); + final int resolvedUid = UserHandle.getUid(userId, appId); + if (isValidPackageForUid(packageNameStr, resolvedUid)) { + return packageName.toString(); + } + // Appwidget hosts get to pass packages for widgets they host + final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager(); + if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager + .getHostedWidgetPackages(resolvedUid), packageNameStr)) { + return packageName.toString(); + } + // Otherwise, set the package to the first one in the UID + final String[] packageNames = mPackageManager.getPackagesForUid(resolvedUid); + if (ArrayUtils.isEmpty(packageNames)) { + return null; + } + // Okay, the caller reported a package it does not have access to. + // Instead of crashing the caller for better backwards compatibility + // we report the first package in the UID. Since most of the time apps + // don't use shared user id, this will yield correct results and for + // the edge case of using a shared user id we may report the wrong + // package but this is fine since first, this is a cheating app and + // second there is no way to get the correct package anyway. + return packageNames[0]; + } + + String[] computeValidReportedPackages(int callingUid, + String targetPackage, int targetUid) { + if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { + // Empty array means any package is Okay + return EmptyArray.STRING; + } + // IMPORTANT: The target package is already vetted to be in the target UID + String[] uidPackages = new String[]{targetPackage}; + // Appwidget hosts get to pass packages for widgets they host + final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager(); + if (appWidgetManager != null) { + final ArraySet<String> widgetPackages = appWidgetManager + .getHostedWidgetPackages(targetUid); + if (widgetPackages != null && !widgetPackages.isEmpty()) { + final String[] validPackages = new String[uidPackages.length + + widgetPackages.size()]; + System.arraycopy(uidPackages, 0, validPackages, 0, uidPackages.length); + final int widgetPackageCount = widgetPackages.size(); + for (int i = 0; i < widgetPackageCount; i++) { + validPackages[uidPackages.length + i] = widgetPackages.valueAt(i); + } + return validPackages; + } + } + return uidPackages; + } + public void clearWindowsLocked() { List<WindowInfo> windows = Collections.emptyList(); final int activeWindowId = mActiveWindowId; @@ -3338,7 +3459,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>(); - public final SparseArray<AccessibilityConnectionWrapper> mInteractionConnections = + public final SparseArray<RemoteAccessibilityConnection> mInteractionConnections = new SparseArray<>(); public final SparseArray<IBinder> mWindowTokens = new SparseArray<>(); diff --git a/services/appwidget/Android.bp b/services/appwidget/Android.bp new file mode 100644 index 000000000000..aad2ad198bb7 --- /dev/null +++ b/services/appwidget/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.appwidget", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/appwidget/Android.mk b/services/appwidget/Android.mk deleted file mode 100644 index e9bab4a94a30..000000000000 --- a/services/appwidget/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.appwidget - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index b446209790ad..54cf726c6b0e 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -32,6 +32,7 @@ import android.app.PendingIntent; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener; import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.appwidget.PendingHostUpdate; import android.content.BroadcastReceiver; @@ -100,6 +101,8 @@ import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsFactory; @@ -107,6 +110,7 @@ import com.android.server.LocalServices; import com.android.server.WidgetBackupProvider; import com.android.server.policy.IconUtilities; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -259,6 +263,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku computeMaximumWidgetBitmapMemory(); registerBroadcastReceiver(); registerOnCrossProfileProvidersChangedListener(); + + LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal()); } private void computeMaximumWidgetBitmapMemory() { @@ -4630,4 +4636,24 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } } + + private class AppWidgetManagerLocal extends AppWidgetManagerInternal { + @Override + public ArraySet<String> getHostedWidgetPackages(int uid) { + synchronized (mLock) { + ArraySet<String> widgetPackages = null; + final int widgetCount = mWidgets.size(); + for (int i = 0; i < widgetCount; i++) { + final Widget widget = mWidgets.get(i); + if (widget.host.id.uid == uid) { + if (widgetPackages == null) { + widgetPackages = new ArraySet<>(); + } + widgetPackages.add(widget.provider.id.componentName.getPackageName()); + } + } + return widgetPackages; + } + } + } } diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp new file mode 100644 index 000000000000..2768c1851314 --- /dev/null +++ b/services/autofill/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.autofill", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/autofill/Android.mk b/services/autofill/Android.mk deleted file mode 100644 index a1f19fd4c72d..000000000000 --- a/services/autofill/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.autofill - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 5d4ddf0fb542..4bf3c5ab9268 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -26,6 +26,7 @@ import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.IActivityManager; import android.content.ComponentName; @@ -36,11 +37,13 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -73,11 +76,11 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; +import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.Random; /** @@ -352,7 +355,7 @@ final class AutofillManagerServiceImpl { pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, - hasCallback, componentName); + hasCallback, componentName, flags); if (newSession == null) { return NO_SESSION; } @@ -453,7 +456,7 @@ final class AutofillManagerServiceImpl { private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, boolean hasCallback, - @NonNull ComponentName componentName) { + @NonNull ComponentName componentName, int flags) { // use random ids so that one app cannot know that another app creates sessions int sessionId; int tries = 0; @@ -471,7 +474,7 @@ final class AutofillManagerServiceImpl { final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, - mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName); + mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags); mSessions.put(newSession.id, newSession); return newSession; @@ -481,16 +484,17 @@ final class AutofillManagerServiceImpl { * Asserts the component is owned by the caller. */ private void assertCallerLocked(@NonNull ComponentName componentName) { + final String packageName = componentName.getPackageName(); final PackageManager pm = mContext.getPackageManager(); final int callingUid = Binder.getCallingUid(); final int packageUid; try { - packageUid = pm.getPackageUidAsUser(componentName.getPackageName(), - UserHandle.getCallingUserId()); + packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); } catch (NameNotFoundException e) { throw new SecurityException("Could not verify UID for " + componentName); } - if (callingUid != packageUid) { + if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class) + .hasRunningActivity(callingUid, packageName)) { final String[] packages = pm.getPackagesForUid(callingUid); final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid @@ -715,18 +719,32 @@ final class AutofillManagerServiceImpl { @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, - @NonNull ArrayList<AutofillId> detectedFieldIdsList, - @NonNull ArrayList<Match> detectedMatchesList) { + @Nullable ArrayList<AutofillId> detectedFieldIdsList, + @Nullable ArrayList<Match> detectedMatchesList, + @NonNull String appPackageName) { synchronized (mLock) { if (isValidEventLocked("logDatasetNotSelected()", sessionId)) { AutofillId[] detectedFieldsIds = null; Match[] detectedMatches = null; - if (!detectedFieldIdsList.isEmpty()) { + if (detectedFieldIdsList != null) { detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()]; detectedFieldIdsList.toArray(detectedFieldsIds); detectedMatches = new Match[detectedMatchesList.size()]; detectedMatchesList.toArray(detectedMatches); + + final int size = detectedMatchesList.size(); + float totalScore = 0; + for (int i = 0; i < size; i++) { + totalScore += detectedMatches[i].getScore(); + } + final int averageScore = (int) ((totalScore * 100) / size); + mMetricsLogger.write( + Helper.newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES, + appPackageName, getServicePackageName()) + .setCounterValue(size) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore)); + } mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null, clientState, selectedDatasets, ignoredDatasets, @@ -776,6 +794,11 @@ final class AutofillManagerServiceImpl { synchronized (mLock) { if (isCalledByServiceLocked("setUserData", callingUid)) { mUserData = userData; + // Log it + int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length; + mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED, + getServicePackageName(), null) + .setCounterValue(numberFields)); } } } @@ -1013,10 +1036,12 @@ final class AutofillManagerServiceImpl { expiration = Long.MAX_VALUE; } mDisabledActivities.put(componentName, expiration); - int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; - mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY, - componentName.getPackageName(), getServicePackageName()) - .addTaggedData(MetricsEvent.FIELD_CLASS_NAME, componentName.getClassName()) + final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; + // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead + // of package name + mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY) + .setComponentName(componentName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) .setCounterValue(intDuration)); } } @@ -1034,7 +1059,8 @@ final class AutofillManagerServiceImpl { if (expiration >= elapsedTime) return true; // Restriction expired - clean it up. if (sVerbose) { - Slog.v(TAG, "Removing " + componentName.toShortString() + " from disabled list"); + Slog.v(TAG, "Removing " + componentName.toShortString() + + " from disabled list"); } mDisabledActivities.remove(componentName); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4e64afb63341..56f5f64c090a 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -128,6 +128,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** uid the session is for */ public final int uid; + /** Flags used to start the session */ + public final int mFlags; + @GuardedBy("mLock") @NonNull private IBinder mActivityToken; @@ -441,8 +444,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName) { + @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName, + int flags) { id = sessionId; + mFlags = flags; this.uid = uid; mStartTime = SystemClock.elapsedRealtime(); mService = service; @@ -456,7 +461,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName = componentName; mClient = IAutoFillManagerClient.Stub.asInterface(client); - writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED); + mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) + .addTaggedData(MetricsEvent.FIELD_FLAGS, flags)); } /** @@ -505,8 +511,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - // TODO(b/67867469): remove once feature is finished - if (response.getFieldClassificationIds() != null && !mService.isFieldClassificationEnabled()) { + final AutofillId[] fieldClassificationIds = response.getFieldClassificationIds(); + // TODO(b/67867469): remove once feature is finished (or use method from AFM to check) + if (fieldClassificationIds != null && !mService.isFieldClassificationEnabled()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestFlags); return; @@ -548,6 +555,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState .setType(MetricsEvent.TYPE_SUCCESS) .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, response.getDatasets() == null ? 0 : response.getDatasets().size()); + if (fieldClassificationIds != null) { + log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS, + fieldClassificationIds.length); + } mMetricsLogger.write(log); } @@ -1106,7 +1117,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, - detectedFieldIds, detectedMatches); + detectedFieldIds, detectedMatches, mComponentName.getPackageName()); } /** @@ -2115,6 +2126,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final String prefix2 = prefix + " "; pw.print(prefix); pw.print("id: "); pw.println(id); pw.print(prefix); pw.print("uid: "); pw.println(uid); + pw.print(prefix); pw.print("flags: "); pw.println(mFlags); pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); diff --git a/services/backup/Android.bp b/services/backup/Android.bp new file mode 100644 index 000000000000..ef03d83d4916 --- /dev/null +++ b/services/backup/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.backup", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/backup/Android.mk b/services/backup/Android.mk deleted file mode 100644 index 3e686d122668..000000000000 --- a/services/backup/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.backup - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index bd0d853ce698..1dba0529eaf4 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -1520,7 +1520,11 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } // clear an application's data, blocking until the operation completes or times out - public void clearApplicationDataSynchronous(String packageName) { + // if keepSystemState is true, we intentionally do not also clear system state that + // would ordinarily also be cleared, because we aren't actually wiping the app back + // to empty; we're bringing it into the actual expected state related to the already- + // restored notification state etc. + public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) { // Don't wipe packages marked allowClearUserData=false try { PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); @@ -1541,7 +1545,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter synchronized (mClearDataLock) { mClearingData = true; try { - mActivityManager.clearApplicationUserData(packageName, observer, 0); + mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer, 0); } catch (RemoteException e) { // can't happen because the activity manager is in this process } diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 888dcd7e7a5e..7efe5cadb5cc 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -327,7 +327,7 @@ public class FullRestoreEngine extends RestoreEngine { Slog.d(TAG, "Clearing app data preparatory to full restore"); } - mBackupManagerService.clearApplicationDataSynchronous(pkg); + mBackupManagerService.clearApplicationDataSynchronous(pkg, true); } else { if (MORE_DEBUG) { Slog.d(TAG, "backup agent (" diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index 22691bb60bfa..3dc242f1d48a 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -591,7 +591,7 @@ public class PerformAdbRestoreTask implements Runnable { Slog.d(TAG, "Clearing app data preparatory to full restore"); } - mBackupManagerService.clearApplicationDataSynchronous(pkg); + mBackupManagerService.clearApplicationDataSynchronous(pkg, true); } else { if (DEBUG) { Slog.d(TAG, "backup agent (" diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 5884dc536080..2db743020237 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -947,7 +947,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // We also need to wipe the current target's data, as it's probably // in an incoherent state. backupManagerService.clearApplicationDataSynchronous( - mCurrentPackage.packageName); + mCurrentPackage.packageName, false); // Schedule the next state based on the nature of our failure if (status == BackupTransport.TRANSPORT_ERROR) { @@ -1115,7 +1115,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // If the agent fails restore, it might have put the app's data // into an incoherent state. For consistency we wipe its data // again in this case before continuing with normal teardown - backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName); + backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName, false); keyValueAgentCleanup(); } diff --git a/services/companion/Android.bp b/services/companion/Android.bp new file mode 100644 index 000000000000..d2dac357a227 --- /dev/null +++ b/services/companion/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.companion", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/companion/Android.mk b/services/companion/Android.mk deleted file mode 100644 index be48761cbae5..000000000000 --- a/services/companion/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.companion - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/core/Android.bp b/services/core/Android.bp new file mode 100644 index 000000000000..446c2a52be22 --- /dev/null +++ b/services/core/Android.bp @@ -0,0 +1,57 @@ +java_library_static { + name: "services.core", + + aidl: { + include_dirs: [ + "frameworks/native/aidl/binder", + "system/core/storaged/binder", + "system/netd/server/binder", + "system/vold/binder", + ], + }, + srcs: [ + "java/**/*.java", + ":netd_aidl", + ":netd_metrics_aidl", + ":installd_aidl", + ":storaged_aidl", + ":vold_aidl", + "java/com/android/server/EventLogTags.logtags", + "java/com/android/server/am/EventLogTags.logtags", + ], + + libs: [ + "services.net", + "android.hardware.light-V2.0-java", + "android.hardware.power-V1.0-java", + "android.hardware.tv.cec-V1.0-java", + "android.hidl.manager-V1.0-java", + ], + + static_libs: [ + "time_zone_distro", + "time_zone_distro_installer", + "android.hardware.health-V1.0-java", + "android.hardware.health-V2.0-java", + "android.hardware.weaver-V1.0-java", + "android.hardware.biometrics.fingerprint-V2.1-java", + "android.hardware.oemlock-V1.0-java", + "android.hardware.tetheroffload.control-V1.0-java", + "android.hardware.vibrator-V1.0-java", + "android.hardware.configstore-V1.0-java", + "android.hardware.contexthub-V1.0-java", + ], +} + +java_genrule { + name: "services.core.priorityboosted", + srcs: [":services.core"], + tools: ["lockedregioncodeinjection"], + cmd: "$(location lockedregioncodeinjection) " + + " --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;\" " + + " --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " + + " --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " + + " -o $(out) " + + " -i $(in)", + out: ["services.core.priorityboosted.jar"], +} diff --git a/services/core/Android.mk b/services/core/Android.mk deleted file mode 100644 index 3d81baf27958..000000000000 --- a/services/core/Android.mk +++ /dev/null @@ -1,72 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.core - -LOCAL_AIDL_INCLUDES := \ - frameworks/native/aidl/binder \ - system/core/storaged/binder \ - system/netd/server/binder \ - system/vold/binder - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) \ - java/com/android/server/EventLogTags.logtags \ - java/com/android/server/am/EventLogTags.logtags \ - ../../../../system/core/storaged/binder/android/os/IStoraged.aidl \ - ../../../../system/netd/server/binder/android/net/INetd.aidl \ - ../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \ - ../../../../system/vold/binder/android/os/IVold.aidl \ - ../../../../system/vold/binder/android/os/IVoldListener.aidl \ - ../../../../system/vold/binder/android/os/IVoldTaskListener.aidl \ - ../../../native/cmds/installd/binder/android/os/IInstalld.aidl \ - -LOCAL_AIDL_INCLUDES += \ - system/netd/server/binder - -LOCAL_JAVA_LIBRARIES := \ - services.net \ - android.hardware.light-V2.0-java \ - android.hardware.power-V1.0-java \ - android.hardware.tv.cec-V1.0-java \ - android.hidl.manager-V1.0-java - -LOCAL_STATIC_JAVA_LIBRARIES := \ - time_zone_distro \ - time_zone_distro_installer \ - android.hidl.base-V1.0-java \ - android.hardware.health-V1.0-java \ - android.hardware.health-V2.0-java \ - android.hardware.weaver-V1.0-java \ - android.hardware.biometrics.fingerprint-V2.1-java \ - android.hardware.oemlock-V1.0-java \ - android.hardware.tetheroffload.control-V1.0-java \ - android.hardware.vibrator-V1.0-java \ - android.hardware.configstore-V1.0-java \ - android.hardware.contexthub-V1.0-java - -ifneq ($(INCREMENTAL_BUILDS),) - LOCAL_PROGUARD_ENABLED := disabled - LOCAL_JACK_ENABLED := incremental -endif - -LOCAL_JACK_FLAGS := \ - -D jack.transformations.boost-locked-region-priority=true \ - -D jack.transformations.boost-locked-region-priority.classname=com.android.server.am.ActivityManagerService,com.android.server.wm.WindowHashMap \ - -D jack.transformations.boost-locked-region-priority.request=com.android.server.am.ActivityManagerService\#boostPriorityForLockedSection,com.android.server.wm.WindowManagerService\#boostPriorityForLockedSection \ - -D jack.transformations.boost-locked-region-priority.reset=com.android.server.am.ActivityManagerService\#resetPriorityAfterLockedSection,com.android.server.wm.WindowManagerService\#resetPriorityAfterLockedSection - -LOCAL_JAR_PROCESSOR := lockedregioncodeinjection -# Use = instead of := to delay evaluation of ${in} and ${out} -LOCAL_JAR_PROCESSOR_ARGS = \ - --targets \ - "Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;" \ - --pre \ - "com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection" \ - --post \ - "com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection" \ - -o ${out} \ - -i ${in} - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/core/java/com/android/server/timezone/ClockHelper.java b/services/core/java/com/android/server/AlarmManagerInternal.java index 353728a15e7c..dbff957a0da7 100644 --- a/services/core/java/com/android/server/timezone/ClockHelper.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -11,15 +11,11 @@ * 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. + * limitations under the License */ -package com.android.server.timezone; +package com.android.server; -/** - * An easy-to-mock interface for obtaining a monotonically increasing time value in milliseconds. - */ -interface ClockHelper { - - long currentTimestamp(); +public interface AlarmManagerInternal { + void removeAlarmsForUid(int uid); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index ca1524959358..ae4f0cef18b6 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -92,6 +92,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.LocalLog; import com.android.server.ForceAppStandbyTracker.Listener; +import com.android.server.LocalServices; /** * Alarm manager implementaion. @@ -467,21 +468,14 @@ class AlarmManagerService extends SystemService { return newStart; } - boolean remove(final PendingIntent operation, final IAlarmListener listener) { - if (operation == null && listener == null) { - if (localLOGV) { - Slog.w(TAG, "requested remove() of null operation", - new RuntimeException("here")); - } - return false; - } + boolean remove(Predicate<Alarm> predicate) { boolean didRemove = false; long newStart = 0; // recalculate endpoints as we go long newEnd = Long.MAX_VALUE; int newFlags = 0; for (int i = 0; i < alarms.size(); ) { Alarm alarm = alarms.get(i); - if (alarm.matches(operation, listener)) { + if (predicate.test(alarm)) { alarms.remove(i); didRemove = true; if (alarm.alarmClock != null) { @@ -507,111 +501,6 @@ class AlarmManagerService extends SystemService { return didRemove; } - boolean remove(final String packageName) { - if (packageName == null) { - if (localLOGV) { - Slog.w(TAG, "requested remove() of null packageName", - new RuntimeException("here")); - } - return false; - } - boolean didRemove = false; - long newStart = 0; // recalculate endpoints as we go - long newEnd = Long.MAX_VALUE; - int newFlags = 0; - for (int i = alarms.size()-1; i >= 0; i--) { - Alarm alarm = alarms.get(i); - if (alarm.matches(packageName)) { - alarms.remove(i); - didRemove = true; - if (alarm.alarmClock != null) { - mNextAlarmClockMayChange = true; - } - } else { - if (alarm.whenElapsed > newStart) { - newStart = alarm.whenElapsed; - } - if (alarm.maxWhenElapsed < newEnd) { - newEnd = alarm.maxWhenElapsed; - } - newFlags |= alarm.flags; - } - } - if (didRemove) { - // commit the new batch bounds - start = newStart; - end = newEnd; - flags = newFlags; - } - return didRemove; - } - - boolean removeForStopped(final int uid) { - boolean didRemove = false; - long newStart = 0; // recalculate endpoints as we go - long newEnd = Long.MAX_VALUE; - int newFlags = 0; - for (int i = alarms.size()-1; i >= 0; i--) { - Alarm alarm = alarms.get(i); - try { - if (alarm.uid == uid && ActivityManager.getService().isAppStartModeDisabled( - uid, alarm.packageName)) { - alarms.remove(i); - didRemove = true; - if (alarm.alarmClock != null) { - mNextAlarmClockMayChange = true; - } - } else { - if (alarm.whenElapsed > newStart) { - newStart = alarm.whenElapsed; - } - if (alarm.maxWhenElapsed < newEnd) { - newEnd = alarm.maxWhenElapsed; - } - newFlags |= alarm.flags; - } - } catch (RemoteException e) { - } - } - if (didRemove) { - // commit the new batch bounds - start = newStart; - end = newEnd; - flags = newFlags; - } - return didRemove; - } - - boolean remove(final int userHandle) { - boolean didRemove = false; - long newStart = 0; // recalculate endpoints as we go - long newEnd = Long.MAX_VALUE; - for (int i = 0; i < alarms.size(); ) { - Alarm alarm = alarms.get(i); - if (UserHandle.getUserId(alarm.creatorUid) == userHandle) { - alarms.remove(i); - didRemove = true; - if (alarm.alarmClock != null) { - mNextAlarmClockMayChange = true; - } - } else { - if (alarm.whenElapsed > newStart) { - newStart = alarm.whenElapsed; - } - if (alarm.maxWhenElapsed < newEnd) { - newEnd = alarm.maxWhenElapsed; - } - i++; - } - } - if (didRemove) { - // commit the new batch bounds - start = newStart; - end = newEnd; - } - return didRemove; - } - boolean hasPackage(final String packageName) { final int N = alarms.size(); for (int i = 0; i < N; i++) { @@ -759,6 +648,8 @@ class AlarmManagerService extends SystemService { mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context); mForceAppStandbyTracker.addListener(mForceAppStandbyListener); + + publishLocalService(AlarmManagerInternal.class, new LocalService()); } static long convertToElapsed(long when, int type) { @@ -1554,6 +1445,21 @@ class AlarmManagerService extends SystemService { } } + /** + * System-process internal API + */ + private final class LocalService implements AlarmManagerInternal { + @Override + public void removeAlarmsForUid(int uid) { + synchronized (mLock) { + removeLocked(uid); + } + } + } + + /** + * Public-facing binder interface + */ private final IBinder mService = new IAlarmManager.Stub() { @Override public void set(String callingPackage, @@ -2430,10 +2336,19 @@ class AlarmManagerService extends SystemService { } private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { + if (operation == null && directReceiver == null) { + if (localLOGV) { + Slog.w(TAG, "requested remove() of null operation", + new RuntimeException("here")); + } + return; + } + boolean didRemove = false; + final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver); for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(operation, directReceiver); + didRemove |= b.remove(whichAlarms); if (b.size() == 0) { mAlarmBatches.remove(i); } @@ -2476,11 +2391,58 @@ class AlarmManagerService extends SystemService { } } - void removeLocked(String packageName) { + void removeLocked(final int uid) { boolean didRemove = false; + final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid; for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(packageName); + didRemove |= b.remove(whichAlarms); + if (b.size() == 0) { + mAlarmBatches.remove(i); + } + } + for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { + final Alarm a = mPendingWhileIdleAlarms.get(i); + if (a.uid == uid) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + mPendingWhileIdleAlarms.remove(i); + } + } + for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) { + final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); + for (int j = alarmsForUid.size() - 1; j >= 0; j--) { + if (alarmsForUid.get(j).uid == uid) { + alarmsForUid.remove(j); + } + } + if (alarmsForUid.size() == 0) { + mPendingBackgroundAlarms.removeAt(i); + } + } + if (didRemove) { + if (DEBUG_BATCH) { + Slog.v(TAG, "remove(uid) changed bounds; rebatching"); + } + rebatchAllAlarmsLocked(true); + rescheduleKernelAlarmsLocked(); + updateNextAlarmClockLocked(); + } + } + + void removeLocked(final String packageName) { + if (packageName == null) { + if (localLOGV) { + Slog.w(TAG, "requested remove() of null packageName", + new RuntimeException("here")); + } + return; + } + + boolean didRemove = false; + final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(packageName); + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + didRemove |= b.remove(whichAlarms); if (b.size() == 0) { mAlarmBatches.remove(i); } @@ -2513,11 +2475,20 @@ class AlarmManagerService extends SystemService { } } - void removeForStoppedLocked(int uid) { + void removeForStoppedLocked(final int uid) { boolean didRemove = false; + final Predicate<Alarm> whichAlarms = (Alarm a) -> { + try { + if (a.uid == uid && ActivityManager.getService().isAppStartModeDisabled( + uid, a.packageName)) { + return true; + } + } catch (RemoteException e) { /* fall through */} + return false; + }; for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { Batch b = mAlarmBatches.get(i); - didRemove |= b.removeForStopped(uid); + didRemove |= b.remove(whichAlarms); if (b.size() == 0) { mAlarmBatches.remove(i); } @@ -2546,9 +2517,11 @@ class AlarmManagerService extends SystemService { void removeUserLocked(int userHandle) { boolean didRemove = false; + final Predicate<Alarm> whichAlarms = + (Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle; for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(userHandle); + didRemove |= b.remove(whichAlarms); if (b.size() == 0) { mAlarmBatches.remove(i); } @@ -3396,6 +3369,7 @@ class AlarmManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); synchronized (mLock) { String action = intent.getAction(); String pkgList[] = null; @@ -3416,7 +3390,6 @@ class AlarmManagerService extends SystemService { removeUserLocked(userHandle); } } else if (Intent.ACTION_UID_REMOVED.equals(action)) { - int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); if (uid >= 0) { mLastAllowWhileIdleDispatch.delete(uid); } @@ -3436,7 +3409,13 @@ class AlarmManagerService extends SystemService { } if (pkgList != null && (pkgList.length > 0)) { for (String pkg : pkgList) { - removeLocked(pkg); + if (uid >= 0) { + // package-removed case + removeLocked(uid); + } else { + // external-applications-unavailable etc case + removeLocked(pkg); + } mPriorities.remove(pkg); for (int i=mBroadcastStats.size()-1; i>=0; i--) { ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i); diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java index 08392b067a9a..c86042b23460 100644 --- a/services/core/java/com/android/server/AnimationThread.java +++ b/services/core/java/com/android/server/AnimationThread.java @@ -22,8 +22,8 @@ import android.os.Handler; import android.os.Trace; /** - * Thread for handling all window animations, or anything that's directly impacting animations like - * starting windows or traversals. + * Thread for handling all legacy window animations, or anything that's directly impacting + * animations like starting windows or traversals. */ public final class AnimationThread extends ServiceThread { private static AnimationThread sInstance; diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 763a4e494593..d9713a517a94 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1628,7 +1628,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (mQuietEnable) { + if (!mQuietEnable) { if (!mBluetooth.enable()) { Slog.e(TAG, "IBluetooth.enable() returned false"); } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index fc57a0d58400..37f1dc490fe3 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -108,6 +108,8 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.service.vr.IVrManager; +import android.service.vr.IVrStateCallbacks; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.ArrayMap; @@ -189,6 +191,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_CREATE_SESSION = 1050; static final int MSG_START_INPUT = 2000; + static final int MSG_START_VR_INPUT = 2010; static final int MSG_UNBIND_CLIENT = 3000; static final int MSG_BIND_CLIENT = 3010; @@ -317,6 +320,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + /** + * VR state callback. + * Listens for when VR mode finishes. + */ + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + @Override + public void onVrStateChanged(boolean enabled) { + if (!enabled) { + restoreNonVrImeFromSettingsNoCheck(); + } + } + }; + + private void restoreNonVrImeFromSettingsNoCheck() { + // switch back to non-VR InputMethod from settings. + synchronized (mMethodMap) { + final String lastInputId = mSettings.getSelectedInputMethod(); + setInputMethodLocked(lastInputId, + mSettings.getSelectedInputMethodSubtypeId(lastInputId)); + } + } + static final class ClientState { final IInputMethodClient client; final IInputContext inputContext; @@ -863,6 +888,30 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** + * Start a VR InputMethod that matches IME with package name of {@param component}. + * Note: This method is called from {@link android.app.VrManager}. + */ + private void startVrInputMethodNoCheck(@Nullable ComponentName component) { + if (component == null) { + // clear the current VR-only IME (if any) and restore normal IME. + restoreNonVrImeFromSettingsNoCheck(); + return; + } + + synchronized (mMethodMap) { + String packageName = component.getPackageName(); + for (InputMethodInfo info : mMethodList) { + if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) { + // set this is as current inputMethod without updating settings. + setInputMethodEnabledLocked(info.getId(), true); + setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID); + break; + } + } + } + } + + /** * Handles {@link Intent#ACTION_LOCALE_CHANGED}. * * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all @@ -1338,6 +1387,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mFileManager = new InputMethodFileManager(mMethodMap, userId); mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( mSettings, context); + // Register VR-state listener. + IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE); + if (vrManager != null) { + try { + vrManager.registerListener(mVrStateCallbacks); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register VR mode state listener."); + } + } } private void resetDefaultImeLocked(Context context) { @@ -1562,12 +1620,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public List<InputMethodInfo> getInputMethodList() { + return getInputMethodList(false /* isVrOnly */); + } + + public List<InputMethodInfo> getVrInputMethodList() { + return getInputMethodList(true /* isVrOnly */); + } + + private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) { // TODO: Make this work even for non-current users? if (!calledFromValidUser()) { return Collections.emptyList(); } synchronized (mMethodMap) { - return new ArrayList<>(mMethodList); + ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + for (InputMethodInfo info : mMethodList) { + + if (info.isVrOnly() == isVrOnly) { + methodList.add(info); + } + } + return methodList; } } @@ -3356,6 +3429,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_SET_INTERACTIVE: handleSetInteractive(msg.arg1 != 0); return true; + case MSG_START_VR_INPUT: + startVrInputMethodNoCheck((ComponentName) msg.obj); + return true; case MSG_SWITCH_IME: handleSwitchInputMethod(msg.arg1 != 0); return true; @@ -3876,8 +3952,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId, boolean setSubtypeOnly) { - // Update the history of InputMethod and Subtype - mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype); + // Updates to InputMethod are transient in VR mode. Its not included in history. + final boolean isVrInput = imi != null && imi.isVrOnly(); + if (!isVrInput) { + // Update the history of InputMethod and Subtype + mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype); + } mCurUserActionNotificationSequenceNumber = Math.max(mCurUserActionNotificationSequenceNumber + 1, 1); @@ -3892,6 +3972,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurUserActionNotificationSequenceNumber, mCurClient)); } + if (isVrInput) { + // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped. + return; + } + // Set Subtype here if (imi == null || subtypeId < 0) { mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); @@ -4351,6 +4436,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD); } + + @Override + public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName)); + } } private static String imeWindowStatusToString(final int imeWindowVis) { diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 2d2424fe7b59..9bfdd0c5009c 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -433,7 +433,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { || !otherStack.affectedBySplitScreenResize()) { continue; } - otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + false /* animate */, false /* showRecents */, + false /* sendNonResizeableNotification */); } } finally { mSupervisor.mWindowManager.continueSurfaceLayout(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7b4703a4677c..6760c862dbb6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -401,6 +401,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; +import com.android.server.AlarmManagerInternal; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; @@ -471,6 +472,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import dalvik.system.VMRuntime; + import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -5948,7 +5950,7 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean clearApplicationUserData(final String packageName, + public boolean clearApplicationUserData(final String packageName, boolean keepState, final IPackageDataObserver observer, int userId) { enforceNotIsolatedCaller("clearApplicationUserData"); int uid = Binder.getCallingUid(); @@ -6052,14 +6054,27 @@ public class ActivityManagerService extends IActivityManager.Stub pm.clearApplicationUserData(packageName, localObserver, resolvedUserId); if (appInfo != null) { - synchronized (this) { - // Remove all permissions granted from/to this package - removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true); + // Restore already established notification state and permission grants, + // so it told us to keep those intact -- it's about to emplace app data + // that is appropriate for those bits of system state. + if (!keepState) { + synchronized (this) { + // Remove all permissions granted from/to this package + removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true); + } + + // Reset notification state + INotificationManager inm = NotificationManager.getService(); + inm.clearData(packageName, appInfo.uid, uid == appInfo.uid); } - // Reset notification settings. - INotificationManager inm = NotificationManager.getService(); - inm.clearData(packageName, appInfo.uid, uid == appInfo.uid); + // Clear its scheduled jobs + JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); + js.cancelJobsForUid(appInfo.uid, "clear data"); + + // Clear its pending alarms + AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); + ami.removeAlarmsForUid(uid); } } catch (RemoteException e) { } @@ -10570,7 +10585,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (toTop) { stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task); } - stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents); + stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents, + true /* sendNonResizeableNotification */); return windowingMode != task.getWindowingMode(); } finally { Binder.restoreCallingIdentity(ident); @@ -20679,7 +20695,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " + app.processName + " new config " + configCopy); mLifecycleManager.scheduleTransaction(app.thread, - new ConfigurationChangeItem(configCopy)); + ConfigurationChangeItem.obtain(configCopy)); } } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); @@ -24477,6 +24493,26 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean isRuntimeRestarted() { return mSystemServiceManager.isRuntimeRestarted(); } + + @Override + public boolean hasRunningActivity(int uid, @Nullable String packageName) { + if (packageName == null) return false; + + synchronized (ActivityManagerService.this) { + for (int i = 0; i < mLruProcesses.size(); i++) { + final ProcessRecord processRecord = mLruProcesses.get(i); + if (processRecord.uid == uid) { + for (int j = 0; j < processRecord.activities.size(); j++) { + final ActivityRecord activityRecord = processRecord.activities.get(j); + if (packageName.equals(activityRecord.packageName)) { + return true; + } + } + } + } + } + return false; + } } /** diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index a089e6ceef78..60a623677825 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -618,7 +618,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + ", displayId=" + displayId + ", config=" + config); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new MoveToDisplayItem(displayId, config)); + MoveToDisplayItem.obtain(displayId, config)); } catch (RemoteException e) { // If process died, whatever. } @@ -636,7 +636,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + config); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new ActivityConfigurationChangeItem(config)); + ActivityConfigurationChangeItem.obtain(config)); } catch (RemoteException e) { // If process died, whatever. } @@ -663,7 +663,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private void scheduleMultiWindowModeChanged(Configuration overrideConfig) { try { service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new MultiWindowModeChangeItem(mLastReportedMultiWindowMode, + MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig)); } catch (Exception e) { // If process died, I don't care. @@ -691,7 +691,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private void schedulePictureInPictureModeChanged(Configuration overrideConfig) { try { service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new PipModeChangeItem(mLastReportedPictureInPictureMode, + PipModeChangeItem.obtain(mLastReportedPictureInPictureMode, overrideConfig)); } catch (Exception e) { // If process died, no one cares. @@ -1380,7 +1380,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo ArrayList<ReferrerIntent> ar = new ArrayList<>(1); ar.add(rintent); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new NewIntentItem(ar, state == PAUSED)); + NewIntentItem.obtain(ar, state == PAUSED)); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); @@ -1603,7 +1603,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo sleeping = false; app.pendingUiClean = true; service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - new WindowVisibilityItem(true /* showWindow */)); + WindowVisibilityItem.obtain(true /* showWindow */)); // The activity may be waiting for stop, but that is no longer appropriate for it. mStackSupervisor.mStoppingActivities.remove(this); mStackSupervisor.mGoingToSleepActivities.remove(this); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index edf9813497e0..cf40be53e70d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -483,10 +483,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai @Override public void setWindowingMode(int windowingMode) { - setWindowingMode(windowingMode, false /* animate */, true /* showRecents */); + setWindowingMode(windowingMode, false /* animate */, true /* showRecents */, + true /* sendNonResizeableNotification */); } - void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents) { + void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents, + boolean sendNonResizeableNotification) { final int currentMode = getWindowingMode(); final ActivityDisplay display = getDisplay(); final TaskRecord topTask = topTask(); @@ -505,7 +507,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack(); // Take any required action due to us not supporting the preferred windowing mode. - if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) { + if (sendNonResizeableNotification + && windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) { if (alreadyInSplitScreenMode && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) { @@ -524,8 +527,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final WindowManagerService wm = mService.mWindowManager; final ActivityRecord topActivity = getTopActivity(); - if (windowingMode != WINDOWING_MODE_FULLSCREEN && topActivity != null - && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) { + if (sendNonResizeableNotification && windowingMode != WINDOWING_MODE_FULLSCREEN + && topActivity != null && topActivity.isNonResizableOrForcedResizable() + && !topActivity.noDisplay) { // Inform the user that they are starting an app that may not work correctly in // multi-window mode. final String packageName = topActivity.appInfo.packageName; @@ -1430,7 +1434,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mService.updateUsageStats(prev, false); mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken, - new PauseActivityItem(prev.finishing, userLeaving, + PauseActivityItem.obtain(prev.finishing, userLeaving, prev.configChangeFlags, pauseImmediately)); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. @@ -2061,7 +2065,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + r); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new WindowVisibilityItem(false /* showWindow */)); + WindowVisibilityItem.obtain(false /* showWindow */)); } // Reset the flag indicating that an app can enter picture-in-picture once the @@ -2589,13 +2593,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); mService.mLifecycleManager.scheduleTransaction(next.app.thread, - next.appToken, new ActivityResultItem(a)); + next.appToken, ActivityResultItem.obtain(a)); } } if (next.newIntents != null) { mService.mLifecycleManager.scheduleTransaction(next.app.thread, - next.appToken, new NewIntentItem(next.newIntents, + next.appToken, NewIntentItem.obtain(next.newIntents, false /* andPause */)); } @@ -2614,7 +2618,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.app.forceProcessStateUpTo(mService.mTopProcessState); next.clearOptionsLocked(); mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken, - new ResumeActivityItem(next.app.repProcState, + ResumeActivityItem.obtain(next.app.repProcState, mService.isNextTransitionForward())); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " @@ -3266,7 +3270,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai list.add(new ResultInfo(resultWho, requestCode, resultCode, data)); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new ActivityResultItem(list)); + ActivityResultItem.obtain(list)); return; } catch (Exception e) { Slog.w(TAG, "Exception thrown sending result to " + r, e); @@ -3395,7 +3399,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai EventLogTags.writeAmStopActivity( r.userId, System.identityHashCode(r), r.shortComponentName); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new StopActivityItem(r.visible, r.configChangeFlags)); + StopActivityItem.obtain(r.visible, r.configChangeFlags)); if (shouldSleepOrShutDownActivities()) { r.setSleeping(true); } @@ -4201,7 +4205,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - new DestroyActivityItem(r.finishing, r.configChangeFlags)); + DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)); } catch (Exception e) { // We can just ignore exceptions here... if the process // has crashed, our death notification will clean things diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 7561d0f8ad96..edaa51112867 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1239,9 +1239,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) { synchronized (mService) { - return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, - PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags - | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + try { + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + return mService.getPackageManagerInternalLocked().resolveIntent( + intent, resolvedType, PackageManager.MATCH_INSTANT + | PackageManager.MATCH_DEFAULT_ONLY | flags + | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + + } finally { + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } } } @@ -1400,9 +1407,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Create activity launch transaction. - final ClientTransaction clientTransaction = new ClientTransaction(app.thread, + final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken); - clientTransaction.addCallback(new LaunchActivityItem(new Intent(r.intent), + clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global // and override configs. @@ -1415,9 +1422,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = new ResumeActivityItem(mService.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); } else { - lifecycleItem = new PauseActivityItem(); + lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java index a97b93ca01ba..aed49e0037e1 100644 --- a/services/core/java/com/android/server/am/ActivityStartController.java +++ b/services/core/java/com/android/server/am/ActivityStartController.java @@ -105,7 +105,8 @@ public class ActivityStartController { /** * TODO(b/64750076): Capture information necessary for dump and * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object - * around */ + * around + */ private ActivityStarter mLastStarter; ActivityStartController(ActivityManagerService service) { @@ -130,10 +131,16 @@ public class ActivityStartController { * considered invalid and no longer modified or used. */ ActivityStarter obtainStarter(Intent intent, String reason) { - final ActivityStarter starter = mFactory.obtainStarter(); - mLastStarter = starter; + return mFactory.obtain().setIntent(intent).setReason(reason); + } + + void onExecutionComplete(ActivityStarter starter) { + if (mLastStarter == null) { + mLastStarter = mFactory.obtain(); + } - return starter.setIntent(intent).setReason(reason); + mLastStarter.set(starter); + mFactory.recycle(starter); } /** @@ -142,6 +149,10 @@ public class ActivityStartController { */ void postStartActivityProcessingForLastStarter(ActivityRecord r, int result, ActivityStack targetStack) { + if (mLastStarter == null) { + return; + } + mLastStarter.postStartActivityProcessing(r, result, targetStack); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index dda8e9c18229..69cc3c7aacbc 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -96,11 +96,13 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.service.voice.IVoiceInteractionSession; import android.text.TextUtils; import android.util.EventLog; +import android.util.Pools.SynchronizedPool; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -130,7 +132,6 @@ class ActivityStarter { private final ActivityManagerService mService; private final ActivityStackSupervisor mSupervisor; private final ActivityStartInterceptor mInterceptor; - private final ActivityStartController mController; // Share state variable among methods when starting an activity. @@ -208,18 +209,34 @@ class ActivityStarter { * this instance. * @return an {@link ActivityStarter} */ - ActivityStarter obtainStarter(); + ActivityStarter obtain(); + + /** + * Recycles a starter for reuse. + */ + void recycle(ActivityStarter starter); } /** * Default implementation of {@link StarterFactory}. */ static class DefaultFactory implements Factory { + /** + * The maximum count of starters that should be active at one time: + * 1. last ran starter (for logging and post activity processing) + * 2. current running starter + * 3. starter from re-entry in (2) + */ + private final int MAX_STARTER_COUNT = 3; + private ActivityStartController mController; private ActivityManagerService mService; private ActivityStackSupervisor mSupervisor; private ActivityStartInterceptor mInterceptor; + private SynchronizedPool<ActivityStarter> mStarterPool = + new SynchronizedPool<>(MAX_STARTER_COUNT); + DefaultFactory(ActivityManagerService service, ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { mService = service; @@ -233,9 +250,20 @@ class ActivityStarter { } @Override - public ActivityStarter obtainStarter() { - // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead. - return new ActivityStarter(mController, mService, mSupervisor, mInterceptor); + public ActivityStarter obtain() { + ActivityStarter starter = mStarterPool.acquire(); + + if (starter == null) { + starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor); + } + + return starter; + } + + @Override + public void recycle(ActivityStarter starter) { + starter.reset(true /* clearRequest*/); + mStarterPool.release(starter); } } @@ -288,6 +316,76 @@ class ActivityStarter { * {@see ActivityStarter#startActivityMayWait}. */ boolean mayWait; + + /** + * Sets values back to the initial state, clearing any held references. + */ + void reset() { + caller = null; + intent = null; + ephemeralIntent = null; + resolvedType = null; + activityInfo = null; + resolveInfo = null; + voiceSession = null; + voiceInteractor = null; + resultTo = null; + resultWho = null; + requestCode = 0; + callingPid = 0; + callingUid = 0; + callingPackage = null; + realCallingPid = 0; + realCallingUid = 0; + startFlags = 0; + activityOptions = null; + ignoreTargetSecurity = false; + componentSpecified = false; + outActivity = null; + inTask = null; + reason = null; + profilerInfo = null; + globalConfig = null; + waitOptions = null; + userId = 0; + waitResult = null; + mayWait = false; + } + + /** + * Adopts all values from passed in request. + */ + void set(Request request) { + caller = request.caller; + intent = request.intent; + ephemeralIntent = request.ephemeralIntent; + resolvedType = request.resolvedType; + activityInfo = request.activityInfo; + resolveInfo = request.resolveInfo; + voiceSession = request.voiceSession; + voiceInteractor = request.voiceInteractor; + resultTo = request.resultTo; + resultWho = request.resultWho; + requestCode = request.requestCode; + callingPid = request.callingPid; + callingUid = request.callingUid; + callingPackage = request.callingPackage; + realCallingPid = request.realCallingPid; + realCallingUid = request.realCallingUid; + startFlags = request.startFlags; + activityOptions = request.activityOptions; + ignoreTargetSecurity = request.ignoreTargetSecurity; + componentSpecified = request.componentSpecified; + outActivity = request.outActivity; + inTask = request.inTask; + reason = request.reason; + profilerInfo = request.profilerInfo; + globalConfig = request.globalConfig; + waitOptions = request.waitOptions; + userId = request.userId; + waitResult = request.waitResult; + mayWait = request.mayWait; + } } ActivityStarter(ActivityStartController controller, ActivityManagerService service, @@ -296,6 +394,52 @@ class ActivityStarter { mService = service; mSupervisor = supervisor; mInterceptor = interceptor; + reset(true); + } + + /** + * Effectively duplicates the starter passed in. All state and request values will be + * mirrored. + * @param starter + */ + void set(ActivityStarter starter) { + mStartActivity = starter.mStartActivity; + mIntent = starter.mIntent; + mCallingUid = starter.mCallingUid; + mOptions = starter.mOptions; + + mLaunchTaskBehind = starter.mLaunchTaskBehind; + mLaunchFlags = starter.mLaunchFlags; + mLaunchMode = starter.mLaunchMode; + + mLaunchBounds.set(starter.mLaunchBounds); + + mNotTop = starter.mNotTop; + mDoResume = starter.mDoResume; + mStartFlags = starter.mStartFlags; + mSourceRecord = starter.mSourceRecord; + mPreferredDisplayId = starter.mPreferredDisplayId; + + mInTask = starter.mInTask; + mAddingToTask = starter.mAddingToTask; + mReuseTask = starter.mReuseTask; + + mNewTaskInfo = starter.mNewTaskInfo; + mNewTaskIntent = starter.mNewTaskIntent; + mSourceStack = starter.mSourceStack; + + mTargetStack = starter.mTargetStack; + mMovedToFront = starter.mMovedToFront; + mNoAnimation = starter.mNoAnimation; + mKeepCurTransition = starter.mKeepCurTransition; + mAvoidMoveToFront = starter.mAvoidMoveToFront; + + mVoiceSession = starter.mVoiceSession; + mVoiceInteractor = starter.mVoiceInteractor; + + mIntentDelivered = starter.mIntentDelivered; + + mRequest.set(starter.mRequest); } ActivityRecord getStartActivity() { @@ -313,25 +457,47 @@ class ActivityStarter { * @return The starter result. */ int execute() { - // TODO(b/64750076): Look into passing request directly to these methods to allow - // for transactional diffs and preprocessing. - if (mRequest.mayWait) { - return startActivityMayWait(mRequest.caller, mRequest.callingUid, - mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, - mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, - mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, - mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, - mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId, - mRequest.inTask, mRequest.reason); - } else { - return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, - mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, - mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, - mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, - mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, - mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, - mRequest.ignoreTargetSecurity, mRequest.componentSpecified, - mRequest.outActivity, mRequest.inTask, mRequest.reason); + try { + // TODO(b/64750076): Look into passing request directly to these methods to allow + // for transactional diffs and preprocessing. + if (mRequest.mayWait) { + return startActivityMayWait(mRequest.caller, mRequest.callingUid, + mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, + mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, + mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, + mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, + mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId, + mRequest.inTask, mRequest.reason); + } else { + return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, + mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, + mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, + mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, + mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, + mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, + mRequest.ignoreTargetSecurity, mRequest.componentSpecified, + mRequest.outActivity, mRequest.inTask, mRequest.reason); + } + } finally { + onExecutionComplete(); + } + } + + /** + * Starts an activity based on the provided {@link ActivityRecord} and environment parameters. + * Note that this method is called internally as well as part of {@link #startActivity}. + * + * @return The start result. + */ + int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, + ActivityRecord[] outActivity) { + try { + return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, + doResume, options, inTask, outActivity); + } finally { + onExecutionComplete(); } } @@ -369,6 +535,14 @@ class ActivityStarter { return result != START_ABORTED ? result : START_SUCCESS; } + /** + * Called when execution is complete. Sets state indicating completion and proceeds with + * recycling if appropriate. + */ + private void onExecutionComplete() { + mController.onExecutionComplete(this); + } + private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, @@ -651,7 +825,7 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); - return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, + return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, options, inTask, outActivity); } @@ -924,16 +1098,10 @@ class ActivityStarter { } } - /** - * Starts an activity based on the provided {@link ActivityRecord} and environment parameters. - * Note that this method is called internally as well as part of {@link #startActivity}. - * - * @return The start result. - */ - int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { + private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, + ActivityRecord[] outActivity) { int result = START_CANCELED; try { mService.mWindowManager.deferSurfaceLayout(); @@ -1189,9 +1357,57 @@ class ActivityStarter { return START_SUCCESS; } + /** + * Resets the {@link ActivityStarter} state. + * @param clearRequest whether the request should be reset to default values. + */ + void reset(boolean clearRequest) { + mStartActivity = null; + mIntent = null; + mCallingUid = -1; + mOptions = null; + + mLaunchTaskBehind = false; + mLaunchFlags = 0; + mLaunchMode = INVALID_LAUNCH_MODE; + + mLaunchBounds.setEmpty(); + + mNotTop = null; + mDoResume = false; + mStartFlags = 0; + mSourceRecord = null; + mPreferredDisplayId = INVALID_DISPLAY; + + mInTask = null; + mAddingToTask = false; + mReuseTask = null; + + mNewTaskInfo = null; + mNewTaskIntent = null; + mSourceStack = null; + + mTargetStack = null; + mMovedToFront = false; + mNoAnimation = false; + mKeepCurTransition = false; + mAvoidMoveToFront = false; + + mVoiceSession = null; + mVoiceInteractor = null; + + mIntentDelivered = false; + + if (clearRequest) { + mRequest.reset(); + } + } + private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) { + reset(false /* clearRequest */); + mStartActivity = r; mIntent = r.intent; mOptions = options; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 87690d1961a9..93fb3e3e3e6e 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1335,8 +1335,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub null, mStats.mHandler, null, mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); - checkinStats.dumpProtoLocked(mContext, fd, apps, flags, - historyStart); + checkinStats.dumpProtoLocked(mContext, fd, apps, flags); mStats.mCheckinFile.delete(); return; } @@ -1349,7 +1348,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid()); synchronized (mStats) { - mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart); + mStats.dumpProtoLocked(mContext, fd, apps, flags); if (writeData) { mStats.writeAsyncLocked(); } diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java index c04d103c7ff5..cc70f18dc747 100644 --- a/services/core/java/com/android/server/am/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java @@ -43,6 +43,7 @@ class ClientLifecycleManager { */ void scheduleTransaction(ClientTransaction transaction) throws RemoteException { transaction.schedule(); + transaction.recycle(); } /** @@ -100,7 +101,7 @@ class ClientLifecycleManager { */ private static ClientTransaction transactionWithState(@NonNull IApplicationThread client, @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) { - final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken); + final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken); clientTransaction.setLifecycleStateRequest(stateRequest); return clientTransaction; } @@ -113,7 +114,7 @@ class ClientLifecycleManager { */ private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client, IBinder activityToken, @NonNull ClientTransactionItem callback) { - final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken); + final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken); clientTransaction.addCallback(callback); return clientTransaction; } diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index 28c358582dbe..bd2e96ed4ef2 100644 --- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java +++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java @@ -51,6 +51,7 @@ public class DefaultNetworkMetrics { // Information about the current status of the default network. @GuardedBy("this") private DefaultNetworkEvent mCurrentDefaultNetwork; + // True if the current default network has been validated. @GuardedBy("this") private boolean mIsCurrentlyValid; @GuardedBy("this") @@ -71,6 +72,8 @@ public class DefaultNetworkMetrics { printEvent(localTimeMs, pw, ev); } mCurrentDefaultNetwork.updateDuration(timeMs); + // When printing default network events for bug reports, update validation time + // and refresh the last validation timestmap for future validation time updates. if (mIsCurrentlyValid) { updateValidationTime(timeMs); mLastValidationTimeMs = timeMs; @@ -92,11 +95,13 @@ public class DefaultNetworkMetrics { } public synchronized void logDefaultNetworkValidity(long timeMs, boolean isValid) { + // Transition from valid to invalid: update validity duration since last update if (!isValid && mIsCurrentlyValid) { mIsCurrentlyValid = false; updateValidationTime(timeMs); } + // Transition from invalid to valid: simply mark the validation timestamp. if (isValid && !mIsCurrentlyValid) { mIsCurrentlyValid = true; mLastValidationTimeMs = timeMs; @@ -114,6 +119,9 @@ public class DefaultNetworkMetrics { } private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) { + if (mIsCurrentlyValid) { + updateValidationTime(timeMs); + } DefaultNetworkEvent ev = mCurrentDefaultNetwork; ev.updateDuration(timeMs); ev.previousTransports = mLastTransports; @@ -122,7 +130,6 @@ public class DefaultNetworkMetrics { // The system acquired a new default network. fillLinkInfo(ev, oldNai); ev.finalScore = oldNai.getCurrentScore(); - ev.validatedMs = ev.durationMs; } // Only change transport of the previous default network if the event currently logged // corresponds to an existing default network, and not to the absence of a default network. @@ -143,9 +150,10 @@ public class DefaultNetworkMetrics { fillLinkInfo(ev, newNai); ev.initialScore = newNai.getCurrentScore(); if (newNai.lastValidated) { - mIsCurrentlyValid = true; - mLastValidationTimeMs = timeMs; + logDefaultNetworkValidity(timeMs, true); } + } else { + mIsCurrentlyValid = false; } mCurrentDefaultNetwork = ev; } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index c4e6ff6bc906..6280edb87a02 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -644,6 +644,11 @@ public final class ContentService extends IContentService.Stub { int userId = UserHandle.getCallingUserId(); final int callingUid = Binder.getCallingUid(); + if (request.isPeriodic()) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + } + long identityToken = clearCallingIdentity(); try { SyncStorageEngine.EndPoint info; @@ -653,8 +658,6 @@ public final class ContentService extends IContentService.Stub { info = new SyncStorageEngine.EndPoint(account, provider, userId); if (request.isPeriodic()) { // Remove periodic sync. - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); getSyncManager().removePeriodicSync(info, extras, "cancelRequest() by uid=" + callingUid); } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 9cd52d779be0..c7a2f0b9264e 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -323,7 +323,7 @@ public class SyncManager { private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - EndPoint target = new EndPoint(null, null, context.getUserId()); + EndPoint target = new EndPoint(null, null, getSendingUserId()); updateRunningAccounts(target /* sync targets for user */); } }; @@ -735,15 +735,15 @@ public class SyncManager { } public void onStartUser(int userHandle) { - mLogger.log("onStartUser: user=", userHandle); + mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userHandle)); } public void onUnlockUser(int userHandle) { - mLogger.log("onUnlockUser: user=", userHandle); + mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userHandle)); } public void onStopUser(int userHandle) { - mLogger.log("onStopUser: user=", userHandle); + mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userHandle)); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 19a74d7cdeac..c1bfa478ee89 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -76,6 +76,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.wm.WindowManagerInternal; +import com.android.server.wm.SurfaceAnimationThread; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -292,6 +293,8 @@ public final class DisplayManagerService extends SystemService { Process.THREAD_GROUP_TOP_APP); Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(), Process.THREAD_GROUP_TOP_APP); + Process.setThreadGroupAndCpuset(SurfaceAnimationThread.get().getThreadId(), + Process.THREAD_GROUP_TOP_APP); } @Override diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java index 9bcf208ab3f0..c97eeaf30ab3 100644 --- a/services/core/java/com/android/server/job/JobSchedulerInternal.java +++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java @@ -44,6 +44,11 @@ public interface JobSchedulerInternal { List<JobInfo> getSystemScheduledPendingJobs(); /** + * Cancel the jobs for a given uid (e.g. when app data is cleared) + */ + void cancelJobsForUid(int uid, String reason); + + /** * These are for activity manager to communicate to use what is currently performing backups. */ void addBackingUpUid(int uid); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 3f014b550839..009f10e74336 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -1721,16 +1721,29 @@ public final class JobSchedulerService extends com.android.server.SystemService // If the app is in a non-active standby bucket, make sure we've waited // an appropriate amount of time since the last invocation - if (mHeartbeat < mNextBucketHeartbeat[job.getStandbyBucket()]) { - // TODO: log/trace that we're deferring the job due to bucketing if we hit this - if (job.getWhenStandbyDeferred() == 0) { + final int bucket = job.getStandbyBucket(); + if (mHeartbeat < mNextBucketHeartbeat[bucket]) { + // Only skip this job if it's still waiting for the end of its (initial) nominal + // bucket interval. Once it's waited that long, we let it go ahead and clear. + // The final (NEVER) bucket is special; we never age those apps' jobs into + // runnability. + if (bucket >= mConstants.STANDBY_BEATS.length + || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) { + // TODO: log/trace that we're deferring the job due to bucketing if we hit this + if (job.getWhenStandbyDeferred() == 0) { + if (DEBUG_STANDBY) { + Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " + + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job); + } + job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); + } + return false; + } else { if (DEBUG_STANDBY) { - Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " - + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job); + Slog.v(TAG, "Bucket deferred job aged into runnability at " + + mHeartbeat + " : " + job); } - job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); } - return false; } // The expensive check last: validate that the defined package+service is @@ -1984,6 +1997,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } @Override + public void cancelJobsForUid(int uid, String reason) { + JobSchedulerService.this.cancelJobsForUid(uid, reason); + } + + @Override public void addBackingUpUid(int uid) { synchronized (mLock) { // No need to actually do anything here, since for a full backup the diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 219bc6114c79..36cacd7a7d96 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -1149,8 +1149,10 @@ public final class JobStore { public void forEachJob(JobStatusFunctor functor) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex); - for (int i = jobs.size() - 1; i >= 0; i--) { - functor.process(jobs.valueAt(i)); + if (jobs != null) { + for (int i = jobs.size() - 1; i >= 0; i--) { + functor.process(jobs.valueAt(i)); + } } } } diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index 4525a4982d44..cd4bb14ee91f 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -742,6 +742,82 @@ public class ContextHubService extends IContextHubService.Stub { return mClientManager.registerClient(clientCallback, contextHubId); } + /** + * Loads a nanoapp binary at the specified Context hub. + * + * @param contextHubId the ID of the hub to load the binary + * @param transactionCallback the client-facing transaction callback interface + * @param nanoAppBinary the binary to load + * + * @throws RemoteException + */ + @Override + public void loadNanoAppOnHub( + int contextHubId, IContextHubTransactionCallback transactionCallback, + NanoAppBinary nanoAppBinary) throws RemoteException { + checkPermissions(); + if (!checkHalProxyAndContextHubId( + contextHubId, transactionCallback, ContextHubTransaction.TYPE_LOAD_NANOAPP)) { + return; + } + if (nanoAppBinary == null) { + Log.e(TAG, "NanoAppBinary cannot be null in loadNanoAppOnHub"); + transactionCallback.onTransactionComplete( + ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS); + return; + } + + ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction( + contextHubId, nanoAppBinary, transactionCallback); + addTransaction(transaction); + } + + /** + * Unloads a nanoapp from the specified Context Hub. + * + * @param contextHubId the ID of the hub to unload the nanoapp + * @param transactionCallback the client-facing transaction callback interface + * @param nanoAppId the ID of the nanoapp to unload + * + * @throws RemoteException + */ + @Override + public void unloadNanoAppFromHub( + int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) + throws RemoteException { + checkPermissions(); + if (!checkHalProxyAndContextHubId( + contextHubId, transactionCallback, ContextHubTransaction.TYPE_UNLOAD_NANOAPP)) { + return; + } + + ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction( + contextHubId, nanoAppId, transactionCallback); + addTransaction(transaction); + } + + /** + * Queries for a list of nanoapps from the specified Context hub. + * + * @param contextHubId the ID of the hub to query + * @param transactionCallback the client-facing transaction callback interface + * + * @throws RemoteException + */ + @Override + public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback) + throws RemoteException { + checkPermissions(); + if (!checkHalProxyAndContextHubId( + contextHubId, transactionCallback, ContextHubTransaction.TYPE_QUERY_NANOAPPS)) { + return; + } + + ContextHubServiceTransaction transaction = + mTransactionManager.createQueryTransaction(contextHubId, transactionCallback); + addTransaction(transaction); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -797,6 +873,42 @@ public class ContextHubService extends IContextHubService.Stub { return 0; } + /** + * Validates the HAL proxy state and context hub ID to see if we can start the transaction. + * + * @param contextHubId the ID of the hub to start the transaction + * @param callback the client transaction callback interface + * @param transactionType the type of the transaction + * + * @return {@code true} if mContextHubProxy and contextHubId is valid, {@code false} otherwise + */ + private boolean checkHalProxyAndContextHubId( + int contextHubId, IContextHubTransactionCallback callback, + @ContextHubTransaction.Type int transactionType) { + if (mContextHubProxy == null) { + try { + callback.onTransactionComplete( + ContextHubTransaction.TRANSACTION_FAILED_HAL_UNAVAILABLE); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling onTransactionComplete", e); + } + return false; + } + if (!isValidContextHubId(contextHubId)) { + Log.e(TAG, "Cannot start " + + ContextHubTransaction.typeToString(transactionType, false /* upperCase */) + + " transaction for invalid hub ID " + contextHubId); + try { + callback.onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling onTransactionComplete", e); + } + return false; + } + + return true; + } + private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) { // App Id encodes vendor & version NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo(); diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java index a543dafc1ed1..ce92f722e2d2 100644 --- a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java +++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java @@ -126,28 +126,9 @@ import java.util.concurrent.TimeUnit; return mIsComplete; } - /** - * @return the human-readable string of this transaction's type - */ - private String getTransactionTypeString() { - switch (mTransactionType) { - case ContextHubTransaction.TYPE_LOAD_NANOAPP: - return "Load"; - case ContextHubTransaction.TYPE_UNLOAD_NANOAPP: - return "Unload"; - case ContextHubTransaction.TYPE_ENABLE_NANOAPP: - return "Enable"; - case ContextHubTransaction.TYPE_DISABLE_NANOAPP: - return "Disable"; - case ContextHubTransaction.TYPE_QUERY_NANOAPPS: - return "Query"; - default: - return "Unknown"; - } - } - @Override public String toString() { - return getTransactionTypeString() + " transaction (ID = " + mTransactionId + ")"; + return ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */) + + " transaction (ID = " + mTransactionId + ")"; } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 2940a6e3fc8d..2041de64c8fb 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -370,10 +370,10 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (!isOverlayPackage(pi)) { - mImpl.onTargetPackageAdded(packageName, userId); - } else { + if (pi.isOverlayPackage()) { mImpl.onOverlayPackageAdded(packageName, userId); + } else { + mImpl.onTargetPackageAdded(packageName, userId); } } } @@ -388,10 +388,10 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (!isOverlayPackage(pi)) { - mImpl.onTargetPackageChanged(packageName, userId); - } else { + if (pi.isOverlayPackage()) { mImpl.onOverlayPackageChanged(packageName, userId); + } else { + mImpl.onTargetPackageChanged(packageName, userId); } } } @@ -404,10 +404,10 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { mPackageManager.forgetPackageInfo(packageName, userId); final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi == null) { - mImpl.onTargetPackageUpgrading(packageName, userId); - } else { + if (oi != null) { mImpl.onOverlayPackageUpgrading(packageName, userId); + } else { + mImpl.onTargetPackageUpgrading(packageName, userId); } } } @@ -421,10 +421,10 @@ public final class OverlayManagerService extends SystemService { false); if (pi != null) { mPackageManager.cachePackageInfo(packageName, userId, pi); - if (!isOverlayPackage(pi)) { - mImpl.onTargetPackageUpgraded(packageName, userId); - } else { + if (pi.isOverlayPackage()) { mImpl.onOverlayPackageUpgraded(packageName, userId); + } else { + mImpl.onTargetPackageUpgraded(packageName, userId); } } } @@ -437,10 +437,10 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { mPackageManager.forgetPackageInfo(packageName, userId); final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi == null) { - mImpl.onTargetPackageRemoved(packageName, userId); - } else { + if (oi != null) { mImpl.onOverlayPackageRemoved(packageName, userId); + } else { + mImpl.onTargetPackageRemoved(packageName, userId); } } } @@ -668,10 +668,6 @@ public final class OverlayManagerService extends SystemService { } }; - private boolean isOverlayPackage(@NonNull final PackageInfo pi) { - return pi != null && pi.overlayTarget != null; - } - private final class OverlayChangeListener implements OverlayManagerServiceImpl.OverlayChangeListener { @Override diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index db6e9749535b..253d4f5b22bb 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -102,11 +102,11 @@ final class OverlayManagerServiceImpl { mSettings.init(overlayPackage.packageName, newUserId, overlayPackage.overlayTarget, overlayPackage.applicationInfo.getBaseCodePath(), - overlayPackage.isStaticOverlay, overlayPackage.overlayPriority); + overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); if (oi == null) { // This overlay does not exist in our settings. - if (overlayPackage.isStaticOverlay || + if (overlayPackage.isStaticOverlayPackage() || mDefaultOverlays.contains(overlayPackage.packageName)) { // Enable this overlay by default. if (DEBUG) { @@ -255,8 +255,8 @@ final class OverlayManagerServiceImpl { mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId); mSettings.init(packageName, userId, overlayPackage.overlayTarget, - overlayPackage.applicationInfo.getBaseCodePath(), overlayPackage.isStaticOverlay, - overlayPackage.overlayPriority); + overlayPackage.applicationInfo.getBaseCodePath(), + overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority); try { if (updateState(targetPackage, overlayPackage, userId)) { mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId); @@ -313,7 +313,7 @@ final class OverlayManagerServiceImpl { } // Ignore static overlays. - if (overlayPackage.isStaticOverlay) { + if (overlayPackage.isStaticOverlayPackage()) { return false; } @@ -363,7 +363,7 @@ final class OverlayManagerServiceImpl { continue; } - if (disabledOverlayPackageInfo.isStaticOverlay) { + if (disabledOverlayPackageInfo.isStaticOverlayPackage()) { // Don't touch static overlays. continue; } @@ -388,7 +388,7 @@ final class OverlayManagerServiceImpl { private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null || overlayPackage.isStaticOverlay) { + if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) { return false; } return true; @@ -483,7 +483,8 @@ final class OverlayManagerServiceImpl { throws OverlayManagerSettings.BadKeyException { // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. if (targetPackage != null && - !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) { + !("android".equals(targetPackage.packageName) + && overlayPackage.isStaticOverlayPackage())) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8b2854c56977..5dfb48a70472 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -607,6 +607,12 @@ public class PackageManagerService extends IPackageManager.Stub */ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; + /** + * Permissions required in order to receive instant application lifecycle broadcasts. + */ + private static final String[] INSTANT_APP_BROADCAST_PERMISSION = + new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS }; + final ServiceThread mHandlerThread; final PackageHandler mHandler; @@ -1967,16 +1973,20 @@ public class PackageManagerService extends IPackageManager.Stub // Determine the set of users who are adding this package for // the first time vs. those who are seeing an update. - int[] firstUsers = EMPTY_INT_ARRAY; - int[] updateUsers = EMPTY_INT_ARRAY; + int[] firstUserIds = EMPTY_INT_ARRAY; + int[] firstInstantUserIds = EMPTY_INT_ARRAY; + int[] updateUserIds = EMPTY_INT_ARRAY; + int[] instantUserIds = EMPTY_INT_ARRAY; final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0; final PackageSetting ps = (PackageSetting) res.pkg.mExtras; for (int newUser : res.newUsers) { - if (ps.getInstantApp(newUser)) { - continue; - } + final boolean isInstantApp = ps.getInstantApp(newUser); if (allNewUsers) { - firstUsers = ArrayUtils.appendInt(firstUsers, newUser); + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } continue; } boolean isNew = true; @@ -1987,9 +1997,17 @@ public class PackageManagerService extends IPackageManager.Stub } } if (isNew) { - firstUsers = ArrayUtils.appendInt(firstUsers, newUser); + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } } else { - updateUsers = ArrayUtils.appendInt(updateUsers, newUser); + if (isInstantApp) { + instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser); + } else { + updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser); + } } } @@ -2002,7 +2020,7 @@ public class PackageManagerService extends IPackageManager.Stub int appId = UserHandle.getAppId(res.uid); boolean isSystem = res.pkg.applicationInfo.isSystemApp(); sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, - virtualPreload /*startReceiver*/, appId, firstUsers); + virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds); // Send added for users that don't see the package for the first time Bundle extras = new Bundle(1); @@ -2012,11 +2030,13 @@ public class PackageManagerService extends IPackageManager.Stub } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, updateUsers); + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, updateUsers); + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds); } // Send replaced for users that don't see the package for the first time @@ -2024,24 +2044,26 @@ public class PackageManagerService extends IPackageManager.Stub sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, - updateUsers); + updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, updateUsers); + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds); } sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null /*package*/, null /*extras*/, 0 /*flags*/, packageName /*targetPackage*/, - null /*finishedReceiver*/, updateUsers); + null /*finishedReceiver*/, updateUserIds, instantUserIds); } else if (launchedForRestore && !isSystemApp(res.pkg)) { // First-install and we did a restore, so we're responsible for the // first-launch broadcast. if (DEBUG_BACKUP) { Slog.i(TAG, "Post-restore of " + packageName - + " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers)); + + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); } - sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers); + sendFirstLaunchBroadcast(packageName, installerPackage, + firstUserIds, firstInstantUserIds); } // Send broadcast package appeared if forward locked/external for all users @@ -2059,9 +2081,9 @@ public class PackageManagerService extends IPackageManager.Stub } // Work that needs to happen on first install within each user - if (firstUsers != null && firstUsers.length > 0) { + if (firstUserIds != null && firstUserIds.length > 0) { synchronized (mPackages) { - for (int userId : firstUsers) { + for (int userId : firstUserIds) { // If this app is a browser and it's newly-installed for some // users, clear any default-browser state in those users. The // app's nature doesn't depend on the user, so we can just check @@ -2099,7 +2121,7 @@ public class PackageManagerService extends IPackageManager.Stub // should not change. // Don't notify the manager for ephemeral apps as they are not expected to // survive long enough to benefit of background optimizations. - for (int userId : firstUsers) { + for (int userId : firstUserIds) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); // There's a race currently where some install events may interleave with an uninstall. // This can lead to package info being null (b/36642664). @@ -9714,6 +9736,7 @@ public class PackageManagerService extends IPackageManager.Stub // them in the case where we're not upgrading or booting for the first time. String primaryCpuAbiFromSettings = null; String secondaryCpuAbiFromSettings = null; + boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; // writer synchronized (mPackages) { @@ -9791,11 +9814,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) { + if (!needToDeriveAbi) { PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName); if (foundPs != null) { primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString; secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString; + } else { + // when re-adding a system package failed after uninstalling updates. + needToDeriveAbi = true; } } @@ -10011,7 +10037,7 @@ public class PackageManagerService extends IPackageManager.Stub final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { - if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) { + if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir); @@ -12773,9 +12799,10 @@ public class PackageManagerService extends IPackageManager.Stub } }; + @Override public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, - final int[] userIds) { + final int[] userIds, int[] instantUserIds) { mHandler.post(new Runnable() { @Override public void run() { @@ -12788,33 +12815,11 @@ public class PackageManagerService extends IPackageManager.Stub } else { resolvedUserIds = userIds; } - for (int id : resolvedUserIds) { - final Intent intent = new Intent(action, - pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); - if (extras != null) { - intent.putExtras(extras); - } - if (targetPkg != null) { - intent.setPackage(targetPkg); - } - // Modify the UID when posting to other users - int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (uid > 0 && UserHandle.getUserId(uid) != id) { - uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); - intent.putExtra(Intent.EXTRA_UID, uid); - } - intent.putExtra(Intent.EXTRA_USER_HANDLE, id); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); - if (DEBUG_BROADCASTS) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "Sending to user " + id + ": " - + intent.toShortString(false, true, false, false) - + " " + intent.getExtras(), here); - } - am.broadcastIntent(null, intent, null, finishedReceiver, - 0, null, null, null, android.app.AppOpsManager.OP_NONE, - null, finishedReceiver != null, false, id); + doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver, + resolvedUserIds, false); + if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) { + doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver, + instantUserIds, true); } } catch (RemoteException ex) { } @@ -12823,6 +12828,49 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Sends a broadcast for the given action. + * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with + * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows + * the system and applications allowed to see instant applications to receive package + * lifecycle events for instant applications. + */ + private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras, + int flags, String targetPkg, IIntentReceiver finishedReceiver, + int[] userIds, boolean isInstantApp) + throws RemoteException { + for (int id : userIds) { + final Intent intent = new Intent(action, + pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); + final String[] requiredPermissions = + isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null; + if (extras != null) { + intent.putExtras(extras); + } + if (targetPkg != null) { + intent.setPackage(targetPkg); + } + // Modify the UID when posting to other users + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid > 0 && UserHandle.getUserId(uid) != id) { + uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); + intent.putExtra(Intent.EXTRA_UID, uid); + } + intent.putExtra(Intent.EXTRA_USER_HANDLE, id); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); + if (DEBUG_BROADCASTS) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Sending to user " + id + ": " + + intent.toShortString(false, true, false, false) + + " " + intent.getExtras(), here); + } + am.broadcastIntent(null, intent, null, finishedReceiver, + 0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE, + null, finishedReceiver != null, false, id); + } + } + + /** * Check if the external storage media is available. This is true if there * is a mounted external storage medium or if the external storage is * emulated. @@ -12836,14 +12884,13 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } - // writer - synchronized (mPackages) { - if (!isExternalMediaAvailable()) { + if (!isExternalMediaAvailable()) { // If the external storage is no longer mounted at this point, // the caller may not have been able to delete all of this // packages files and can not delete any more. Bail. - return null; - } + return null; + } + synchronized (mPackages) { final ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned; if (lastPackage != null) { pkgs.remove(lastPackage); @@ -13071,8 +13118,11 @@ public class PackageManagerService extends IPackageManager.Stub private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting, int userId) { final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting); + final boolean isInstantApp = pkgSetting.getInstantApp(userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/, - false /*startReceiver*/, pkgSetting.appId, userId); + false /*startReceiver*/, pkgSetting.appId, userIds, instantUserIds); // Send a session commit broadcast final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo(); @@ -13081,18 +13131,21 @@ public class PackageManagerService extends IPackageManager.Stub sendSessionCommitBroadcast(info, userId); } + @Override public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, - boolean includeStopped, int appId, int... userIds) { - if (ArrayUtils.isEmpty(userIds)) { + boolean includeStopped, int appId, int[] userIds, int[] instantUserIds) { + if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { return; } Bundle extras = new Bundle(1); // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast - extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId)); + final int uid = UserHandle.getUid( + (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId); + extras.putInt(Intent.EXTRA_UID, uid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - packageName, extras, 0, null, null, userIds); - if (sendBootCompleted) { + packageName, extras, 0, null, null, userIds, instantUserIds); + if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { sendBootCompletedBroadcastToSystemApp( @@ -13236,7 +13289,7 @@ public class PackageManagerService extends IPackageManager.Stub suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, - new int[] {userId}); + new int[] {userId}, null); } } @@ -14124,7 +14177,8 @@ public class PackageManagerService extends IPackageManager.Stub * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL * handling. */ - void notifyFirstLaunch(final String pkgName, final String installerPackage, final int userId) { + void notifyFirstLaunch(final String packageName, final String installerPackage, + final int userId) { // Serialize this with the rest of the install-process message chain. In the // restore-at-install case, this Runnable will necessarily run before the // POST_INSTALL message is processed, so the contents of mRunningInstalls @@ -14139,12 +14193,12 @@ public class PackageManagerService extends IPackageManager.Stub if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) { continue; } - if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) { + if (packageName.equals(data.res.pkg.applicationInfo.packageName)) { // right package; but is it for the right user? for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) { if (userId == data.res.newUsers[uIndex]) { if (DEBUG_BACKUP) { - Slog.i(TAG, "Package " + pkgName + Slog.i(TAG, "Package " + packageName + " being restored so deferring FIRST_LAUNCH"); } return; @@ -14154,16 +14208,20 @@ public class PackageManagerService extends IPackageManager.Stub } // didn't find it, so not being restored if (DEBUG_BACKUP) { - Slog.i(TAG, "Package " + pkgName + " sending normal FIRST_LAUNCH"); + Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH"); } - sendFirstLaunchBroadcast(pkgName, installerPackage, new int[] {userId}); + final boolean isInstantApp = isInstantApp(packageName, userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; + sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds); } }); } - private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds) { + private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, + int[] userIds, int[] instantUserIds) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, - installerPkg, null, userIds); + installerPkg, null, userIds, instantUserIds); } private abstract class HandlerParams { @@ -17310,6 +17368,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] origUsers; int[] removedUsers = null; int[] broadcastUsers = null; + int[] instantUserIds = null; SparseArray<Integer> installReasons; boolean isRemovedPackageSystemUpdate = false; boolean isUpdate; @@ -17355,7 +17414,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i); packageSender.sendPackageAddedForNewUsers(installedInfo.name, true /*sendBootCompleted*/, false /*startReceiver*/, - UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers); + UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers, null); } } @@ -17364,18 +17423,18 @@ public class PackageManagerService extends IPackageManager.Stub extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, - removedPackage, extras, 0, null /*targetPackage*/, null, null); + removedPackage, extras, 0, null /*targetPackage*/, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - removedPackage, extras, 0, null /*targetPackage*/, null, null); + removedPackage, extras, 0, null /*targetPackage*/, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, - null, null, 0, removedPackage, null, null); + null, null, 0, removedPackage, null, null, null); if (installerPackageName != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, null); + installerPackageName, null, null, null); packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, null); + installerPackageName, null, null, null); } } @@ -17396,23 +17455,24 @@ public class PackageManagerService extends IPackageManager.Stub extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers); if (removedPackage != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, - removedPackage, extras, 0, null /*targetPackage*/, null, broadcastUsers); + removedPackage, extras, 0, null /*targetPackage*/, null, + broadcastUsers, instantUserIds); if (installerPackageName != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, 0 /*flags*/, - installerPackageName, null, broadcastUsers); + installerPackageName, null, broadcastUsers, instantUserIds); } if (dataRemoved && !isRemovedPackageSystemUpdate) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, broadcastUsers); + null, null, broadcastUsers, instantUserIds); } } if (removedAppId >= 0) { packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, - null, null, broadcastUsers); + null, null, broadcastUsers, instantUserIds); } } @@ -17424,12 +17484,14 @@ public class PackageManagerService extends IPackageManager.Stub } broadcastUsers = EMPTY_INT_ARRAY; + instantUserIds = EMPTY_INT_ARRAY; for (int i = userIds.length - 1; i >= 0; --i) { final int userId = userIds[i]; if (deletedPackageSetting.getInstantApp(userId)) { - continue; + instantUserIds = ArrayUtils.appendInt(instantUserIds, userId); + } else { + broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId); } - broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId); } } } @@ -19975,8 +20037,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // little component state change. final int flags = !componentNames.contains(packageName) ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0; + final int userId = UserHandle.getUserId(packageUid); + final boolean isInstantApp = isInstantApp(packageName, userId); + final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; + final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, - new int[] {UserHandle.getUserId(packageUid)}); + userIds, instantUserIds); } @Override @@ -21095,7 +21161,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null); + sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null); } } @@ -23324,9 +23390,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } interface PackageSender { + /** + * @param userIds User IDs where the action occurred on a full application + * @param instantUserIds User IDs where the action occurred on an instant application + */ void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds); + final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds); void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, - boolean includeStopped, int appId, int... userIds); + boolean includeStopped, int appId, int[] userIds, int[] instantUserIds); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a7cced75bfd6..2d82c469592e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1374,7 +1374,7 @@ class PackageManagerShellCommand extends ShellCommand { } ClearDataObserver obs = new ClearDataObserver(); - ActivityManager.getService().clearApplicationUserData(pkg, obs, userId); + ActivityManager.getService().clearApplicationUserData(pkg, false, obs, userId); synchronized (obs) { while (!obs.finished) { try { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 0907dd7c5a9e..e614d72d660f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -330,7 +330,10 @@ public class ShortcutService extends IShortcutService.Stub { | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES; - @GuardedBy("mLock") + /** + * Note we use a fine-grained lock for {@link #mUnlockedUsers} due to b/64303666. + */ + @GuardedBy("mUnlockedUsers") final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray(); // Stats @@ -600,7 +603,7 @@ public class ShortcutService extends IShortcutService.Stub { if (DEBUG) { Slog.d(TAG, "handleUnlockUser: user=" + userId); } - synchronized (mLock) { + synchronized (mUnlockedUsers) { mUnlockedUsers.put(userId, true); } @@ -628,7 +631,9 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { unloadUserLocked(userId); - mUnlockedUsers.put(userId, false); + synchronized (mUnlockedUsers) { + mUnlockedUsers.put(userId, false); + } } } @@ -1149,9 +1154,12 @@ public class ShortcutService extends IShortcutService.Stub { // Requires mLock held, but "Locked" prefix would look weired so we just say "L". protected boolean isUserUnlockedL(@UserIdInt int userId) { // First, check the local copy. - if (mUnlockedUsers.get(userId)) { - return true; + synchronized (mUnlockedUsers) { + if (mUnlockedUsers.get(userId)) { + return true; + } } + // If the local copy says the user is locked, check with AM for the actual state, since // the user might just have been unlocked. // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false @@ -2739,6 +2747,26 @@ public class ShortcutService extends IShortcutService.Stub { public boolean isRequestPinItemSupported(int callingUserId, int requestType) { return ShortcutService.this.isRequestPinItemSupported(callingUserId, requestType); } + + @Override + public boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid) { + Preconditions.checkNotNull(callingPackage); + + final int userId = UserHandle.getUserId(callingUid); + final ComponentName defaultLauncher = getDefaultLauncher(userId); + if (defaultLauncher == null) { + return false; + } + if (!callingPackage.equals(defaultLauncher.getPackageName())) { + return false; + } + synchronized (mLock) { + if (!isUidForegroundLocked(callingUid)) { + return false; + } + } + return true; + } } final BroadcastReceiver mReceiver = new BroadcastReceiver() { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9a7e72a0455a..61591bbef08d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -48,6 +48,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; @@ -607,6 +608,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { PointerLocationView mPointerLocationView; + boolean mEmulateDisplayCutout = false; + // During layout, the layer at which the doc window is placed. int mDockLayer; // During layout, this is the layer of the status bar. @@ -965,6 +968,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POLICY_CONTROL), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.EMULATE_DISPLAY_CUTOUT), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -2396,6 +2402,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mImmersiveModeConfirmation != null) { mImmersiveModeConfirmation.loadSetting(mCurrentUserId); } + mEmulateDisplayCutout = Settings.Global.getInt(resolver, + Settings.Global.EMULATE_DISPLAY_CUTOUT, + Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF) + != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF; } synchronized (mWindowManagerFuncs.getWindowManagerLock()) { PolicyControl.reloadFromSetting(mContext); @@ -4436,7 +4446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { - displayFrames.onBeginLayout(); + displayFrames.onBeginLayout(mEmulateDisplayCutout, mStatusBarHeight); // TODO(multi-display): This doesn't seem right...Maybe only apply to default display? mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -4506,6 +4516,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } layoutScreenDecorWindows(displayFrames, pf, df, dcf); + + if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, + displayFrames.mStable.top); + } } private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) { @@ -4641,11 +4659,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Rect dockFrame = displayFrames.mDock; mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); + final Rect cutoutSafeUnrestricted = mTmpRect; + cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); + cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { // It's a system nav bar or a portrait screen; nav bar goes on bottom. - final int top = displayFrames.mUnrestricted.bottom + final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); - mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom); + mTmpNavigationFrame.set(0, top, displayWidth, cutoutSafeUnrestricted.bottom); displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4666,9 +4688,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { // Landscape screen; nav bar goes to the right. - final int left = displayFrames.mUnrestricted.right + final int left = cutoutSafeUnrestricted.right - getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); + mTmpNavigationFrame.set(left, 0, cutoutSafeUnrestricted.right, displayHeight); displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4689,9 +4711,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else if (mNavigationBarPosition == NAV_BAR_LEFT) { // Seascape screen; nav bar goes to the left. - final int right = displayFrames.mUnrestricted.left + final int right = cutoutSafeUnrestricted.left + getNavigationBarWidth(rotation, uiMode); - mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); + mTmpNavigationFrame.set(cutoutSafeUnrestricted.left, 0, right, displayHeight); displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -4843,6 +4865,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); + final long fl2 = attrs.flags2; final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(win, null); @@ -4863,6 +4886,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int adjust = sim & SOFT_INPUT_MASK_ADJUST; + final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 + || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 + || (requestedSysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0; + + final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; + final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; + final boolean layoutInCutout = (fl2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0; + sf.set(displayFrames.mStable); if (type == TYPE_INPUT_METHOD) { @@ -4872,7 +4903,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { df.set(displayFrames.mDock); pf.set(displayFrames.mDock); // IM dock windows layout below the nav bar... - pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; + pf.bottom = df.bottom = of.bottom = Math.min(displayFrames.mUnrestricted.bottom, + displayFrames.mDisplayCutoutSafe.bottom); // ...with content insets above the nav bar cf.bottom = vf.bottom = displayFrames.mStable.bottom; if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { @@ -4943,8 +4975,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) - == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { + if (layoutInScreen && layoutInsetDecor) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR"); // This is the case for a normal activity window: we want it to cover all of the @@ -5021,6 +5052,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // moving from a window that is not hiding the status bar to one that is. cf.set(displayFrames.mRestricted); } + if (requestedFullscreen && !layoutInCutout) { + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + } applyStableConstraints(sysUiFl, fl, cf, displayFrames); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); @@ -5028,7 +5062,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.set(cf); } } - } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl + } else if (layoutInScreen || (sysUiFl & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() @@ -5106,6 +5140,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); + if (requestedFullscreen && !layoutInCutout) { + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + } } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); @@ -5176,9 +5213,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.set(cf); } } + pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); } } + // Ensure that windows that did not request to be laid out in the cutout don't get laid + // out there. + if (!layoutInCutout) { + final Rect displayCutoutSafeExceptMaybeTop = mTmpRect; + displayCutoutSafeExceptMaybeTop.set(displayFrames.mDisplayCutoutSafe); + if (layoutInScreen && layoutInsetDecor) { + // At the top we have the status bar, so apps that are + // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR already expect that there's an inset + // there and we don't need to exclude the window from that area. + displayCutoutSafeExceptMaybeTop.top = Integer.MIN_VALUE; + } + pf.intersectUnchecked(displayCutoutSafeExceptMaybeTop); + } + + // Content should never appear in the cutout. + cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. // Also, we don't allow windows in multi-window mode to extend out of the screen. if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index 1c54320f25b8..c362c800a83d 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -22,11 +22,14 @@ import android.app.timezone.RulesUpdaterContract; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; +import android.os.FileUtils; +import android.os.SystemClock; import android.provider.TimeZoneRulesDataContract; import android.util.Slog; import java.io.File; import java.io.PrintWriter; +import java.time.Clock; /** * Monitors the installed applications associated with time zone updates. If the app packages are @@ -58,7 +61,7 @@ public class PackageTracker { private final IntentHelper mIntentHelper; private final ConfigHelper mConfigHelper; private final PackageStatusStorage mPackageStatusStorage; - private final ClockHelper mClockHelper; + private final Clock mElapsedRealtimeClock; // False if tracking is disabled. private boolean mTrackingEnabled; @@ -91,15 +94,15 @@ public class PackageTracker { /** Creates the {@link PackageTracker} for normal use. */ static PackageTracker create(Context context) { + Clock elapsedRealtimeClock = SystemClock.elapsedRealtimeClock(); PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context); - // TODO(nfuller): Switch to FileUtils.createDir() when available. http://b/31008728 - File storageDir = new File(Environment.getDataSystemDirectory(), "timezone"); + File storageDir = FileUtils.createDir(Environment.getDataSystemDirectory(), "timezone"); if (!storageDir.exists()) { storageDir.mkdir(); } return new PackageTracker( - helperImpl /* clock */, + elapsedRealtimeClock /* elapsedRealtimeClock */, helperImpl /* configHelper */, helperImpl /* packageManagerHelper */, new PackageStatusStorage(storageDir), @@ -107,10 +110,10 @@ public class PackageTracker { } // A constructor that can be used by tests to supply mocked / faked dependencies. - PackageTracker(ClockHelper clockHelper, ConfigHelper configHelper, + PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper, PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage, IntentHelper intentHelper) { - mClockHelper = clockHelper; + mElapsedRealtimeClock = elapsedRealtimeClock; mConfigHelper = configHelper; mPackageManagerHelper = packageManagerHelper; mPackageStatusStorage = packageStatusStorage; @@ -425,7 +428,7 @@ public class PackageTracker { } private void setCheckInProgress() { - mLastTriggerTimestamp = mClockHelper.currentTimestamp(); + mLastTriggerTimestamp = mElapsedRealtimeClock.millis(); } private void setCheckComplete() { @@ -441,7 +444,7 @@ public class PackageTracker { return false; } // Risk of overflow, but highly unlikely given the implementation and not problematic. - return mClockHelper.currentTimestamp() > mLastTriggerTimestamp + mCheckTimeAllowedMillis; + return mElapsedRealtimeClock.millis() > mLastTriggerTimestamp + mCheckTimeAllowedMillis; } private PackageVersions lookupInstalledPackageVersions() { diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java index 6a330e6acf08..5f90be134b8d 100644 --- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java +++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; -import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; @@ -34,7 +33,7 @@ import java.util.List; /** * A single class that implements multiple helper interfaces for use by {@link PackageTracker}. */ -final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, PackageManagerHelper { +final class PackageTrackerHelperImpl implements ConfigHelper, PackageManagerHelper { private static final String TAG = "PackageTrackerHelperImpl"; @@ -74,13 +73,6 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa } @Override - public long currentTimestamp() { - // Use of elapsedRealtime() because this is in-memory state and elapsedRealtime() shouldn't - // change if the system clock changes. - return SystemClock.elapsedRealtime(); - } - - @Override public long getInstalledPackageVersion(String packageName) throws PackageManager.NameNotFoundException { int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java index 0cf61c0c7c5a..e8a401e79235 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java +++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java @@ -16,6 +16,8 @@ package com.android.server.timezone; +import com.android.internal.util.DumpUtils; + import android.content.Context; import android.content.pm.PackageManager; import android.os.AsyncTask; @@ -46,15 +48,7 @@ final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor @Override public boolean checkDumpPermission(String tag, PrintWriter pw) { - // TODO(nfuller): Switch to DumpUtils.checkDumpPermission() when it is available in AOSP. - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump LocationManagerService from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return false; - } - return true; + return DumpUtils.checkDumpPermission(mContext, tag, pw); } @Override diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 1f4e64e8b1b3..7d55b68f2c96 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -59,6 +59,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import com.android.server.wm.WindowManagerInternal; +import android.view.inputmethod.InputMethodManagerInternal; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -609,6 +610,14 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override + public void setVrInputMethod(ComponentName componentName) { + enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS); + InputMethodManagerInternal imm = + LocalServices.getService(InputMethodManagerInternal.class); + imm.startVrInputMethodNoCheck(componentName); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java new file mode 100644 index 000000000000..84d47b4d66b5 --- /dev/null +++ b/services/core/java/com/android/server/wm/AnimationAdapter.java @@ -0,0 +1,69 @@ +/* + * 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 android.annotation.ColorInt; +import android.graphics.Point; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Animation; + +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +/** + * Interface that describes an animation and bridges the animation start to the component + * responsible for running the animation. + */ +interface AnimationAdapter { + + /** + * @return Whether we should detach the wallpaper during the animation. + * @see Animation#setDetachWallpaper + */ + boolean getDetachWallpaper(); + + /** + * @return The background color behind the animation. + */ + @ColorInt int getBackgroundColor(); + + /** + * Requests to start the animation. + * + * @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an + * overview of the mechanism. This surface needs to be released by the + * component running the animation after {@code finishCallback} has been + * invoked, or after the animation was cancelled. + * @param t The Transaction to apply the initial frame of the animation. + * @param finishCallback The callback to be invoked when the animation has finished. + */ + void startAnimation(SurfaceControl animationLeash, Transaction t, + OnAnimationFinishedCallback finishCallback); + + /** + * Called when the animation that was started with {@link #startAnimation} was cancelled by the + * window manager. + * + * @param animationLeash The leash passed to {@link #startAnimation}. + */ + void onAnimationCancelled(SurfaceControl animationLeash); + + /** + * @return The approximate duration of the animation, in milliseconds. + */ + long getDurationHint(); +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ca8ffca00ee7..eda8fec7ab39 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -135,6 +135,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.util.DisplayMetrics; import android.util.MutableBoolean; import android.util.Slog; @@ -178,20 +179,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** The containers below are the only child containers the display can have. */ // Contains all window containers that are related to apps (Activities) - private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(); + private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService); // Contains all non-app window containers that should be displayed above the app containers // (e.g. Status bar) private final AboveAppWindowContainers mAboveAppWindowsContainers = - new AboveAppWindowContainers("mAboveAppWindowsContainers"); + new AboveAppWindowContainers("mAboveAppWindowsContainers", mService); // Contains all non-app window containers that should be displayed below the app containers // (e.g. Wallpaper). private final NonAppWindowContainers mBelowAppWindowsContainers = - new NonAppWindowContainers("mBelowAppWindowsContainers"); + new NonAppWindowContainers("mBelowAppWindowsContainers", mService); // Contains all IME window containers. Note that the z-ordering of the IME windows will depend // on the IME target. We mainly have this container grouping so we can keep track of all the IME // window containers together and move them in-sync if/when needed. private final NonAppWindowContainers mImeWindowsContainers = - new NonAppWindowContainers("mImeWindowsContainers"); + new NonAppWindowContainers("mImeWindowsContainers", mService); private WindowState mTmpWindow; private WindowState mTmpWindow2; @@ -317,8 +318,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Used for handing back size of display */ private final Rect mTmpBounds = new Rect(); - WindowManagerService mService; - /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; @@ -765,6 +764,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ DisplayContent(Display display, WindowManagerService service, WallpaperController wallpaperController) { + super(service); if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) { throw new IllegalArgumentException("Display with ID=" + display.getDisplayId() + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId()) @@ -777,7 +777,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo display.getDisplayInfo(mDisplayInfo); display.getMetrics(mDisplayMetrics); isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; - mService = service; mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo); initializeDisplayBaseInfo(); mDividerControllerLocked = new DockedStackDividerController(service, this); @@ -1767,8 +1766,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo updateBounds(); } - void getContentRect(Rect out) { - out.set(mDisplayFrames.mContent); + void getStableRect(Rect out) { + out.set(mDisplayFrames.mStable); } TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) { @@ -2339,6 +2338,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Updates the layer assignment of windows on this display. */ void assignWindowLayers(boolean setLayoutNeeded) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers"); assignChildLayers(getPendingTransaction()); if (setLayoutNeeded) { setLayoutNeeded(); @@ -2349,6 +2349,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // prepareSurfaces. This allows us to synchronize Z-ordering changes with // the hiding and showing of surfaces. scheduleAnimation(); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } // TODO: This should probably be called any time a visual change is made to the hierarchy like @@ -3182,6 +3183,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> { + DisplayChildWindowContainer(WindowManagerService service) { + super(service); + } + @Override boolean fillsParent() { return true; @@ -3209,6 +3214,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private TaskStack mPinnedStack = null; private TaskStack mSplitScreenPrimaryStack = null; + TaskStackContainers(WindowManagerService service) { + super(service); + } + /** * Returns the topmost stack on the display that is compatible with the input windowing mode * and activity type. Null is no compatible stack on the display. @@ -3516,35 +3525,37 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - final int NORMAL_STACK_STATE = 0; - final int BOOSTED_STATE = 1; - final int ALWAYS_ON_TOP_STATE = 2; + int layer = 0; // We allow stacks to change visual order from the AM specified order due to // Z-boosting during animations. However we must take care to ensure TaskStacks // which are marked as alwaysOnTop remain that way. - int layer = 0; - for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - layer++; - if (state == NORMAL_STACK_STATE) { - s.assignLayer(t, layer); - } else if (state == BOOSTED_STATE && s.needsZBoost()) { - s.assignLayer(t, layer); - } else if (state == ALWAYS_ON_TOP_STATE && - s.isAlwaysOnTop()) { - s.assignLayer(t, layer); - } - s.assignChildLayers(t); + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + s.assignChildLayers(); + if (!s.needsZBoost() && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + } + } + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (s.needsZBoost() && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (state == BOOSTED_STATE && mAnimationLayer != null) { - t.setLayer(mAnimationLayer, layer + 1); + } + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); } } + + // The appropriate place for App-Transitions to occur is right + // above all other animations but still below things in the Picture-and-Picture + // windowing mode. + if (mAnimationLayer != null) { + t.setLayer(mAnimationLayer, layer++); + } } @Override @@ -3560,8 +3571,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private final class AboveAppWindowContainers extends NonAppWindowContainers { - AboveAppWindowContainers(String name) { - super(name); + AboveAppWindowContainers(String name, WindowManagerService service) { + super(name, service); } void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) { @@ -3577,14 +3588,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw( TYPE_INPUT_METHOD_DIALOG, true)) { - t.setRelativeLayer(imeContainer.getSurfaceControl(), - wt.getSurfaceControl(), -1); + imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1); needAssignIme = false; } } if (needAssignIme) { - t.setRelativeLayer(imeContainer.getSurfaceControl(), - getSurfaceControl(), Integer.MAX_VALUE); + imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE); } } } @@ -3618,7 +3627,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; private final String mName; - NonAppWindowContainers(String name) { + NonAppWindowContainers(String name, WindowManagerService service) { + super(service); mName = name; } @@ -3712,8 +3722,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - t.setLayer(mOverlayLayer, 1) - .setLayer(mWindowingLayer, 0); // These are layers as children of "mWindowingLayer" mBelowAppWindowsContainers.assignLayer(t, 0); @@ -3737,8 +3745,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // place it in the AboveAppWindowContainers. if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode() && (imeTarget.getSurfaceControl() != null)) { - t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(), - imeTarget.getSurfaceControl(), + mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure // this is always above SurfaceView but always below attached window. 1); diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java index 209ce3fcfdb4..015571255f0d 100644 --- a/services/core/java/com/android/server/wm/DisplayFrames.java +++ b/services/core/java/com/android/server/wm/DisplayFrames.java @@ -22,12 +22,16 @@ import static android.view.Surface.ROTATION_90; import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS; import android.annotation.NonNull; +import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; import android.view.DisplayInfo; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; +import java.util.Arrays; /** * Container class for all the display frames that affect how we do window layout on a display. @@ -124,7 +128,7 @@ public class DisplayFrames { info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom); } - public void onBeginLayout() { + public void onBeginLayout(boolean emulateDisplayCutout, int statusBarHeight) { switch (mRotation) { case ROTATION_90: mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top; @@ -165,12 +169,64 @@ public class DisplayFrames { mDisplayCutout = DisplayCutout.NO_CUTOUT; mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + if (emulateDisplayCutout) { + setEmulatedDisplayCutout((int) (statusBarHeight * 0.8)); + } } public int getInputMethodWindowVisibleHeight() { return mDock.bottom - mCurrent.bottom; } + private void setEmulatedDisplayCutout(int height) { + final boolean swappedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270; + + final int screenWidth = swappedDimensions ? mDisplayHeight : mDisplayWidth; + final int screenHeight = swappedDimensions ? mDisplayWidth : mDisplayHeight; + + final int widthTop = (int) (screenWidth * 0.3); + final int widthBottom = widthTop - height; + + switch (mRotation) { + case ROTATION_90: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point(0, (screenWidth - widthTop) / 2), + new Point(height, (screenWidth - widthBottom) / 2), + new Point(height, (screenWidth + widthBottom) / 2), + new Point(0, (screenWidth + widthTop) / 2) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.left = height; + break; + case ROTATION_180: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point((screenWidth - widthTop) / 2, screenHeight), + new Point((screenWidth - widthBottom) / 2, screenHeight - height), + new Point((screenWidth + widthBottom) / 2, screenHeight - height), + new Point((screenWidth + widthTop) / 2, screenHeight) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.bottom = screenHeight - height; + break; + case ROTATION_270: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point(screenHeight, (screenWidth - widthTop) / 2), + new Point(screenHeight - height, (screenWidth - widthBottom) / 2), + new Point(screenHeight - height, (screenWidth + widthBottom) / 2), + new Point(screenHeight, (screenWidth + widthTop) / 2) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.right = screenHeight - height; + break; + default: + mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList( + new Point((screenWidth - widthTop) / 2, 0), + new Point((screenWidth - widthBottom) / 2, height), + new Point((screenWidth + widthBottom) / 2, height), + new Point((screenWidth + widthTop) / 2, 0) + )).calculateRelativeTo(mUnrestricted); + mDisplayCutoutSafe.top = height; + break; + } + } + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mStable.writeToProto(proto, STABLE_BOUNDS); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 7e29a3aa811e..88b7a11f02fd 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -390,12 +390,13 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { } } - final boolean inPositioning = (mService.mTaskPositioner != null); + final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked(); if (inPositioning) { if (DEBUG_TASK_POSITIONING) { Log.d(TAG_WM, "Inserting window handle for repositioning"); } - final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle; + final InputWindowHandle dragWindowHandle = + mService.mTaskPositioningController.getDragWindowHandleLocked(); if (dragWindowHandle != null) { addInputWindowHandle(dragWindowHandle); } else { diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java new file mode 100644 index 000000000000..5fe456562270 --- /dev/null +++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java @@ -0,0 +1,100 @@ +/* + * 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 android.graphics.Point; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Animation; + +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +/** + * Animation that can be executed without holding the window manager lock. See + * {@link SurfaceAnimationRunner}. + */ +class LocalAnimationAdapter implements AnimationAdapter { + + private final AnimationSpec mSpec; + private final SurfaceAnimationRunner mAnimator; + + LocalAnimationAdapter(AnimationSpec spec, SurfaceAnimationRunner animator) { + mSpec = spec; + mAnimator = animator; + } + + @Override + public boolean getDetachWallpaper() { + return mSpec.getDetachWallpaper(); + } + + @Override + public int getBackgroundColor() { + return mSpec.getBackgroundColor(); + } + + @Override + public void startAnimation(SurfaceControl animationLeash, Transaction t, + OnAnimationFinishedCallback finishCallback) { + mAnimator.startAnimation(mSpec, animationLeash, t, + () -> finishCallback.onAnimationFinished(this)); + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + mAnimator.onAnimationCancelled(animationLeash); + } + + @Override + public long getDurationHint() { + return mSpec.getDuration(); + } + + /** + * Describes how to apply an animation. + */ + interface AnimationSpec { + + /** + * @see AnimationAdapter#getDetachWallpaper + */ + default boolean getDetachWallpaper() { + return false; + } + + /** + * @see AnimationAdapter#getBackgroundColor + */ + default int getBackgroundColor() { + return 0; + } + + /** + * @return The duration of the animation. + */ + long getDuration(); + + /** + * Called when the spec needs to apply the current animation state to the leash. + * + * @param t The transaction to use to apply a transform. + * @param leash The leash to apply the state to. + * @param currentPlayTime The current time of the animation. + */ + void apply(Transaction t, SurfaceControl leash, long currentPlayTime); + } +} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 4008811b2dc8..b08eb18a6f13 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -101,8 +101,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1; private static final int SET_USER_ACTIVITY_TIMEOUT = 2; - WindowManagerService mService; - private boolean mWallpaperForceHidingChanged = false; private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; @@ -160,7 +158,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { }; RootWindowContainer(WindowManagerService service) { - mService = service; + super(service); mHandler = new MyHandler(service.mH.getLooper()); mWallpaperController = new WallpaperController(mService); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 63eea104c7e7..192d6c84e190 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -368,7 +368,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { long ident = Binder.clearCallingIdentity(); try { - return mService.startMovingTask(window, startX, startY); + return mService.mTaskPositioningController.startMovingTask(window, startX, startY); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java new file mode 100644 index 000000000000..5bc27223edf4 --- /dev/null +++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java @@ -0,0 +1,217 @@ +/* + * 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.view.Choreographer.CALLBACK_TRAVERSAL; +import static android.view.Choreographer.getSfInstance; + +import android.animation.AnimationHandler; +import android.animation.AnimationHandler.AnimationFrameCallbackProvider; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.Nullable; +import android.util.ArrayMap; +import android.view.Choreographer; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Transformation; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.server.AnimationThread; +import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; + +/** + * Class to run animations without holding the window manager lock. + */ +class SurfaceAnimationRunner { + + private final Object mLock = new Object(); + + @VisibleForTesting + Choreographer mChoreographer; + + private final Runnable mApplyTransactionRunnable = this::applyTransaction; + private final AnimationHandler mAnimationHandler; + private final Transaction mFrameTransaction; + private boolean mApplyScheduled; + + @GuardedBy("mLock") + @VisibleForTesting + final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); + + @GuardedBy("mLock") + @VisibleForTesting + final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>(); + + SurfaceAnimationRunner() { + this(null /* callbackProvider */, new Transaction()); + } + + @VisibleForTesting + SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider, + Transaction frameTransaction) { + SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(), + 0 /* timeout */); + mFrameTransaction = frameTransaction; + mAnimationHandler = new AnimationHandler(); + mAnimationHandler.setProvider(callbackProvider != null + ? callbackProvider + : new SfVsyncFrameCallbackProvider(mChoreographer)); + } + + void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, + Runnable finishCallback) { + synchronized (mLock) { + final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, + finishCallback); + mPendingAnimations.put(animationLeash, runningAnim); + mChoreographer.postFrameCallback(this::stepAnimation); + + // Some animations (e.g. move animations) require the initial transform to be applied + // immediately. + applyTransformation(runningAnim, t, 0 /* currentPlayTime */); + } + } + + void onAnimationCancelled(SurfaceControl leash) { + synchronized (mLock) { + if (mPendingAnimations.containsKey(leash)) { + mPendingAnimations.remove(leash); + // TODO: Releasing the leash is problematic if reparenting hasn't happened yet. + // Fix with transaction + //leash.release(); + return; + } + final ValueAnimator anim = mRunningAnimations.get(leash); + if (anim != null) { + mRunningAnimations.remove(leash); + SurfaceAnimationThread.getHandler().post(() -> { + anim.cancel(); + applyTransaction(); + //leash.release(); + }); + } + } + } + + private void startPendingAnimationsLocked() { + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + startAnimationLocked(mPendingAnimations.valueAt(i)); + } + mPendingAnimations.clear(); + } + + private void startAnimationLocked(RunningAnimation a) { + final ValueAnimator result = new SfValueAnimator(); + + // Animation length is already expected to be scaled. + result.overrideDurationScale(1.0f); + result.setDuration(a.animSpec.getDuration()); + result.addUpdateListener(animation -> { + applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime()); + + // Transaction will be applied in the commit phase. + scheduleApplyTransaction(); + }); + result.addListener(new AnimatorListenerAdapter() { + + private boolean mCancelled; + + @Override + public void onAnimationStart(Animator animation) { + mFrameTransaction.show(a.leash); + } + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + synchronized (mLock) { + mRunningAnimations.remove(a.leash); + } + if (!mCancelled) { + // Post on other thread that we can push final state without jank. + AnimationThread.getHandler().post(() -> { + a.finishCallback.run(); + + // Make sure to release the leash after finishCallback has been invoked such + // that reparenting is done already when releasing the leash. + a.leash.release(); + }); + } + } + }); + result.start(); + mRunningAnimations.put(a.leash, result); + } + + private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { + a.animSpec.apply(t, a.leash, currentPlayTime); + } + + private void stepAnimation(long frameTimeNanos) { + synchronized (mLock) { + startPendingAnimationsLocked(); + } + } + + private void scheduleApplyTransaction() { + if (!mApplyScheduled) { + mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, + null /* token */); + mApplyScheduled = true; + } + } + + private void applyTransaction() { + mFrameTransaction.apply(); + mApplyScheduled = false; + } + + private static final class RunningAnimation { + final AnimationSpec animSpec; + final SurfaceControl leash; + final Runnable finishCallback; + + RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { + this.animSpec = animSpec; + this.leash = leash; + this.finishCallback = finishCallback; + } + } + + /** + * Value animator that uses sf-vsync signal to tick. + */ + private class SfValueAnimator extends ValueAnimator { + + SfValueAnimator() { + setFloatValues(0f, 1f); + } + + @Override + public AnimationHandler getAnimationHandler() { + return mAnimationHandler; + } + } +} diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationThread.java b/services/core/java/com/android/server/wm/SurfaceAnimationThread.java new file mode 100644 index 000000000000..0d3afc06a86c --- /dev/null +++ b/services/core/java/com/android/server/wm/SurfaceAnimationThread.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static android.os.Process.THREAD_PRIORITY_DISPLAY; + +import android.os.Handler; +import android.os.Trace; + +import com.android.server.ServiceThread; + +/** + * Thread for running {@link SurfaceAnimationRunner} that does not hold the window manager lock. + */ +public final class SurfaceAnimationThread extends ServiceThread { + private static SurfaceAnimationThread sInstance; + private static Handler sHandler; + + private SurfaceAnimationThread() { + super("android.anim.lf", THREAD_PRIORITY_DISPLAY, false /*allowIo*/); + } + + private static void ensureThreadLocked() { + if (sInstance == null) { + sInstance = new SurfaceAnimationThread(); + sInstance.start(); + sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER); + sHandler = new Handler(sInstance.getLooper()); + } + } + + public static SurfaceAnimationThread get() { + synchronized (SurfaceAnimationThread.class) { + ensureThreadLocked(); + return sInstance; + } + } + + public static Handler getHandler() { + synchronized (SurfaceAnimationThread.class) { + ensureThreadLocked(); + return sHandler; + } + } +} diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java new file mode 100644 index 000000000000..713d58ba4d1a --- /dev/null +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -0,0 +1,311 @@ +/* + * 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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Slog; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +import java.io.PrintWriter; + +/** + * A class that can run animations on objects that have a set of child surfaces. We do this by + * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash + * gets attached in the surface hierarchy where the the children were attached to. We then hand off + * the Leash to the component handling the animation, which is specified by the + * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the + * animation will be invoked, at which we reparent the children back to the original parent. + */ +class SurfaceAnimator { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM; + private final WindowManagerService mService; + private AnimationAdapter mAnimation; + private SurfaceControl mLeash; + private final Animatable mAnimatable; + private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback; + private final Runnable mAnimationFinishedCallback; + private boolean mAnimationStartDelayed; + + /** + * @param animatable The object to animate. + * @param animationFinishedCallback Callback to invoke when an animation has finished running. + */ + SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback, + WindowManagerService service) { + mAnimatable = animatable; + mService = service; + mAnimationFinishedCallback = animationFinishedCallback; + mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback); + } + + private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) { + return anim -> { + synchronized (mService.mWindowMap) { + if (anim != mAnimation) { + // Callback was from another animation - ignore. + return; + } + + final Transaction t = new Transaction(); + SurfaceControl.openTransaction(); + try { + reset(t); + animationFinishedCallback.run(); + } finally { + // TODO: This should use pendingTransaction eventually, but right now things + // happening on the animation finished callback are happening on the global + // transaction. + SurfaceControl.mergeToGlobalTransaction(t); + SurfaceControl.closeTransaction(); + } + } + }; + } + + /** + * Starts an animation. + * + * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the + * component responsible for running the animation. It runs the animation with + * {@link AnimationAdapter#startAnimation} once the hierarchy with + * the Leash has been set up. + * @param hidden Whether the container holding the child surfaces is currently visible or not. + * This is important as it will start with the leash hidden or visible before + * handing it to the component that is responsible to run the animation. + */ + void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) { + cancelAnimation(t, true /* restarting */); + mAnimation = anim; + final SurfaceControl surface = mAnimatable.getSurface(); + if (surface == null) { + Slog.w(TAG, "Unable to start animation, surface is null or no children."); + cancelAnimation(); + return; + } + mLeash = createAnimationLeash(surface, t, + mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden); + mAnimatable.onLeashCreated(t, mLeash); + if (mAnimationStartDelayed) { + if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed"); + return; + } + mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback); + } + + /** + * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation} + * will not start the animation until {@link #endDelayingAnimationStart} is called. When an + * animation start is being delayed, the animator is considered animating already. + */ + void startDelayingAnimationStart() { + + // We only allow delaying animation start we are not currently animating + if (!isAnimating()) { + mAnimationStartDelayed = true; + } + } + + /** + * See {@link #startDelayingAnimationStart}. + */ + void endDelayingAnimationStart() { + final boolean delayed = mAnimationStartDelayed; + mAnimationStartDelayed = false; + if (delayed && mAnimation != null) { + mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(), + mInnerAnimationFinishedCallback); + mAnimatable.commitPendingTransaction(); + } + } + + /** + * @return Whether we are currently running an animation, or we have a pending animation that + * is waiting to be started with {@link #endDelayingAnimationStart} + */ + boolean isAnimating() { + return mAnimation != null; + } + + /** + * @return The current animation spec if we are running an animation, or {@code null} otherwise. + */ + AnimationAdapter getAnimation() { + return mAnimation; + } + + /** + * Cancels any currently running animation. + */ + void cancelAnimation() { + cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */); + mAnimatable.commitPendingTransaction(); + } + + /** + * Sets the layer of the surface. + * <p> + * When the layer of the surface needs to be adjusted, we need to set it on the leash if the + * surface is reparented to the leash. This method takes care of that. + */ + void setLayer(Transaction t, int layer) { + t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurface(), layer); + } + + /** + * Reparents the surface. + * + * @see #setLayer + */ + void reparent(Transaction t, SurfaceControl newParent) { + t.reparent(mLeash != null ? mLeash : mAnimatable.getSurface(), newParent.getHandle()); + } + + /** + * @return True if the surface is attached to the leash; false otherwise. + */ + boolean hasLeash() { + return mLeash != null; + } + + private void cancelAnimation(Transaction t, boolean restarting) { + if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting); + final SurfaceControl leash = mLeash; + final AnimationAdapter animation = mAnimation; + reset(t); + if (animation != null) { + if (!mAnimationStartDelayed) { + animation.onAnimationCancelled(leash); + } + if (!restarting) { + mAnimationFinishedCallback.run(); + } + } + if (!restarting) { + mAnimationStartDelayed = false; + } + } + + private void reset(Transaction t) { + final SurfaceControl surface = mAnimatable.getSurface(); + final SurfaceControl parent = mAnimatable.getParentSurface(); + + // If the surface was destroyed, we don't care to reparent it back. + final boolean destroy = mLeash != null && surface != null && parent != null; + if (destroy) { + if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent"); + t.reparent(surface, parent.getHandle()); + } + mLeash = null; + mAnimation = null; + + // Make sure to inform the animatable after the leash was destroyed. + if (destroy) { + mAnimatable.onLeashDestroyed(t); + } + } + + private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width, + int height, boolean hidden) { + if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); + final SurfaceControl.Builder builder = mAnimatable.makeLeash() + .setName(surface + " - animation-leash") + .setSize(width, height); + final SurfaceControl leash = builder.build(); + if (!hidden) { + t.show(leash); + } + t.reparent(surface, leash.getHandle()); + return leash; + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation); + pw.print(" mLeash="); pw.println(mLeash); + } + + /** + * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the + * component that is running the animation when the animation is finished. + */ + interface OnAnimationFinishedCallback { + void onAnimationFinished(AnimationAdapter anim); + } + + /** + * Interface to be animated by {@link SurfaceAnimator}. + */ + interface Animatable { + + /** + * @return The pending transaction that will be committed in the next frame. + */ + @NonNull Transaction getPendingTransaction(); + + /** + * Schedules a commit of the pending transaction. + */ + void commitPendingTransaction(); + + /** + * Called when the was created. + * + * @param t The transaction to use to apply any necessary changes. + * @param leash The leash that was created. + */ + void onLeashCreated(Transaction t, SurfaceControl leash); + + /** + * Called when the leash is being destroyed, and the surface was reparented back to the + * original parent. + * + * @param t The transaction to use to apply any necessary changes. + */ + void onLeashDestroyed(Transaction t); + + /** + * @return A new child surface. + */ + SurfaceControl.Builder makeLeash(); + + /** + * @return The surface of the object to be animated. + */ + @Nullable SurfaceControl getSurface(); + + /** + * @return The parent of the surface object to be animated. + */ + @Nullable SurfaceControl getParentSurface(); + + /** + * @return The width of the surface to be animated. + */ + int getSurfaceWidth(); + + /** + * @return The height of the surface to be animated. + */ + int getSurfaceHeight(); + } +} diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 8aa129a45373..6ea8a4790630 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -59,7 +59,6 @@ class Task extends WindowContainer<AppWindowToken> { final int mTaskId; final int mUserId; private boolean mDeferRemoval = false; - final WindowManagerService mService; final Rect mPreparedFrozenBounds = new Rect(); final Configuration mPreparedFrozenMergedConfig = new Configuration(); @@ -102,10 +101,10 @@ class Task extends WindowContainer<AppWindowToken> { Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription, TaskWindowContainerController controller) { + super(service); mTaskId = taskId; mStack = stack; mUserId = userId; - mService = service; mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; setController(controller); diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 87d0a4016634..ca9f3a933f70 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -16,15 +16,11 @@ package com.android.server.wm; -import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; -import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.RESIZE_MODE_USER; import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.dipToPixel; @@ -44,7 +40,6 @@ import android.util.Slog; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; import android.view.Display; -import android.view.DisplayInfo; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java new file mode 100644 index 000000000000..bb5706ceb0ec --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -0,0 +1,159 @@ +/* + * 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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.annotation.Nullable; +import android.app.IActivityManager; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Display; +import android.view.IWindow; +import com.android.internal.annotations.GuardedBy; +import com.android.server.input.InputManagerService; +import com.android.server.input.InputWindowHandle; + +/** + * Controller for task positioning by drag. + */ +class TaskPositioningController { + private final WindowManagerService mService; + private final InputManagerService mInputManager; + private final InputMonitor mInputMonitor; + private final IActivityManager mActivityManager; + + @GuardedBy("WindowManagerSerivce.mWindowMap") + private @Nullable TaskPositioner mTaskPositioner; + + boolean isPositioningLocked() { + return mTaskPositioner != null; + } + + InputWindowHandle getDragWindowHandleLocked() { + return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null; + } + + TaskPositioningController(WindowManagerService service, InputManagerService inputManager, + InputMonitor inputMonitor, IActivityManager activityManager) { + mService = service; + mInputMonitor = inputMonitor; + mInputManager = inputManager; + mActivityManager = activityManager; + } + + boolean startMovingTask(IWindow window, float startX, float startY) { + WindowState win = null; + synchronized (mService.mWindowMap) { + win = mService.windowForClientLocked(null, window, false); + // win shouldn't be null here, pass it down to startPositioningLocked + // to get warning if it's null. + if (!startPositioningLocked( + win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) { + return false; + } + } + try { + mActivityManager.setFocusedTask(win.getTask().mTaskId); + } catch(RemoteException e) {} + return true; + } + + void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { + int taskId = -1; + synchronized (mService.mWindowMap) { + final Task task = displayContent.findTaskForResizePoint(x, y); + if (task != null) { + if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, + task.preserveOrientationOnResize(), x, y)) { + return; + } + taskId = task.mTaskId; + } else { + taskId = displayContent.taskIdFromPoint(x, y); + } + } + if (taskId >= 0) { + try { + mActivityManager.setFocusedTask(taskId); + } catch(RemoteException e) {} + } + } + + private boolean startPositioningLocked(WindowState win, boolean resize, + boolean preserveOrientation, float startX, float startY) { + if (DEBUG_TASK_POSITIONING) + Slog.d(TAG_WM, "startPositioningLocked: " + + "win=" + win + ", resize=" + resize + ", preserveOrientation=" + + preserveOrientation + ", {" + startX + ", " + startY + "}"); + + if (win == null || win.getAppToken() == null) { + Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win); + return false; + } + if (win.mInputChannel == null) { + Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, " + + " probably being removed"); + return false; + } + + final DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win); + return false; + } + + Display display = displayContent.getDisplay(); + mTaskPositioner = new TaskPositioner(mService); + mTaskPositioner.register(displayContent); + mInputMonitor.updateInputWindowsLw(true /*force*/); + + // We need to grab the touch focus so that the touch events during the + // resizing/scrolling are not sent to the app. 'win' is the main window + // of the app, it may not have focus since there might be other windows + // on top (eg. a dialog window). + WindowState transferFocusFromWin = win; + if (mService.mCurrentFocus != null && mService.mCurrentFocus != win + && mService.mCurrentFocus.mAppToken == win.mAppToken) { + transferFocusFromWin = mService.mCurrentFocus; + } + if (!mInputManager.transferTouchFocus( + transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) { + Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); + mTaskPositioner.unregister(); + mTaskPositioner = null; + mInputMonitor.updateInputWindowsLw(true /*force*/); + return false; + } + + mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY); + return true; + } + + void finishPositioning() { + if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning"); + + synchronized (mService.mWindowMap) { + if (mTaskPositioner != null) { + mTaskPositioner.unregister(); + mTaskPositioner = null; + mInputMonitor.updateInputWindowsLw(true /*force*/); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 94fbd0e1f0d9..259f8df15e31 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -197,7 +197,7 @@ class TaskSnapshotSurface implements StartingSurface { } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, - View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, + View.GONE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, tmpRect, tmpCutout, null); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 4a3a3fc960a5..28b1390d92de 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -75,9 +75,6 @@ public class TaskStack extends WindowContainer<Task> implements /** Unique identifier */ final int mStackId; - /** The service */ - private final WindowManagerService mService; - /** The display this stack sits under. */ // TODO: Track parent marks like this in WindowContainer. private DisplayContent mDisplayContent; @@ -151,7 +148,7 @@ public class TaskStack extends WindowContainer<Task> implements final Rect mTmpDimBoundsRect = new Rect(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { - mService = service; + super(service); mStackId = stackId; setController(controller); mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( @@ -1117,12 +1114,12 @@ public class TaskStack extends WindowContainer<Task> implements return false; } - final Rect displayContentRect = mTmpRect; + final Rect displayStableRect = mTmpRect; final Rect contentBounds = mTmpRect2; // Calculate the content bounds excluding the area occupied by IME - getDisplayContent().getContentRect(displayContentRect); - contentBounds.set(displayContentRect); + getDisplayContent().getStableRect(displayStableRect); + contentBounds.set(displayStableRect); int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top); imeTop += imeWin.getGivenContentInsetsLw().top; @@ -1130,7 +1127,7 @@ public class TaskStack extends WindowContainer<Task> implements contentBounds.bottom = imeTop; } - final int yOffset = displayContentRect.bottom - contentBounds.bottom; + final int yOffset = displayStableRect.bottom - contentBounds.bottom; final int dividerWidth = getDisplayContent().mDividerControllerLocked.getContentWidth(); @@ -1142,7 +1139,7 @@ public class TaskStack extends WindowContainer<Task> implements // occluded by IME. We shift its bottom up by the height of the IME, but // leaves at least 30% of the top stack visible. final int minTopStackBottom = - getMinTopStackBottom(displayContentRect, getRawBounds().bottom); + getMinTopStackBottom(displayStableRect, getRawBounds().bottom); final int bottom = Math.max( getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive, minTopStackBottom); @@ -1162,7 +1159,7 @@ public class TaskStack extends WindowContainer<Task> implements final int topBeforeImeAdjust = getRawBounds().top - dividerWidth + dividerWidthInactive; final int minTopStackBottom = - getMinTopStackBottom(displayContentRect, + getMinTopStackBottom(displayStableRect, getRawBounds().top - dividerWidth); final int top = Math.max( getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive); diff --git a/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java b/services/core/java/com/android/server/wm/TransactionFactory.java index 3ce63c77a881..067f08369710 100644 --- a/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java +++ b/services/core/java/com/android/server/wm/TransactionFactory.java @@ -11,18 +11,17 @@ * 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. + * limitations under the License */ -package com.android.test.cantsavestate2; +package com.android.server.wm; -import android.app.Activity; -import android.os.Bundle; +import android.view.SurfaceControl.Transaction; + +/** + * Helper class to inject custom transaction objects into window manager. + */ +interface TransactionFactory { + Transaction make(); +}; -public class CantSave2Activity extends Activity { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.cant_save_2_activity); - } -} diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java new file mode 100644 index 000000000000..56175c7d0538 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -0,0 +1,70 @@ +/* + * 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 android.graphics.Point; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; + +/** + * Animation spec for regular window animations. + */ +public class WindowAnimationSpec implements AnimationSpec { + + private Animation mAnimation; + private final Point mPosition = new Point(); + private final ThreadLocal<Tmp> mThreadLocalTmps = ThreadLocal.withInitial(Tmp::new); + + public WindowAnimationSpec(Animation animation, Point position) { + mAnimation = animation; + mPosition.set(position.x, position.y); + } + + @Override + public boolean getDetachWallpaper() { + return mAnimation.getDetachWallpaper(); + } + + @Override + public int getBackgroundColor() { + return mAnimation.getBackgroundColor(); + } + + @Override + public long getDuration() { + return mAnimation.computeDurationHint(); + } + + @Override + public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) { + final Tmp tmp = mThreadLocalTmps.get(); + tmp.transformation.clear(); + mAnimation.getTransformation(currentPlayTime, tmp.transformation); + tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y); + t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats); + t.setAlpha(leash, tmp.transformation.getAlpha()); + } + + private static class Tmp { + final Transformation transformation = new Transformation(); + final float[] floats = new float[9]; + } +} diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 64675825243e..d6329bfab5a1 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -21,6 +21,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER; import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION; +import static com.android.server.wm.proto.WindowContainerProto.VISIBLE; import static android.view.SurfaceControl.Transaction; import android.annotation.CallSuper; @@ -71,18 +72,25 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< WindowContainerController mController; protected SurfaceControl mSurfaceControl; + private int mLastLayer = 0; + private SurfaceControl mLastRelativeToLayer = null; /** * Applied as part of the animation pass in "prepareSurfaces". */ - private Transaction mPendingTransaction = new Transaction(); + private final Transaction mPendingTransaction; + protected final WindowManagerService mService; + + WindowContainer(WindowManagerService service) { + mService = service; + mPendingTransaction = service.mTransactionFactory.make(); + } @Override final protected WindowContainer getParent() { return mParent; } - @Override protected int getChildCount() { return mChildren.size(); @@ -755,34 +763,46 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void assignLayer(Transaction t, int layer) { - if (mSurfaceControl != null) { + final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; + if (mSurfaceControl != null && changed) { t.setLayer(mSurfaceControl, layer); + mLastLayer = layer; + mLastRelativeToLayer = null; + } + } + + void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { + final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo; + if (mSurfaceControl != null && changed) { + t.setRelativeLayer(mSurfaceControl, relativeTo, layer); + mLastLayer = layer; + mLastRelativeToLayer = relativeTo; } } void assignChildLayers(Transaction t) { int layer = 0; - boolean boosting = false; // We use two passes as a way to promote children which // need Z-boosting to the end of the list. - for (int i = 0; i < 2; i++ ) { - for (int j = 0; j < mChildren.size(); ++j) { - final WindowContainer wc = mChildren.get(j); - if (wc.needsZBoost() && !boosting) { - continue; - } - wc.assignLayer(t, layer); - wc.assignChildLayers(t); - - layer++; + for (int j = 0; j < mChildren.size(); ++j) { + final WindowContainer wc = mChildren.get(j); + wc.assignChildLayers(t); + if (!wc.needsZBoost()) { + wc.assignLayer(t, layer++); + } + } + for (int j = 0; j < mChildren.size(); ++j) { + final WindowContainer wc = mChildren.get(j); + if (wc.needsZBoost()) { + wc.assignLayer(t, layer++); } - boosting = true; } } void assignChildLayers() { assignChildLayers(getPendingTransaction()); + scheduleAnimation(); } boolean needsZBoost() { @@ -809,6 +829,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final long token = proto.start(fieldId); super.writeToProto(proto, CONFIGURATION_CONTAINER, trim); proto.write(ORIENTATION, mOrientation); + proto.write(VISIBLE, isVisible()); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3ad4df7a7cee..8c9948e1ee49 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -215,6 +215,7 @@ import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.SurfaceControl; +import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; @@ -754,7 +755,7 @@ public class WindowManagerService extends IWindowManager.Stub // Whether or not a layout can cause a wake up when theater mode is enabled. boolean mAllowTheaterModeWakeFromLayout; - TaskPositioner mTaskPositioner; + final TaskPositioningController mTaskPositioningController; final DragDropController mDragDropController; // For frozen screen animations. @@ -768,6 +769,7 @@ public class WindowManagerService extends IWindowManager.Stub int mTransactionSequence; final WindowAnimator mAnimator; + final SurfaceAnimationRunner mSurfaceAnimationRunner; final BoundsAnimationController mBoundsAnimationController; @@ -806,12 +808,8 @@ public class WindowManagerService extends IWindowManager.Stub static WindowManagerThreadPriorityBooster sThreadPriorityBooster = new WindowManagerThreadPriorityBooster(); - class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory { - public SurfaceControl.Builder make(SurfaceSession s) { - return new SurfaceControl.Builder(s); - } - }; - SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory(); + SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new; + TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new; static void boostPriorityForLockedSection() { sThreadPriorityBooster.boost(); @@ -1085,10 +1083,13 @@ public class WindowManagerService extends IWindowManager.Stub mHoldingScreenWakeLock.setReferenceCounted(false); mAnimator = new WindowAnimator(this); + mSurfaceAnimationRunner = new SurfaceAnimationRunner(); mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); + mTaskPositioningController = + new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager); mDragDropController = new DragDropController(this, mH.getLooper()); LocalServices.addService(WindowManagerInternal.class, new LocalService()); @@ -1502,7 +1503,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. - displayContent.assignWindowLayers(false /* setLayoutNeeded */); + win.getParent().assignChildLayers(); if (focusChanged) { mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/); @@ -1972,6 +1973,13 @@ public class WindowManagerService extends IWindowManager.Stub + " newVis=" + viewVisibility, stack); } + win.setDisplayLayoutNeeded(); + win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + // We should only relayout if the view is visible, it is a starting window, or the // associated appToken is not hidden. final boolean shouldRelayout = viewVisibility == View.VISIBLE && @@ -1981,15 +1989,6 @@ public class WindowManagerService extends IWindowManager.Stub if (shouldRelayout) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - // We are about to create a surface, but we didn't run a layout yet. So better run - // a layout now that we already know the right size, as a resize call will make the - // surface transaction blocking until next vsync and slow us down. - // TODO: Ideally we'd create the surface after running layout a bit further down, - // but moving this seems to be too risky at this point in the release. - if (win.mLayoutSeq == -1) { - win.setDisplayLayoutNeeded(); - mWindowPlacerLocked.performSurfacePlacement(true); - } result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility); try { @@ -2091,16 +2090,11 @@ public class WindowManagerService extends IWindowManager.Stub mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); } - win.setDisplayLayoutNeeded(); - win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientationFromAppTokens"); configChanged = updateOrientationFromAppTokensLocked(false, displayId); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo(); dc.mWallpaperController.updateWallpaperOffset( @@ -4450,107 +4444,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - boolean startMovingTask(IWindow window, float startX, float startY) { - WindowState win = null; - synchronized (mWindowMap) { - win = windowForClientLocked(null, window, false); - // win shouldn't be null here, pass it down to startPositioningLocked - // to get warning if it's null. - if (!startPositioningLocked( - win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) { - return false; - } - } - try { - mActivityManager.setFocusedTask(win.getTask().mTaskId); - } catch(RemoteException e) {} - return true; - } - - private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { - int taskId = -1; - synchronized (mWindowMap) { - final Task task = displayContent.findTaskForResizePoint(x, y); - if (task != null) { - if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, - task.preserveOrientationOnResize(), x, y)) { - return; - } - taskId = task.mTaskId; - } else { - taskId = displayContent.taskIdFromPoint(x, y); - } - } - if (taskId >= 0) { - try { - mActivityManager.setFocusedTask(taskId); - } catch(RemoteException e) {} - } - } - - private boolean startPositioningLocked(WindowState win, boolean resize, - boolean preserveOrientation, float startX, float startY) { - if (DEBUG_TASK_POSITIONING) - Slog.d(TAG_WM, "startPositioningLocked: " - + "win=" + win + ", resize=" + resize + ", preserveOrientation=" - + preserveOrientation + ", {" + startX + ", " + startY + "}"); - - if (win == null || win.getAppToken() == null) { - Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win); - return false; - } - if (win.mInputChannel == null) { - Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, " - + " probably being removed"); - return false; - } - - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent == null) { - Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win); - return false; - } - - Display display = displayContent.getDisplay(); - mTaskPositioner = new TaskPositioner(this); - mTaskPositioner.register(displayContent); - mInputMonitor.updateInputWindowsLw(true /*force*/); - - // We need to grab the touch focus so that the touch events during the - // resizing/scrolling are not sent to the app. 'win' is the main window - // of the app, it may not have focus since there might be other windows - // on top (eg. a dialog window). - WindowState transferFocusFromWin = win; - if (mCurrentFocus != null && mCurrentFocus != win - && mCurrentFocus.mAppToken == win.mAppToken) { - transferFocusFromWin = mCurrentFocus; - } - if (!mInputManager.transferTouchFocus( - transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) { - Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); - mTaskPositioner.unregister(); - mTaskPositioner = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - return false; - } - - mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY); - return true; - } - - private void finishPositioning() { - if (DEBUG_TASK_POSITIONING) { - Slog.d(TAG_WM, "finishPositioning"); - } - synchronized (mWindowMap) { - if (mTaskPositioner != null) { - mTaskPositioner.unregister(); - mTaskPositioner = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - } - } - } - // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- @@ -5013,12 +4906,13 @@ public class WindowManagerService extends IWindowManager.Stub } case TAP_OUTSIDE_TASK: { - handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2); + mTaskPositioningController.handleTapOutsideTask( + (DisplayContent)msg.obj, msg.arg1, msg.arg2); } break; case FINISH_TASK_POSITIONING: { - finishPositioning(); + mTaskPositioningController.finishPositioning(); } break; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d23a6c73d853..e38605d33f26 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -186,7 +186,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to capture touch events in that area. static final int RESIZE_HANDLE_WIDTH_IN_DP = 30; - final WindowManagerService mService; final WindowManagerPolicy mPolicy; final Context mContext; final Session mSession; @@ -627,7 +626,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a, int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) { - mService = service; + super(service); mSession = s; mClient = c; mAppOp = appOp; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index a3d4b71a68af..5bcf59cf1711 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -46,9 +46,6 @@ import java.io.PrintWriter; class WindowToken extends WindowContainer<WindowState> { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM; - // The window manager! - protected final WindowManagerService mService; - // The actual token. final IBinder token; @@ -107,7 +104,7 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) { - mService = service; + super(service); token = _token; windowType = type; mPersistOnEmpty = persistOnEmpty; diff --git a/services/coverage/Android.bp b/services/coverage/Android.bp new file mode 100644 index 000000000000..16c9c1bfcdaa --- /dev/null +++ b/services/coverage/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.coverage", + srcs: ["java/**/*.java"], + libs: ["jacocoagent"], +} diff --git a/services/coverage/Android.mk b/services/coverage/Android.mk deleted file mode 100644 index da99994d6b4c..000000000000 --- a/services/coverage/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.coverage - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := jacocoagent - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp new file mode 100644 index 000000000000..05052047e0fb --- /dev/null +++ b/services/devicepolicy/Android.bp @@ -0,0 +1,9 @@ +java_library_static { + name: "services.devicepolicy", + srcs: ["java/**/*.java"], + + libs: [ + "conscrypt", + "services.core", + ], +} diff --git a/services/devicepolicy/Android.mk b/services/devicepolicy/Android.mk deleted file mode 100644 index 7020f174fefe..000000000000 --- a/services/devicepolicy/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.devicepolicy - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := conscrypt services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b979ff46702d..a3bcdcb9442e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -40,6 +40,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS; import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL; import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP; +import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE; import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES; import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS; import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT; @@ -79,7 +80,6 @@ import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; -import android.app.admin.IDevicePolicyManager; import android.app.admin.NetworkEvent; import android.app.admin.PasswordMetrics; import android.app.admin.SecurityLog; @@ -297,7 +297,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DELEGATION_ENABLE_SYSTEM_APP, DELEGATION_KEEP_UNINSTALLED_PACKAGES, DELEGATION_PACKAGE_ACCESS, - DELEGATION_PERMISSION_GRANT + DELEGATION_PERMISSION_GRANT, + DELEGATION_INSTALL_EXISTING_PACKAGE, + DELEGATION_KEEP_UNINSTALLED_PACKAGES }; /** @@ -740,7 +742,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_ORGANIZATION_NAME = "organization-name"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; - private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled"; + private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled"; final DeviceAdminInfo info; @@ -790,7 +792,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean requireAutoTime = false; // Can only be set by a device owner. boolean forceEphemeralUsers = false; // Can only be set by a device owner. boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner. - boolean isLogoutButtonEnabled = false; // Can only be set by a device owner. + boolean isLogoutEnabled = false; // Can only be set by a device owner. // one notification after enabling + one more after reboots static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2; @@ -1108,10 +1110,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.text(organizationName); out.endTag(null, TAG_ORGANIZATION_NAME); } - if (isLogoutButtonEnabled) { - out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED); - out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled)); - out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED); + if (isLogoutEnabled) { + out.startTag(null, TAG_IS_LOGOUT_ENABLED); + out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled)); + out.endTag(null, TAG_IS_LOGOUT_ENABLED); } } @@ -1286,8 +1288,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else { Log.w(LOG_TAG, "Missing text when loading organization name"); } - } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) { - isLogoutButtonEnabled = Boolean.parseBoolean( + } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) { + isLogoutEnabled = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); @@ -8844,6 +8846,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean installExistingPackage(ComponentName who, String callerPackage, + String packageName) { + synchronized (this) { + // Ensure the caller is a PO or an install existing package delegate + enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, + DELEGATION_INSTALL_EXISTING_PACKAGE); + final int callingUserId = mInjector.userHandleGetCallingUserId(); + if (!isUserAffiliatedWithDeviceLocked(callingUserId)) { + throw new SecurityException("Admin " + who + + " is neither the device owner or affiliated user's profile owner."); + } + + final long id = mInjector.binderClearCallingIdentity(); + try { + if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "installing " + packageName + " for " + + callingUserId); + } + + // Install the package. + return mIPackageManager.installExistingPackageAsUser(packageName, callingUserId, + 0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY) + == PackageManager.INSTALL_SUCCEEDED; + } catch (RemoteException re) { + // shouldn't happen + return false; + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + } + } + + @Override public void setAccountManagementDisabled(ComponentName who, String accountType, boolean disabled) { if (!mHasFeature) { @@ -11555,8 +11590,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { long ident = mInjector.binderClearCallingIdentity(); try { - return ActivityManager.getService().clearApplicationUserData(packageName, callback, - userId); + return ActivityManager.getService().clearApplicationUserData(packageName, false, + callback, userId); } catch(RemoteException re) { // Same process, should not happen. } catch (SecurityException se) { @@ -11581,35 +11616,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) { + public synchronized void setLogoutEnabled(ComponentName admin, boolean enabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(admin); getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - if (enabled == isLogoutButtonEnabledInternalLocked()) { + if (enabled == isLogoutEnabledInternalLocked()) { // already in the requested state return; } ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - deviceOwner.isLogoutButtonEnabled = enabled; + deviceOwner.isLogoutEnabled = enabled; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } @Override - public boolean isLogoutButtonEnabled() { + public boolean isLogoutEnabled() { if (!mHasFeature) { return false; } synchronized (this) { - return isLogoutButtonEnabledInternalLocked(); + return isLogoutEnabledInternalLocked(); } } - private boolean isLogoutButtonEnabledInternalLocked() { + private boolean isLogoutEnabledInternalLocked() { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled; + return (deviceOwner != null) && deviceOwner.isLogoutEnabled; } } diff --git a/services/midi/Android.bp b/services/midi/Android.bp new file mode 100644 index 000000000000..3745e89e8034 --- /dev/null +++ b/services/midi/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.midi", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/midi/Android.mk b/services/midi/Android.mk deleted file mode 100644 index faac01cd86f4..000000000000 --- a/services/midi/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.midi - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/net/Android.bp b/services/net/Android.bp new file mode 100644 index 000000000000..e0ae68f20483 --- /dev/null +++ b/services/net/Android.bp @@ -0,0 +1,4 @@ +java_library_static { + name: "services.net", + srcs: ["java/**/*.java"], +} diff --git a/services/net/Android.mk b/services/net/Android.mk deleted file mode 100644 index 408794e7d0b9..000000000000 --- a/services/net/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.net - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_AIDL_INCLUDES += \ - system/netd/server/binder - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java index 1925c39e5211..6cf4fa9a3dfc 100644 --- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java +++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java @@ -19,7 +19,7 @@ package android.net.ip; import static android.system.OsConstants.*; import android.net.NetworkUtils; -import android.net.util.BlockingSocketReader; +import android.net.util.PacketReader; import android.net.util.ConnectivityPacketSummary; import android.os.Handler; import android.system.ErrnoException; @@ -65,7 +65,7 @@ public class ConnectivityPacketTracker { private final String mTag; private final LocalLog mLog; - private final BlockingSocketReader mPacketListener; + private final PacketReader mPacketListener; private boolean mRunning; private String mDisplayName; @@ -101,7 +101,7 @@ public class ConnectivityPacketTracker { mDisplayName = null; } - private final class PacketListener extends BlockingSocketReader { + private final class PacketListener extends PacketReader { private final int mIfIndex; private final byte mHwAddr[]; diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java index 70983c864ffc..fdb366c55a7b 100644 --- a/services/net/java/android/net/ip/IpClient.java +++ b/services/net/java/android/net/ip/IpClient.java @@ -815,6 +815,15 @@ public class IpClient extends StateMachine { pw.println(Objects.toString(provisioningConfig, "N/A")); pw.decreaseIndent(); + final IpReachabilityMonitor iprm = mIpReachabilityMonitor; + if (iprm != null) { + pw.println(); + pw.println(mTag + " current IpReachabilityMonitor state:"); + pw.increaseIndent(); + iprm.dump(pw); + pw.decreaseIndent(); + } + pw.println(); pw.println(mTag + " StateMachine dump:"); pw.increaseIndent(); @@ -1237,6 +1246,7 @@ public class IpClient extends StateMachine { mIpReachabilityMonitor = new IpReachabilityMonitor( mContext, mInterfaceName, + getHandler(), mLog, new IpReachabilityMonitor.Callback() { @Override diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java new file mode 100644 index 000000000000..680733478657 --- /dev/null +++ b/services/net/java/android/net/ip/IpNeighborMonitor.java @@ -0,0 +1,236 @@ +/* + * 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.net.ip; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.NetlinkMessage; +import android.net.netlink.NetlinkSocket; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlMsgHdr; +import android.net.util.PacketReader; +import android.net.util.SharedLog; +import android.os.Handler; +import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.Os; +import android.system.OsConstants; +import android.util.Log; + +import com.android.internal.util.BitUtils; + +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.StringJoiner; + + +/** + * IpNeighborMonitor. + * + * Monitors the kernel rtnetlink neighbor notifications and presents to callers + * NeighborEvents describing each event. Callers can provide a consumer instance + * to both filter (e.g. by interface index and IP address) and handle the + * generated NeighborEvents. + * + * @hide + */ +public class IpNeighborMonitor extends PacketReader { + private static final String TAG = IpNeighborMonitor.class.getSimpleName(); + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) + * for the given IP address on the specified interface index. + * + * @return 0 if the request was successfully passed to the kernel; otherwise return + * a non-zero error code. + */ + public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) { + final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; + if (DBG) { Log.d(TAG, msgSnippet); } + + final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( + 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); + + try { + NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); + } catch (ErrnoException e) { + Log.e(TAG, "Error " + msgSnippet + ": " + e); + return -e.errno; + } + + return 0; + } + + public static class NeighborEvent { + final long elapsedMs; + final short msgType; + final int ifindex; + final InetAddress ip; + final short nudState; + final byte[] linkLayerAddr; + + public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, + short nudState, byte[] linkLayerAddr) { + this.elapsedMs = elapsedMs; + this.msgType = msgType; + this.ifindex = ifindex; + this.ip = ip; + this.nudState = nudState; + this.linkLayerAddr = linkLayerAddr; + } + + boolean isConnected() { + return (msgType != NetlinkConstants.RTM_DELNEIGH) && + StructNdMsg.isNudStateConnected(nudState); + } + + boolean isValid() { + return (msgType != NetlinkConstants.RTM_DELNEIGH) && + StructNdMsg.isNudStateValid(nudState); + } + + @Override + public String toString() { + final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}"); + return j.add("@" + elapsedMs) + .add(NetlinkConstants.stringForNlMsgType(msgType)) + .add("if=" + ifindex) + .add(ip.getHostAddress()) + .add(StructNdMsg.stringForNudState(nudState)) + .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]") + .toString(); + } + } + + public interface NeighborEventConsumer { + // Every neighbor event received on the netlink socket is passed in + // here. Subclasses should filter for events of interest. + public void accept(NeighborEvent event); + } + + private final SharedLog mLog; + private final NeighborEventConsumer mConsumer; + + public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { + super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); + mLog = log.forSubComponent(TAG); + mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; + } + + @Override + protected FileDescriptor createFd() { + FileDescriptor fd = null; + + try { + fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE); + Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH))); + Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0))); + + if (VDBG) { + final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd); + Log.d(TAG, "bound to sockaddr_nl{" + + BitUtils.uint32(nlAddr.getPortId()) + ", " + + nlAddr.getGroupsMask() + + "}"); + } + } catch (ErrnoException|SocketException e) { + logError("Failed to create rtnetlink socket", e); + IoUtils.closeQuietly(fd); + return null; + } + + return fd; + } + + @Override + protected void handlePacket(byte[] recvbuf, int length) { + final long whenMs = SystemClock.elapsedRealtime(); + + final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); + byteBuffer.order(ByteOrder.nativeOrder()); + + parseNetlinkMessageBuffer(byteBuffer, whenMs); + } + + private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { + while (byteBuffer.remaining() > 0) { + final int position = byteBuffer.position(); + final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); + if (nlMsg == null || nlMsg.getHeader() == null) { + byteBuffer.position(position); + mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); + break; + } + + final int srcPortId = nlMsg.getHeader().nlmsg_pid; + if (srcPortId != 0) { + mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId)); + break; + } + + if (nlMsg instanceof NetlinkErrorMessage) { + mLog.e("netlink error: " + nlMsg); + continue; + } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { + mLog.i("non-rtnetlink neighbor msg: " + nlMsg); + continue; + } + + evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); + } + } + + private void evaluateRtNetlinkNeighborMessage( + RtNetlinkNeighborMessage neighMsg, long whenMs) { + final short msgType = neighMsg.getHeader().nlmsg_type; + final StructNdMsg ndMsg = neighMsg.getNdHeader(); + if (ndMsg == null) { + mLog.e("RtNetlinkNeighborMessage without ND message header!"); + return; + } + + final int ifindex = ndMsg.ndm_ifindex; + final InetAddress destination = neighMsg.getDestination(); + final short nudState = + (msgType == NetlinkConstants.RTM_DELNEIGH) + ? StructNdMsg.NUD_NONE + : ndMsg.ndm_state; + + final NeighborEvent event = new NeighborEvent( + whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress()); + + if (VDBG) { + Log.d(TAG, neighMsg.toString()); + } + if (DBG) { + Log.d(TAG, event.toString()); + } + + mConsumer.accept(event); + } +} diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 714b35a03396..b31ffbba0279 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -22,30 +22,27 @@ import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.ProxyInfo; import android.net.RouteInfo; +import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpReachabilityEvent; -import android.net.netlink.NetlinkConstants; -import android.net.netlink.NetlinkErrorMessage; -import android.net.netlink.NetlinkMessage; -import android.net.netlink.NetlinkSocket; -import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdMsg; -import android.net.netlink.StructNdaCacheInfo; -import android.net.netlink.StructNlMsgHdr; import android.net.util.MultinetworkPolicyTracker; import android.net.util.SharedLog; +import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.system.ErrnoException; -import android.system.NetlinkSocketAddress; import android.system.OsConstants; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.DumpUtils.Dump; import java.io.InterruptedIOException; +import java.io.PrintWriter; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -134,6 +131,8 @@ import java.util.Set; * state it may be best for the link to disconnect completely and * reconnect afresh. * + * Accessing an instance of this class from multiple threads is NOT safe. + * * @hide */ public class IpReachabilityMonitor { @@ -169,64 +168,33 @@ public class IpReachabilityMonitor { } } - private final Object mLock = new Object(); private final String mInterfaceName; private final int mInterfaceIndex; + private final IpNeighborMonitor mIpNeighborMonitor; private final SharedLog mLog; private final Callback mCallback; private final Dependencies mDependencies; private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; - private final NetlinkSocketObserver mNetlinkSocketObserver; - private final Thread mObserverThread; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - @GuardedBy("mLock") private LinkProperties mLinkProperties = new LinkProperties(); - // TODO: consider a map to a private NeighborState class holding more - // information than a single NUD state entry. - @GuardedBy("mLock") - private Map<InetAddress, Short> mIpWatchList = new HashMap<>(); - @GuardedBy("mLock") - private int mIpWatchListVersion; - private volatile boolean mRunning; + private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>(); // Time in milliseconds of the last forced probe request. private volatile long mLastProbeTimeMs; - /** - * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) - * for the given IP address on the specified interface index. - * - * @return 0 if the request was successfully passed to the kernel; otherwise return - * a non-zero error code. - */ - private static int probeNeighbor(int ifIndex, InetAddress ip) { - final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; - if (DBG) { Log.d(TAG, msgSnippet); } - - final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( - 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); - - try { - NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); - } catch (ErrnoException e) { - Log.e(TAG, "Error " + msgSnippet + ": " + e); - return -e.errno; - } - - return 0; + public IpReachabilityMonitor( + Context context, String ifName, Handler h, SharedLog log, Callback callback) { + this(context, ifName, h, log, callback, null); } - public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) { - this(context, ifName, log, callback, null); - } - - public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback, + public IpReachabilityMonitor( + Context context, String ifName, Handler h, SharedLog log, Callback callback, MultinetworkPolicyTracker tracker) { - this(ifName, getInterfaceIndex(ifName), log, callback, tracker, + this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker, Dependencies.makeDefault(context, ifName)); } @VisibleForTesting - IpReachabilityMonitor(String ifName, int ifIndex, SharedLog log, Callback callback, + IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback, MultinetworkPolicyTracker tracker, Dependencies dependencies) { mInterfaceName = ifName; mLog = log.forSubComponent(TAG); @@ -234,45 +202,54 @@ public class IpReachabilityMonitor { mMultinetworkPolicyTracker = tracker; mInterfaceIndex = ifIndex; mDependencies = dependencies; - mNetlinkSocketObserver = new NetlinkSocketObserver(); - mObserverThread = new Thread(mNetlinkSocketObserver); - mObserverThread.start(); + + mIpNeighborMonitor = new IpNeighborMonitor(h, mLog, + (NeighborEvent event) -> { + if (mInterfaceIndex != event.ifindex) return; + if (!mNeighborWatchList.containsKey(event.ip)) return; + + final NeighborEvent prev = mNeighborWatchList.put(event.ip, event); + + // TODO: Consider what to do with other states that are not within + // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE). + if (event.nudState == StructNdMsg.NUD_FAILED) { + mLog.w("ALERT neighbor went from: " + prev + " to: " + event); + handleNeighborLost(event); + } + }); + mIpNeighborMonitor.start(); } public void stop() { - mRunning = false; + mIpNeighborMonitor.stop(); clearLinkProperties(); - mNetlinkSocketObserver.clearNetlinkSocket(); } - // TODO: add a public dump() method that can be called during a bug report. - - private String describeWatchList() { - final String delimiter = ", "; - StringBuilder sb = new StringBuilder(); - synchronized (mLock) { - sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, "); - sb.append("v{" + mIpWatchListVersion + "}, "); - sb.append("ntable=["); - boolean firstTime = true; - for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(entry.getKey().getHostAddress() + "/" + - StructNdMsg.stringForNudState(entry.getValue())); - } - sb.append("]"); - } - return sb.toString(); + public void dump(PrintWriter pw) { + DumpUtils.dumpAsync( + mIpNeighborMonitor.getHandler(), + new Dump() { + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(describeWatchList("\n")); + } + }, + pw, "", 1000); } - private boolean isWatching(InetAddress ip) { - synchronized (mLock) { - return mRunning && mIpWatchList.containsKey(ip); + private String describeWatchList() { return describeWatchList(" "); } + + private String describeWatchList(String sep) { + final StringBuilder sb = new StringBuilder(); + sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep); + sb.append("ntable=[" + sep); + String delimiter = ""; + for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { + sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue()); + delimiter = "," + sep; } + sb.append("]"); + return sb.toString(); } private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { @@ -284,13 +261,6 @@ public class IpReachabilityMonitor { return false; } - private short getNeighborStateLocked(InetAddress ip) { - if (mIpWatchList.containsKey(ip)) { - return mIpWatchList.get(ip); - } - return StructNdMsg.NUD_NONE; - } - public void updateLinkProperties(LinkProperties lp) { if (!mInterfaceName.equals(lp.getInterfaceName())) { // TODO: figure out whether / how to cope with interface changes. @@ -299,70 +269,63 @@ public class IpReachabilityMonitor { return; } - synchronized (mLock) { - mLinkProperties = new LinkProperties(lp); - Map<InetAddress, Short> newIpWatchList = new HashMap<>(); + mLinkProperties = new LinkProperties(lp); + Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>(); - final List<RouteInfo> routes = mLinkProperties.getRoutes(); - for (RouteInfo route : routes) { - if (route.hasGateway()) { - InetAddress gw = route.getGateway(); - if (isOnLink(routes, gw)) { - newIpWatchList.put(gw, getNeighborStateLocked(gw)); - } + final List<RouteInfo> routes = mLinkProperties.getRoutes(); + for (RouteInfo route : routes) { + if (route.hasGateway()) { + InetAddress gw = route.getGateway(); + if (isOnLink(routes, gw)) { + newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null)); } } + } - for (InetAddress nameserver : lp.getDnsServers()) { - if (isOnLink(routes, nameserver)) { - newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver)); - } + for (InetAddress dns : lp.getDnsServers()) { + if (isOnLink(routes, dns)) { + newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null)); } - - mIpWatchList = newIpWatchList; - mIpWatchListVersion++; } + + mNeighborWatchList = newNeighborWatchList; if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } } public void clearLinkProperties() { - synchronized (mLock) { - mLinkProperties.clear(); - mIpWatchList.clear(); - mIpWatchListVersion++; - } + mLinkProperties.clear(); + mNeighborWatchList.clear(); if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } } - private void handleNeighborLost(String msg) { - InetAddress ip = null; - final ProvisioningChange delta; - synchronized (mLock) { - LinkProperties whatIfLp = new LinkProperties(mLinkProperties); + private void handleNeighborLost(NeighborEvent event) { + final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); - for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { - if (entry.getValue() != StructNdMsg.NUD_FAILED) { - continue; - } - - ip = entry.getKey(); - for (RouteInfo route : mLinkProperties.getRoutes()) { - if (ip.equals(route.getGateway())) { - whatIfLp.removeRoute(route); - } - } - - if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { - // We should do this unconditionally, but alas we cannot: b/31827713. - whatIfLp.removeDnsServer(ip); + InetAddress ip = null; + for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { + // TODO: Consider using NeighborEvent#isValid() here; it's more + // strict but may interact badly if other entries are somehow in + // NUD_INCOMPLETE (say, during network attach). + if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue; + + ip = entry.getKey(); + for (RouteInfo route : mLinkProperties.getRoutes()) { + if (ip.equals(route.getGateway())) { + whatIfLp.removeRoute(route); } } - delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp); + if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { + // We should do this unconditionally, but alas we cannot: b/31827713. + whatIfLp.removeDnsServer(ip); + } } + final ProvisioningChange delta = LinkProperties.compareProvisioning( + mLinkProperties, whatIfLp); + if (delta == ProvisioningChange.LOST_PROVISIONING) { - final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; + final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; Log.w(TAG, logMsg); if (mCallback != null) { // TODO: remove |ip| when the callback signature no longer has @@ -378,12 +341,9 @@ public class IpReachabilityMonitor { } public void probeAll() { - final List<InetAddress> ipProbeList; - synchronized (mLock) { - ipProbeList = new ArrayList<>(mIpWatchList.keySet()); - } + final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet()); - if (!ipProbeList.isEmpty() && mRunning) { + if (!ipProbeList.isEmpty()) { // Keep the CPU awake long enough to allow all ARP/ND // probes a reasonable chance at success. See b/23197666. // @@ -394,13 +354,10 @@ public class IpReachabilityMonitor { } for (InetAddress target : ipProbeList) { - if (!mRunning) { - break; - } - final int returnValue = probeNeighbor(mInterfaceIndex, target); + final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target); mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)", - target.getHostAddress(), returnValue)); - logEvent(IpReachabilityEvent.PROBE, returnValue); + target.getHostAddress(), rval)); + logEvent(IpReachabilityEvent.PROBE, rval); } mLastProbeTimeMs = SystemClock.elapsedRealtime(); } @@ -446,153 +403,4 @@ public class IpReachabilityMonitor { int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost); mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType)); } - - // TODO: simplify the number of objects by making this extend Thread. - private final class NetlinkSocketObserver implements Runnable { - private NetlinkSocket mSocket; - - @Override - public void run() { - if (VDBG) { Log.d(TAG, "Starting observing thread."); } - mRunning = true; - - try { - setupNetlinkSocket(); - } catch (ErrnoException | SocketException e) { - Log.e(TAG, "Failed to suitably initialize a netlink socket", e); - mRunning = false; - } - - while (mRunning) { - final ByteBuffer byteBuffer; - try { - byteBuffer = recvKernelReply(); - } catch (ErrnoException e) { - if (mRunning) { Log.w(TAG, "ErrnoException: ", e); } - break; - } - final long whenMs = SystemClock.elapsedRealtime(); - if (byteBuffer == null) { - continue; - } - parseNetlinkMessageBuffer(byteBuffer, whenMs); - } - - clearNetlinkSocket(); - - mRunning = false; // Not a no-op when ErrnoException happened. - if (VDBG) { Log.d(TAG, "Finishing observing thread."); } - } - - private void clearNetlinkSocket() { - if (mSocket != null) { - mSocket.close(); - } - } - - // TODO: Refactor the main loop to recreate the socket upon recoverable errors. - private void setupNetlinkSocket() throws ErrnoException, SocketException { - clearNetlinkSocket(); - mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - - final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( - 0, OsConstants.RTMGRP_NEIGH); - mSocket.bind(listenAddr); - - if (VDBG) { - final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); - Log.d(TAG, "bound to sockaddr_nl{" - + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " - + nlAddr.getGroupsMask() - + "}"); - } - } - - private ByteBuffer recvKernelReply() throws ErrnoException { - try { - return mSocket.recvMessage(0); - } catch (InterruptedIOException e) { - // Interruption or other error, e.g. another thread closed our file descriptor. - } catch (ErrnoException e) { - if (e.errno != OsConstants.EAGAIN) { - throw e; - } - } - return null; - } - - private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { - while (byteBuffer.remaining() > 0) { - final int position = byteBuffer.position(); - final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); - if (nlMsg == null || nlMsg.getHeader() == null) { - byteBuffer.position(position); - Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); - break; - } - - final int srcPortId = nlMsg.getHeader().nlmsg_pid; - if (srcPortId != 0) { - Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); - break; - } - - if (nlMsg instanceof NetlinkErrorMessage) { - Log.e(TAG, "netlink error: " + nlMsg); - continue; - } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { - if (DBG) { - Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); - } - continue; - } - - evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); - } - } - - private void evaluateRtNetlinkNeighborMessage( - RtNetlinkNeighborMessage neighMsg, long whenMs) { - final StructNdMsg ndMsg = neighMsg.getNdHeader(); - if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { - return; - } - - final InetAddress destination = neighMsg.getDestination(); - if (!isWatching(destination)) { - return; - } - - final short msgType = neighMsg.getHeader().nlmsg_type; - final short nudState = ndMsg.ndm_state; - final String eventMsg = "NeighborEvent{" - + "elapsedMs=" + whenMs + ", " - + destination.getHostAddress() + ", " - + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " - + NetlinkConstants.stringForNlMsgType(msgType) + ", " - + StructNdMsg.stringForNudState(nudState) - + "}"; - - if (VDBG) { - Log.d(TAG, neighMsg.toString()); - } else if (DBG) { - Log.d(TAG, eventMsg); - } - - synchronized (mLock) { - if (mIpWatchList.containsKey(destination)) { - final short value = - (msgType == NetlinkConstants.RTM_DELNEIGH) - ? StructNdMsg.NUD_NONE - : nudState; - mIpWatchList.put(destination, value); - } - } - - if (nudState == StructNdMsg.NUD_FAILED) { - Log.w(TAG, "ALERT: " + eventMsg); - handleNeighborLost(eventMsg); - } - } - } } diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java index f5f211d8e631..5af3c299bfc1 100644 --- a/services/net/java/android/net/netlink/NetlinkSocket.java +++ b/services/net/java/android/net/netlink/NetlinkSocket.java @@ -16,16 +16,24 @@ package android.net.netlink; +import static android.system.OsConstants.AF_NETLINK; +import static android.system.OsConstants.EIO; +import static android.system.OsConstants.EPROTO; +import static android.system.OsConstants.ETIMEDOUT; +import static android.system.OsConstants.SO_RCVBUF; +import static android.system.OsConstants.SO_RCVTIMEO; +import static android.system.OsConstants.SO_SNDTIMEO; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOL_SOCKET; + import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.Os; -import android.system.OsConstants; import android.system.StructTimeval; import android.util.Log; import libcore.io.IoUtils; import libcore.io.Libcore; -import java.io.Closeable; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.net.SocketAddress; @@ -37,28 +45,27 @@ import java.nio.ByteOrder; /** * NetlinkSocket * - * A small wrapper class to assist with AF_NETLINK socket operations. + * A small static class to assist with AF_NETLINK socket operations. * * @hide */ -public class NetlinkSocket implements Closeable { +public class NetlinkSocket { private static final String TAG = "NetlinkSocket"; - private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; - private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; - final private FileDescriptor mDescriptor; - private NetlinkSocketAddress mAddr; - private long mLastRecvTimeoutMs; - private long mLastSendTimeoutMs; + public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; + public static final int SOCKET_RECV_BUFSIZE = 64 * 1024; public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; + final long IO_TIMEOUT = 300L; + + FileDescriptor fd; - try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) { - final long IO_TIMEOUT = 300L; - nlSocket.connectToKernel(); - nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); - final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); + try { + fd = forProto(nlProto); + connectToKernel(fd); + sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT); + final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && @@ -81,61 +88,30 @@ public class NetlinkSocket implements Closeable { errmsg = response.toString(); } Log.e(TAG, errPrefix + ", errmsg=" + errmsg); - throw new ErrnoException(errmsg, OsConstants.EPROTO); + throw new ErrnoException(errmsg, EPROTO); } } catch (InterruptedIOException e) { Log.e(TAG, errPrefix, e); - throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e); + throw new ErrnoException(errPrefix, ETIMEDOUT, e); } catch (SocketException e) { Log.e(TAG, errPrefix, e); - throw new ErrnoException(errPrefix, OsConstants.EIO, e); + throw new ErrnoException(errPrefix, EIO, e); } - } - - public NetlinkSocket(int nlProto) throws ErrnoException { - mDescriptor = Os.socket( - OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); - - Os.setsockoptInt( - mDescriptor, OsConstants.SOL_SOCKET, - OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); - } - - public NetlinkSocketAddress getLocalAddress() throws ErrnoException { - return (NetlinkSocketAddress) Os.getsockname(mDescriptor); - } - public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { - Os.bind(mDescriptor, (SocketAddress)localAddr); + IoUtils.closeQuietly(fd); } - public void connectTo(NetlinkSocketAddress peerAddr) - throws ErrnoException, SocketException { - Os.connect(mDescriptor, (SocketAddress) peerAddr); + public static FileDescriptor forProto(int nlProto) throws ErrnoException { + final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto); + Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE); + return fd; } - public void connectToKernel() throws ErrnoException, SocketException { - connectTo(new NetlinkSocketAddress(0, 0)); - } - - /** - * Wait indefinitely (or until underlying socket error) for a - * netlink message of at most DEFAULT_RECV_BUFSIZE size. - */ - public ByteBuffer recvMessage() - throws ErrnoException, InterruptedIOException { - return recvMessage(DEFAULT_RECV_BUFSIZE, 0); - } - - /** - * Wait up to |timeoutMs| (or until underlying socket error) for a - * netlink message of at most DEFAULT_RECV_BUFSIZE size. - */ - public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { - return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); + public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException { + Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0))); } - private void checkTimeout(long timeoutMs) { + private static void checkTimeout(long timeoutMs) { if (timeoutMs < 0) { throw new IllegalArgumentException("Negative timeouts not permitted"); } @@ -147,21 +123,14 @@ public class NetlinkSocket implements Closeable { * * Multi-threaded calls with different timeouts will cause unexpected results. */ - public ByteBuffer recvMessage(int bufsize, long timeoutMs) + public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); - synchronized (mDescriptor) { - if (mLastRecvTimeoutMs != timeoutMs) { - Os.setsockoptTimeval(mDescriptor, - OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, - StructTimeval.fromMillis(timeoutMs)); - mLastRecvTimeoutMs = timeoutMs; - } - } + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs)); ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); - int length = Os.read(mDescriptor, byteBuffer); + int length = Os.read(fd, byteBuffer); if (length == bufsize) { Log.w(TAG, "maximum read"); } @@ -172,39 +141,16 @@ public class NetlinkSocket implements Closeable { } /** - * Send a message to a peer to which this socket has previously connected. - * - * This blocks until completion or an error occurs. - */ - public boolean sendMessage(byte[] bytes, int offset, int count) - throws ErrnoException, InterruptedIOException { - return sendMessage(bytes, offset, count, 0); - } - - /** * Send a message to a peer to which this socket has previously connected, * waiting at most |timeoutMs| milliseconds for the send to complete. * * Multi-threaded calls with different timeouts will cause unexpected results. */ - public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) + public static int sendMessage( + FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); - - synchronized (mDescriptor) { - if (mLastSendTimeoutMs != timeoutMs) { - Os.setsockoptTimeval(mDescriptor, - OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, - StructTimeval.fromMillis(timeoutMs)); - mLastSendTimeoutMs = timeoutMs; - } - } - - return (count == Os.write(mDescriptor, bytes, offset, count)); - } - - @Override - public void close() { - IoUtils.closeQuietly(mDescriptor); + Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs)); + return Os.write(fd, bytes, offset, count); } } diff --git a/services/net/java/android/net/netlink/StructNdMsg.java b/services/net/java/android/net/netlink/StructNdMsg.java index b68ec0bc6226..e34ec39ab99b 100644 --- a/services/net/java/android/net/netlink/StructNdMsg.java +++ b/services/net/java/android/net/netlink/StructNdMsg.java @@ -63,6 +63,11 @@ public class StructNdMsg { return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0); } + public static boolean isNudStateValid(short nudState) { + return (isNudStateConnected(nudState) || + ((nudState & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + } + // Neighbor Cache Entry Flags public static byte NTF_USE = (byte) 0x01; public static byte NTF_SELF = (byte) 0x02; @@ -143,7 +148,7 @@ public class StructNdMsg { } public boolean nudValid() { - return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + return isNudStateValid(ndm_state); } @Override diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/PacketReader.java index 99bf46952b4e..10da2a551e21 100644 --- a/services/net/java/android/net/util/BlockingSocketReader.java +++ b/services/net/java/android/net/util/PacketReader.java @@ -67,7 +67,7 @@ import java.io.IOException; * * @hide */ -public abstract class BlockingSocketReader { +public abstract class PacketReader { private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; private static final int UNREGISTER_THIS_FD = 0; @@ -83,11 +83,11 @@ public abstract class BlockingSocketReader { IoUtils.closeQuietly(fd); } - protected BlockingSocketReader(Handler h) { + protected PacketReader(Handler h) { this(h, DEFAULT_RECV_BUF_SIZE); } - protected BlockingSocketReader(Handler h, int recvbufsize) { + protected PacketReader(Handler h, int recvbufsize) { mHandler = h; mQueue = mHandler.getLooper().getQueue(); mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)]; @@ -115,6 +115,8 @@ public abstract class BlockingSocketReader { } } + public Handler getHandler() { return mHandler; } + public final int recvBufSize() { return mPacket.length; } public final long numPacketsReceived() { return mPacketsReceived; } diff --git a/services/print/Android.bp b/services/print/Android.bp new file mode 100644 index 000000000000..80a8c7560de6 --- /dev/null +++ b/services/print/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.print", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/print/Android.mk b/services/print/Android.mk deleted file mode 100644 index 00eb2e4f9f98..000000000000 --- a/services/print/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.print - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/restrictions/Android.bp b/services/restrictions/Android.bp new file mode 100644 index 000000000000..979e891ada5f --- /dev/null +++ b/services/restrictions/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.restrictions", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk deleted file mode 100644 index 57d1c465f825..000000000000 --- a/services/restrictions/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.restrictions - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/tests/Android.mk b/services/tests/Android.mk deleted file mode 100644 index 40369ee2ec5c..000000000000 --- a/services/tests/Android.mk +++ /dev/null @@ -1,3 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java index 7160d7fc8c2f..a44f5c4f8fff 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import android.content.Intent; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -61,14 +62,13 @@ public class ActivityStartControllerTests extends ActivityTestsBase { mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory); mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor, mock(ActivityStartInterceptor.class))); - doReturn(mStarter).when(mFactory).obtainStarter(); + doReturn(mStarter).when(mFactory).obtain(); } /** * Ensures that pending launches are processed. */ @Test - @Presubmit public void testPendingActivityLaunches() { final Random random = new Random(); @@ -88,4 +88,20 @@ public class ActivityStartControllerTests extends ActivityTestsBase { verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null), eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null)); } + + + /** + * Ensures instances are recycled after execution. + */ + @Test + public void testRecycling() throws Exception { + final Intent intent = new Intent(); + final ActivityStarter optionStarter = new ActivityStarter(mController, mService, + mService.mStackSupervisor, mock(ActivityStartInterceptor.class)); + optionStarter + .setIntent(intent) + .setReason("Test") + .execute(); + verify(mFactory, times(1)).recycle(eq(optionStarter)); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index e8194dd8e777..589a89bcbddf 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -63,6 +63,7 @@ import static android.app.ActivityManager.START_PERMISSION_DENIED; import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED; import com.android.internal.os.BatteryStatsImpl; +import com.android.server.am.ActivityStarter.Factory; /** * Tests for the {@link ActivityStarter} class. diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 926a9112ad29..8cf575eec4d9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -2121,24 +2121,32 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { PACKAGE_FALLBACK_LAUNCHER_PRIORITY); } - protected void makeCallerForeground() { + protected void makeUidForeground(int uid) { try { mService.mUidObserver.onUidStateChanged( - mInjectedCallingUid, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); + uid, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } - protected void makeCallerBackground() { + protected void makeCallerForeground() { + makeUidForeground(mInjectedCallingUid); + } + + protected void makeUidBackground(int uid) { try { mService.mUidObserver.onUidStateChanged( - mInjectedCallingUid, ActivityManager.PROCESS_STATE_TOP_SLEEPING, 0); + uid, ActivityManager.PROCESS_STATE_TOP_SLEEPING, 0); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } + protected void makeCallerBackground() { + makeUidBackground(mInjectedCallingUid); + } + protected void publishManifestShortcutsAsCaller(int resId) { addManifestShortcutResource( new ComponentName(getCallingPackage(), ShortcutActivity.class.getName()), diff --git a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java index 3fd1d556df14..b2eb57267cec 100644 --- a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java @@ -16,19 +16,28 @@ package com.android.server.pm; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import android.os.SystemClock; import android.os.UserHandle; -import android.test.AndroidTestCase; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.util.Log; import com.android.internal.util.ArrayUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Arrays; -public class InstallerTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class InstallerTest { private static final String TAG = "InstallerTest"; private Installer mInstaller; @@ -64,7 +73,7 @@ public class InstallerTest extends AndroidTestCase { } } - @Override + @Before public void setUp() throws Exception { mInstaller = new Installer(getContext()); mInstaller.onStart(); @@ -72,13 +81,15 @@ public class InstallerTest extends AndroidTestCase { mQuota.reset(); } - @Override + @After public void tearDown() throws Exception { Log.i(TAG, mManual.toString()); Log.i(TAG, mQuota.toString()); mInstaller = null; } + @Test + @Ignore("b/68819006") public void testGetAppSize() throws Exception { int[] appIds = null; @@ -119,6 +130,8 @@ public class InstallerTest extends AndroidTestCase { } } + @Test + @Ignore("b/68819006") public void testGetUserSize() throws Exception { final int[] appIds = getAppIds(UserHandle.USER_SYSTEM); @@ -138,6 +151,8 @@ public class InstallerTest extends AndroidTestCase { checkEquals(Arrays.toString(appIds), stats, quotaStats); } + @Test + @Ignore("b/68819006") public void testGetExternalSize() throws Exception { final int[] appIds = getAppIds(UserHandle.USER_SYSTEM); @@ -167,6 +182,10 @@ public class InstallerTest extends AndroidTestCase { return appIds; } + private static Context getContext() { + return InstrumentationRegistry.getContext(); + } + private static void checkEquals(String msg, PackageStats a, PackageStats b) { checkEquals(msg + " codeSize", a.codeSize, b.codeSize); checkEquals(msg + " dataSize", a.dataSize, b.dataSize); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index beffceea7bc7..e12a8da805f6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -43,11 +43,13 @@ public class PackageManagerServiceTest extends AndroidTestCase { class PackageSenderImpl implements PackageSender { public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, final int flags, final String targetPkg, - final IIntentReceiver finishedReceiver, final int[] userIds) { + final IIntentReceiver finishedReceiver, final int[] userIds, + int[] instantUserIds) { } public void sendPackageAddedForNewUsers(String packageName, - boolean sendBootComplete, boolean includeStopped, int appId, int... userIds) { + boolean sendBootComplete, boolean includeStopped, int appId, + int[] userIds, int[] instantUserIds) { } } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 0995f2e35d50..fafae6c71355 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -206,8 +206,8 @@ public class PackageManagerSettingsTests { new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1"); private static final File UPDATED_CODE_PATH = new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-2"); - private static final int INITIAL_VERSION_CODE = 10023; - private static final int UPDATED_VERSION_CODE = 10025; + private static final long INITIAL_VERSION_CODE = 10023L; + private static final long UPDATED_VERSION_CODE = 10025L; @Test public void testPackageStateCopy01() { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 36cc3a059eac..32b0b266bafc 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -520,6 +520,10 @@ public class PackageParserTest { pkg.featureGroups = new ArrayList<>(); pkg.featureGroups.add(new FeatureGroupInfo()); + + pkg.mCompileSdkVersionCodename = "foo23"; + pkg.mCompileSdkVersion = 100; + pkg.mVersionCodeMajor = 100; } private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index f92b575bc976..2257960f6450 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -121,6 +121,8 @@ import java.util.function.BiConsumer; @SmallTest public class ShortcutManagerTest1 extends BaseShortcutManagerTest { + private static final boolean SKIP_FOR_BUG_67325252 = true; + /** * Test for the first launch path, no settings file available. */ @@ -722,6 +724,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } public void testCleanupDanglingBitmaps() throws Exception { + if (SKIP_FOR_BUG_67325252) { + return; + } + assertBitmapDirectories(USER_0, EMPTY_STRINGS); assertBitmapDirectories(USER_10, EMPTY_STRINGS); @@ -7912,4 +7918,35 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .forAllShortcuts(si -> assertTrue(si.isReturnedByServer())); }); } + + public void testIsForegroundDefaultLauncher_true() { + final ComponentName defaultLauncher = new ComponentName("default", "launcher"); + final int uid = 1024; + + setDefaultLauncher(UserHandle.USER_SYSTEM, defaultLauncher); + makeUidForeground(uid); + + assertTrue(mInternal.isForegroundDefaultLauncher("default", uid)); + } + + + public void testIsForegroundDefaultLauncher_defaultButNotForeground() { + final ComponentName defaultLauncher = new ComponentName("default", "launcher"); + final int uid = 1024; + + setDefaultLauncher(UserHandle.USER_SYSTEM, defaultLauncher); + makeUidBackground(uid); + + assertFalse(mInternal.isForegroundDefaultLauncher("default", uid)); + } + + public void testIsForegroundDefaultLauncher_foregroundButNotDefault() { + final ComponentName defaultLauncher = new ComponentName("default", "launcher"); + final int uid = 1024; + + setDefaultLauncher(UserHandle.USER_SYSTEM, defaultLauncher); + makeUidForeground(uid); + + assertFalse(mInternal.isForegroundDefaultLauncher("another", uid)); + } } diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java index 0941d4fd4fc9..9a6da0e791b5 100644 --- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java @@ -16,7 +16,12 @@ package com.android.server.policy; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; @@ -29,6 +34,7 @@ import android.graphics.PixelFormat; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.Surface; import android.view.WindowManager; import org.junit.Before; @@ -105,4 +111,123 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase { assertEquals(0, mAppWindow.attrs.systemUiVisibility); assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility); } + + @Test + public void layoutWindowLw_withDisplayCutout() { + addDisplayCutout(); + + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen() { + addDisplayCutout(); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { + addDisplayCutout(); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0); + assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0); + } + + + @Test + public void layoutWindowLw_withDisplayCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_seascape() { + addDisplayCutout(); + setRotation(ROTATION_270); + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mAppWindow.stableFrame, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + assertInsetBy(mAppWindow.contentFrame, + NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA; + mPolicy.addWindow(mAppWindow); + + mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mPolicy.layoutWindowLw(mAppWindow, null, mFrames); + + assertInsetBy(mAppWindow.parentFrame, 0, 0, 0, 0); + assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.contentFrame, + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0); + } + }
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java index 9c5570725baa..e7e9abad5bbe 100644 --- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java +++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java @@ -17,6 +17,9 @@ package com.android.server.policy; import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -54,6 +57,7 @@ public class PhoneWindowManagerTestBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; + static final int DISPLAY_CUTOUT_HEIGHT = 8; TestablePhoneWindowManager mPolicy; TestContextWrapper mContext; @@ -76,10 +80,17 @@ public class PhoneWindowManagerTestBase { mPolicy = TestablePhoneWindowManager.create(mContext); + setRotation(ROTATION_0); + } + + public void setRotation(int rotation) { DisplayInfo info = new DisplayInfo(); - info.logicalWidth = DISPLAY_WIDTH; - info.logicalHeight = DISPLAY_HEIGHT; - info.rotation = ROTATION_0; + + final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; + info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + info.rotation = rotation; + mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info); } @@ -95,7 +106,7 @@ public class PhoneWindowManagerTestBase { public void addNavigationBar() { mNavigationBar = new FakeWindowState(); - mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT, + mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT, TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); mNavigationBar.attrs.gravity = Gravity.BOTTOM; @@ -104,11 +115,16 @@ public class PhoneWindowManagerTestBase { mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; } + public void addDisplayCutout() { + mPolicy.mEmulateDisplayCutout = true; + } + /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, int expectedInsetRight, int expectedInsetBottom) { assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, - DISPLAY_WIDTH - expectedInsetRight, DISPLAY_HEIGHT - expectedInsetBottom), actual); + mFrames.mDisplayWidth - expectedInsetRight, + mFrames.mDisplayHeight - expectedInsetBottom), actual); } /** @@ -181,6 +197,11 @@ public class PhoneWindowManagerTestBase { policy[0].mAccessibilityManager = new AccessibilityManager(context, mock(IAccessibilityManager.class), UserHandle.USER_CURRENT); policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class); + policy[0].mNavigationBarCanMove = true; + policy[0].mPortraitRotation = ROTATION_0; + policy[0].mLandscapeRotation = ROTATION_90; + policy[0].mUpsideDownRotation = ROTATION_180; + policy[0].mSeascapeRotation = ROTATION_270; policy[0].onConfigurationChanged(); }); return policy[0]; diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java index 0ea8d4f5e353..5d739a308e89 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java @@ -32,6 +32,9 @@ import android.support.test.filters.SmallTest; import java.io.PrintWriter; import java.io.StringWriter; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -57,7 +60,7 @@ public class PackageTrackerTest { private ConfigHelper mMockConfigHelper; private PackageManagerHelper mMockPackageManagerHelper; - private FakeClockHelper mFakeClock; + private FakeClock mFakeClock; private FakeIntentHelper mFakeIntentHelper; private PackageStatusStorage mPackageStatusStorage; private PackageTracker mPackageTracker; @@ -66,7 +69,7 @@ public class PackageTrackerTest { public void setUp() throws Exception { Context context = InstrumentationRegistry.getContext(); - mFakeClock = new FakeClockHelper(); + mFakeClock = new FakeClock(); // Read-only interfaces so are easy to mock. mMockConfigHelper = mock(ConfigHelper.class); @@ -1444,18 +1447,33 @@ public class PackageTrackerTest { } } - private static class FakeClockHelper implements ClockHelper { + private static class FakeClock extends Clock { private long currentTime = 1000; @Override - public long currentTimestamp() { + public long millis() { return currentTime; } public void incrementClock(long millis) { currentTime += millis; } + + @Override + public ZoneId getZone() { + throw new UnsupportedOperationException(); + } + + @Override + public Clock withZone(ZoneId zone) { + throw new UnsupportedOperationException(); + } + + @Override + public Instant instant() { + throw new UnsupportedOperationException(); + } } /** diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 87b34ab696ff..86ce90c03ae4 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; +import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; @@ -364,4 +365,29 @@ public class AppStandbyControllerTests { reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); } + + @Test + public void testPredictionTimedout() throws Exception { + AppStandbyController controller = setupController(); + setChargingState(controller, false); + controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_PREDICTED + "CTS", 1 * HOUR_MS); + + // Fast forward 12 hours + mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD; + controller.checkIdleStates(USER_ID); + // Should still be in predicted bucket, since prediction timeout is 1 day since prediction + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller)); + // Fast forward two more hours + mInjector.mElapsedRealtime += 2 * HOUR_MS; + controller.checkIdleStates(USER_ID); + // Should have now applied prediction timeout + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller)); + + // Fast forward RARE bucket + mInjector.mElapsedRealtime += RARE_THRESHOLD; + controller.checkIdleStates(USER_ID); + // Should continue to apply prediction timeout + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller)); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java index f069d49a244b..4dd51ebd2d06 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java @@ -48,6 +48,10 @@ public class DimmerTests extends WindowTestsBase { final SurfaceControl mControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); + TestWindowContainer() { + super(sWm); + } + @Override SurfaceControl getSurfaceControl() { return mControl; @@ -65,6 +69,10 @@ public class DimmerTests extends WindowTestsBase { final SurfaceControl mHostControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class); + MockSurfaceBuildingContainer() { + super(sWm); + } + class MockSurfaceBuilder extends SurfaceControl.Builder { MockSurfaceBuilder(SurfaceSession ss) { super(ss); @@ -78,6 +86,7 @@ public class DimmerTests extends WindowTestsBase { } } + @Override SurfaceControl.Builder makeChildSurface(WindowContainer child) { return new MockSurfaceBuilder(mSession); diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java index 5da322409f83..ab0a2bd86dd8 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java @@ -34,7 +34,7 @@ import static org.junit.Assert.assertTrue; * Test class for {@link StackWindowController}. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.StackWindowControllerTests + * atest FrameworksServicesTests:StackWindowControllerTests */ @SmallTest @Presubmit @@ -61,7 +61,6 @@ public class StackWindowControllerTests extends WindowTestsBase { } @Test - @Ignore("b/65379195") public void testRemoveContainer_deferRemoval() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(mDisplayContent); diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java new file mode 100644 index 000000000000..9ecf51ef2249 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -0,0 +1,182 @@ +/* + * 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 java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +import android.animation.AnimationHandler.AnimationFrameCallbackProvider; +import android.graphics.Matrix; +import android.graphics.Point; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; + +import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.CountDownLatch; + +/** + * Test class for {@link SurfaceAnimationRunner}. + * + * runtest frameworks-services -c com.android.server.wm.SurfaceAnimationRunnerTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SurfaceAnimationRunnerTest extends WindowTestsBase { + + @Mock SurfaceControl mMockSurface; + @Mock Transaction mMockTransaction; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private SurfaceAnimationRunner mSurfaceAnimationRunner; + private CountDownLatch mFinishCallbackLatch; + + @Before + public void setUp() throws Exception { + super.setUp(); + mFinishCallbackLatch = new CountDownLatch(1); + mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, + mMockTransaction); + } + + private void finishedCallback() { + mFinishCallbackLatch.countDown(); + } + + @Test + public void testAnimation() throws Exception { + mSurfaceAnimationRunner + .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, + this::finishedCallback); + + // Ensure that the initial transformation has been applied. + final Matrix m = new Matrix(); + m.setTranslate(-10, 0); + verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); + verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f)); + + mFinishCallbackLatch.await(1, SECONDS); + assertFinishCallbackCalled(); + + m.setTranslate(10, 0); + verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); + + // At least 3 times: After initialization, first frame, last frame. + verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f)); + } + + @Test + public void testCancel_notStarted() throws Exception { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction); + mSurfaceAnimationRunner + .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, + this::finishedCallback); + mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); + waitUntilHandlersIdle(); + assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty()); + assertFinishCallbackNotCalled(); + //verify(mMockSurface).release(); + } + + @Test + public void testCancel_running() throws Exception { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction); + mSurfaceAnimationRunner + .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, + this::finishedCallback); + waitUntilNextFrame(); + assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); + assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + waitUntilHandlersIdle(); + assertFinishCallbackNotCalled(); + //verify(mMockSurface).release(); + } + + private void waitUntilNextFrame() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, + latch::countDown, null /* token */); + latch.await(); + } + + private void assertFinishCallbackCalled() { + assertEquals(0, mFinishCallbackLatch.getCount()); + } + + private void assertFinishCallbackNotCalled() { + assertEquals(1, mFinishCallbackLatch.getCount()); + } + + private AnimationSpec createTranslateAnimation() { + final Animation a = new TranslateAnimation(-10, 10, 0, 0); + a.initialize(0, 0, 0, 0); + a.setDuration(50); + return new WindowAnimationSpec(a, new Point(0, 0)); + } + + /** + * Callback provider that doesn't animate at all. + */ + private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider { + + @Override + public void postFrameCallback(FrameCallback callback) { + } + + @Override + public void postCommitCallback(Runnable runnable) { + } + + @Override + public long getFrameTime() { + return 0; + } + + @Override + public long getFrameDelay() { + return 0; + } + + @Override + public void setFrameDelay(long delay) { + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java new file mode 100644 index 000000000000..9a52042b5821 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -0,0 +1,217 @@ +/* + * 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 org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Builder; +import android.view.SurfaceControl.Transaction; +import android.view.SurfaceSession; + +import com.google.android.collect.Lists; + +import com.android.server.wm.SurfaceAnimator.Animatable; +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +/** + * Test class for {@link SurfaceAnimatorTest}. + * + * runtest frameworks-services -c com.android.server.wm.SurfaceAnimatorTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SurfaceAnimatorTest extends WindowTestsBase { + + @Mock + AnimationAdapter mSpec; + @Mock + AnimationAdapter mSpec2; + @Mock Transaction mTransaction; + + private SurfaceAnimator mSurfaceAnimator; + private SurfaceControl mParent; + private SurfaceControl mSurface; + private boolean mFinishedCallbackCalled; + private SurfaceControl mLeash; + private SurfaceSession mSession = new SurfaceSession(); + + private final Animatable mAnimatable = new Animatable() { + @Override + public Transaction getPendingTransaction() { + return mTransaction; + } + + @Override + public void commitPendingTransaction() { + } + + @Override + public void onLeashCreated(Transaction t, SurfaceControl leash) { + } + + @Override + public void onLeashDestroyed(Transaction t) { + } + + @Override + public Builder makeLeash() { + return new SurfaceControl.Builder(mSession) { + + @Override + public SurfaceControl build() { + mLeash = super.build(); + return mLeash; + } + }.setParent(mParent); + } + + @Override + public SurfaceControl getSurface() { + return mSurface; + } + + @Override + public SurfaceControl getParentSurface() { + return mParent; + } + + @Override + public int getSurfaceWidth() { + return 1; + } + + @Override + public int getSurfaceHeight() { + return 1; + } + }; + + private final Runnable mFinishedCallback = () -> { + mFinishedCallbackCalled = true; + }; + + @Before + public void setUp() throws Exception { + super.setUp(); + MockitoAnnotations.initMocks(this); + mParent = sWm.makeSurfaceBuilder(mSession) + .setName("test surface parent") + .setSize(3000, 3000) + .build(); + mSurface = sWm.makeSurfaceBuilder(mSession) + .setName("test surface") + .setSize(1, 1) + .build(); + mFinishedCallbackCalled = false; + mLeash = null; + mSurfaceAnimator = new SurfaceAnimator(mAnimatable, mFinishedCallback, sWm); + } + + @Test + public void testRunAnimation() throws Exception { + mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + + assertTrue(mSurfaceAnimator.isAnimating()); + assertNotNull(mSurfaceAnimator.getAnimation()); + verify(mTransaction).reparent(eq(mSurface), eq(mLeash.getHandle())); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertFalse(mSurfaceAnimator.isAnimating()); + assertNull(mSurfaceAnimator.getAnimation()); + assertTrue(mFinishedCallbackCalled); + + // TODO: Verify reparenting once we use mPendingTransaction to reparent it back + } + + @Test + public void testOverrideAnimation() throws Exception { + mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */); + + assertFalse(mFinishedCallbackCalled); + + final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + assertTrue(mSurfaceAnimator.isAnimating()); + assertNotNull(mSurfaceAnimator.getAnimation()); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + // First animation was finished, but this shouldn't cancel the second animation + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertTrue(mSurfaceAnimator.isAnimating()); + + // Second animation was finished + verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture()); + callbackCaptor.getValue().onAnimationFinished(mSpec2); + assertFalse(mSurfaceAnimator.isAnimating()); + assertTrue(mFinishedCallbackCalled); + } + + @Test + public void testCancelAnimation() throws Exception { + mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + assertTrue(mSurfaceAnimator.isAnimating()); + mSurfaceAnimator.cancelAnimation(); + assertFalse(mSurfaceAnimator.isAnimating()); + verify(mSpec).onAnimationCancelled(any()); + assertTrue(mFinishedCallbackCalled); + } + + @Test + public void testDelayingAnimationStart() throws Exception { + mSurfaceAnimator.startDelayingAnimationStart(); + mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + verifyZeroInteractions(mSpec); + assertTrue(mSurfaceAnimator.isAnimating()); + mSurfaceAnimator.endDelayingAnimationStart(); + verify(mSpec).startAnimation(any(), any(), any()); + } + + @Test + public void testDelayingAnimationStartAndCancelled() throws Exception { + mSurfaceAnimator.startDelayingAnimationStart(); + mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + mSurfaceAnimator.cancelAnimation(); + verifyZeroInteractions(mSpec); + assertFalse(mSurfaceAnimator.isAnimating()); + assertTrue(mFinishedCallbackCalled); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java new file mode 100644 index 000000000000..89447a912c21 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -0,0 +1,115 @@ +/* + * 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.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.InputChannel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +/** + * Tests for the {@link TaskPositioningController} class. + * + * atest com.android.server.wm.TaskPositioningControllerTests + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +@Presubmit +public class TaskPositioningControllerTests extends WindowTestsBase { + private static final int TIMEOUT_MS = 1000; + private TaskPositioningController mTarget; + private WindowState mWindow; + + @Before + public void setUp() throws Exception { + super.setUp(); + + assertNotNull(sWm.mTaskPositioningController); + mTarget = sWm.mTaskPositioningController; + + when(sWm.mInputManager.transferTouchFocus( + any(InputChannel.class), + any(InputChannel.class))).thenReturn(true); + + mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); + mWindow.mInputChannel = new InputChannel(); + synchronized (sWm.mWindowMap) { + sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); + } + } + + @Test + public void testStartAndFinishPositioning() throws Exception { + synchronized (sWm.mWindowMap) { + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0)); + + synchronized (sWm.mWindowMap) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(sWm.mH.runWithScissors(() -> { + mTarget.finishPositioning(); + }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + @Test + public void testHandleTapOutsideTask() throws Exception { + synchronized (sWm.mWindowMap) { + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + final DisplayContent content = mock(DisplayContent.class); + when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask()); + assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow()); + + mTarget.handleTapOutsideTask(content, 0, 0); + + synchronized (sWm.mWindowMap) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(sWm.mH.runWithScissors(() -> { + mTarget.finishPositioning(); + }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java index 8df7568ebcd9..bab2170ad7bb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java @@ -44,7 +44,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testCreation() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); container.setController(controller); assertEquals(controller, container.getController()); @@ -54,7 +54,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testSetContainer() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); @@ -62,7 +62,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { // Assert we can't change the container to another one once set boolean gotException = false; try { - controller.setContainer(new WindowContainer()); + controller.setContainer(new WindowContainer(sWm)); } catch (IllegalArgumentException e) { gotException = true; } @@ -76,7 +76,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); @@ -88,7 +88,7 @@ public class WindowContainerControllerTests extends WindowTestsBase { @Test public void testOnOverrideConfigurationChanged() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); controller.setContainer(container); assertEquals(controller.mContainer, container); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java index 16b6ca684c78..5cb94678a58c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java @@ -193,7 +193,7 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testRemoveImmediately_WithController() throws Exception { - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); final WindowContainerController controller = new WindowContainerController(null, sWm); container.setController(controller); @@ -208,7 +208,7 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testSetController() throws Exception { final WindowContainerController controller = new WindowContainerController(null, sWm); - final WindowContainer container = new WindowContainer(); + final WindowContainer container = new WindowContainer(sWm); container.setController(controller); assertEquals(controller, container.getController()); @@ -587,6 +587,7 @@ public class WindowContainerTests extends WindowTestsBase { TestWindowContainer(int layer, boolean isAnimating, boolean isVisible, Integer orientation) { + super(sWm); mLayer = layer; mIsAnimating = isAnimating; mIsVisible = isVisible; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 4c5e291784d6..c699a94db279 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -185,6 +185,7 @@ class WindowTestsBase { void waitUntilHandlersIdle() { sWm.mH.runWithScissors(() -> { }, 0); sWm.mAnimationHandler.runWithScissors(() -> { }, 0); + SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); } private WindowToken createWindowToken( diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index e5cbdba7b507..6468763440a5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -16,23 +16,6 @@ package com.android.server.wm; -import java.util.HashMap; -import java.util.LinkedList; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.util.Log; - -import android.platform.test.annotations.Presubmit; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -42,10 +25,23 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.LinkedList; /** * Tests for the {@link WindowLayersController} class. @@ -79,7 +75,7 @@ public class ZOrderingTests extends WindowTestsBase { } int getLayer(SurfaceControl sc) { - return mLayersForControl.get(sc); + return mLayersForControl.getOrDefault(sc, 0); } SurfaceControl getRelativeLayer(SurfaceControl sc) { @@ -125,6 +121,7 @@ public class ZOrderingTests extends WindowTestsBase { // would miss construction of the top-level layers. mTransaction = new LayerRecordingTransaction(); sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(); + sWm.mTransactionFactory = () -> mTransaction; } @After diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java index 4eb42201f072..5ebfd488eb97 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; +import android.support.test.filters.FlakyTest; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -257,6 +258,7 @@ public class ScheduleCalendarTest extends NotificationTestCase { } @Test + @FlakyTest public void testIsInSchedule_inScheduleOvernight() { Calendar cal = new GregorianCalendar(); cal.set(Calendar.HOUR_OF_DAY, 23); @@ -273,6 +275,7 @@ public class ScheduleCalendarTest extends NotificationTestCase { } @Test + @FlakyTest public void testIsInSchedule_inScheduleSingleDay() { Calendar cal = new GregorianCalendar(); cal.set(Calendar.HOUR_OF_DAY, 14); diff --git a/services/usage/Android.bp b/services/usage/Android.bp new file mode 100644 index 000000000000..1064b6ed802f --- /dev/null +++ b/services/usage/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.usage", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/usage/Android.mk b/services/usage/Android.mk deleted file mode 100644 index f1cbe98586bb..000000000000 --- a/services/usage/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.usage - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index 5aef55b99e3c..6ac4b36a6ad7 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -72,14 +72,13 @@ public class AppIdleHistory { private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; // Elapsed timebase time when app was last used private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; + // Elapsed timebase time when the app bucket was last predicted externally + private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; + // The standby bucket for the app private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; + // The reason the app was put in the above bucket private static final String ATTR_BUCKETING_REASON = "bucketReason"; - // State that was last informed to listeners, since boot - private static final int STATE_UNINFORMED = 0; - private static final int STATE_ACTIVE = 1; - private static final int STATE_IDLE = 2; - // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration private long mElapsedDuration; // Total device on duration since device was "born" @@ -92,13 +91,21 @@ public class AppIdleHistory { private boolean mScreenOn; - private static class AppUsageHistory { + static class AppUsageHistory { + // Debug final byte[] recent = new byte[HISTORY_SIZE]; + // Last used time using elapsed timebase long lastUsedElapsedTime; + // Last used time using screen_on timebase long lastUsedScreenTime; + // Last predicted time using elapsed timebase + long lastPredictedTime; + // Standby bucket @UsageStatsManager.StandbyBuckets int currentBucket; + // Reason for setting the standby bucket. TODO: Switch to int. String bucketingReason; + // In-memory only, last bucket for which the listeners were informed int lastInformedBucket; } @@ -269,6 +276,7 @@ public class AppIdleHistory { appUsageHistory = new AppUsageHistory(); appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); + appUsageHistory.lastPredictedTime = getElapsedTime(0); appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; appUsageHistory.lastInformedBucket = -1; @@ -295,6 +303,14 @@ public class AppIdleHistory { } } + public AppUsageHistory getAppUsageHistory(String packageName, int userId, + long elapsedRealtime) { + ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); + AppUsageHistory appUsageHistory = + getPackageHistory(userHistory, packageName, elapsedRealtime, true); + return appUsageHistory; + } + public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime, int bucket, String reason) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); @@ -302,6 +318,9 @@ public class AppIdleHistory { getPackageHistory(userHistory, packageName, elapsedRealtime, true); appUsageHistory.currentBucket = bucket; appUsageHistory.bucketingReason = reason; + if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) { + appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime); + } if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket + ", reason=" + appUsageHistory.bucketingReason); @@ -322,7 +341,7 @@ public class AppIdleHistory { return appUsageHistory != null ? appUsageHistory.bucketingReason : null; } - private long getElapsedTime(long elapsedRealtime) { + public long getElapsedTime(long elapsedRealtime) { return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration); } @@ -431,6 +450,12 @@ public class AppIdleHistory { Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); + String lastPredictedTimeString = parser.getAttributeValue(null, + ATTR_LAST_PREDICTED_TIME); + if (lastPredictedTimeString != null) { + appUsageHistory.lastPredictedTime = + Long.parseLong(lastPredictedTimeString); + } String currentBucketString = parser.getAttributeValue(null, ATTR_CURRENT_BUCKET); appUsageHistory.currentBucket = currentBucketString == null @@ -441,6 +466,7 @@ public class AppIdleHistory { if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT; } + appUsageHistory.lastInformedBucket = -1; userHistory.put(packageName, appUsageHistory); } } @@ -477,6 +503,8 @@ public class AppIdleHistory { Long.toString(history.lastUsedElapsedTime)); xml.attribute(null, ATTR_SCREEN_IDLE, Long.toString(history.lastUsedScreenTime)); + xml.attribute(null, ATTR_LAST_PREDICTED_TIME, + Long.toString(history.lastPredictedTime)); xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); @@ -512,6 +540,8 @@ public class AppIdleHistory { TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); idpw.print(" lastUsedScreenOn="); TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); + idpw.print(" lastPredictedTime="); + TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" bucket=" + appUsageHistory.currentBucket + " reason=" + appUsageHistory.bucketingReason); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 3d20a647c7ec..d8086bb4a92d 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageStatsManager.REASON_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_FORCED; +import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; @@ -118,6 +119,9 @@ public class AppStandbyController { STANDBY_BUCKET_RARE }; + // Expiration time for predicted bucket + private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR; + // To name the lock for stack traces static class Lock {} @@ -388,25 +392,27 @@ public class AppStandbyController { STANDBY_BUCKET_EXEMPTED); } else { synchronized (mAppIdleLock) { - String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName, + AppIdleHistory.AppUsageHistory app = + mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime); // If the bucket was forced by the developer, leave it alone - if (REASON_FORCED.equals(bucketingReason)) { + if (REASON_FORCED.equals(app.bucketingReason)) { continue; } + boolean predictionLate = false; // If the bucket was moved up due to usage, let the timeouts apply. - if (REASON_DEFAULT.equals(bucketingReason) - || REASON_USAGE.equals(bucketingReason) - || REASON_TIMEOUT.equals(bucketingReason)) { - int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, - elapsedRealtime); + if (REASON_DEFAULT.equals(app.bucketingReason) + || REASON_USAGE.equals(app.bucketingReason) + || REASON_TIMEOUT.equals(app.bucketingReason) + || (predictionLate = predictionTimedOut(app, elapsedRealtime))) { + int oldBucket = app.currentBucket; int newBucket = getBucketForLocked(packageName, userId, elapsedRealtime); if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); } - if (oldBucket < newBucket) { + if (oldBucket < newBucket || predictionLate) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, REASON_TIMEOUT); maybeInformListeners(packageName, userId, elapsedRealtime, @@ -424,6 +430,14 @@ public class AppStandbyController { return true; } + private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) { + return app.bucketingReason != null + && app.bucketingReason.startsWith(REASON_PREDICTED) + && app.lastPredictedTime > 0 + && mAppIdleHistory.getElapsedTime(elapsedRealtime) + - app.lastPredictedTime > PREDICTION_TIMEOUT; + } + private void maybeInformListeners(String packageName, int userId, long elapsedRealtime, int bucket) { synchronized (mAppIdleLock) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index e55e073cf063..970546976336 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -503,7 +503,7 @@ class UsageStatsDatabase { mCal.getTimeInMillis()); mCal.setTimeInMillis(currentTimeMillis); - mCal.addDays(-7); + mCal.addDays(-10); pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY], mCal.getTimeInMillis()); @@ -803,4 +803,4 @@ class UsageStatsDatabase { } directory.delete(); } -}
\ No newline at end of file +} diff --git a/services/usb/Android.bp b/services/usb/Android.bp new file mode 100644 index 000000000000..0cd9ac306dfb --- /dev/null +++ b/services/usb/Android.bp @@ -0,0 +1,14 @@ +java_library_static { + name: "services.usb", + srcs: ["java/**/*.java"], + + libs: [ + "services.core", + "android.hidl.manager-V1.0-java", + ], + + static_libs: [ + "android.hardware.usb-V1.0-java", + "android.hardware.usb-V1.1-java", + ], +} diff --git a/services/usb/Android.mk b/services/usb/Android.mk deleted file mode 100644 index 55bfccfcef85..000000000000 --- a/services/usb/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.usb - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core \ -android.hidl.manager-V1.0-java - -LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb-V1.0-java \ -android.hardware.usb-V1.1-java - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp new file mode 100644 index 000000000000..390406f97264 --- /dev/null +++ b/services/voiceinteraction/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.voiceinteraction", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/voiceinteraction/Android.mk b/services/voiceinteraction/Android.mk deleted file mode 100644 index c9e5dd02cbbe..000000000000 --- a/services/voiceinteraction/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := services.voiceinteraction - -LOCAL_SRC_FILES += \ - $(call all-java-files-under,java) - -LOCAL_JAVA_LIBRARIES := services.core - -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 51c805da2cac..cd3fdeefee13 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -66,6 +66,7 @@ public class SoundTriggerService extends SystemService { private SoundTriggerDbHelper mDbHelper; private SoundTriggerHelper mSoundTriggerHelper; private final TreeMap<UUID, SoundModel> mLoadedModels; + private Object mCallbacksLock; private final TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback> mIntentCallbacks; private PowerManager.WakeLock mWakelock; @@ -75,6 +76,7 @@ public class SoundTriggerService extends SystemService { mServiceStub = new SoundTriggerServiceStub(); mLocalSoundTriggerService = new LocalSoundTriggerService(context); mLoadedModels = new TreeMap<UUID, SoundModel>(); + mCallbacksLock = new Object(); mIntentCallbacks = new TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback>(); mLock = new Object(); } @@ -211,7 +213,9 @@ public class SoundTriggerService extends SystemService { // don't know if the other model is loaded. if (oldModel != null && !oldModel.equals(soundModel)) { mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid); - mIntentCallbacks.remove(soundModel.uuid); + synchronized (mCallbacksLock) { + mIntentCallbacks.remove(soundModel.uuid); + } } mLoadedModels.put(soundModel.uuid, soundModel); } @@ -240,7 +244,9 @@ public class SoundTriggerService extends SystemService { // don't know if the other model is loaded. if (oldModel != null && !oldModel.equals(soundModel)) { mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id); - mIntentCallbacks.remove(soundModel.uuid); + synchronized (mCallbacksLock) { + mIntentCallbacks.remove(soundModel.uuid); + } } mLoadedModels.put(soundModel.uuid, soundModel); } @@ -262,8 +268,10 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, soundModelId + " is not loaded"); return STATUS_ERROR; } - LocalSoundTriggerRecognitionStatusCallback callback = mIntentCallbacks.get( - soundModelId.getUuid()); + LocalSoundTriggerRecognitionStatusCallback callback = null; + synchronized (mCallbacksLock) { + callback = mIntentCallbacks.get(soundModelId.getUuid()); + } if (callback != null) { Slog.e(TAG, soundModelId + " is already running"); return STATUS_ERROR; @@ -291,7 +299,9 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, "Failed to start model: " + ret); return ret; } - mIntentCallbacks.put(soundModelId.getUuid(), callback); + synchronized (mCallbacksLock) { + mIntentCallbacks.put(soundModelId.getUuid(), callback); + } } return STATUS_OK; } @@ -310,8 +320,10 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, soundModelId + " is not loaded"); return STATUS_ERROR; } - LocalSoundTriggerRecognitionStatusCallback callback = mIntentCallbacks.get( - soundModelId.getUuid()); + LocalSoundTriggerRecognitionStatusCallback callback = null; + synchronized (mCallbacksLock) { + callback = mIntentCallbacks.get(soundModelId.getUuid()); + } if (callback == null) { Slog.e(TAG, soundModelId + " is not running"); return STATUS_ERROR; @@ -334,7 +346,9 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, "Failed to stop model: " + ret); return ret; } - mIntentCallbacks.remove(soundModelId.getUuid()); + synchronized (mCallbacksLock) { + mIntentCallbacks.remove(soundModelId.getUuid()); + } } return STATUS_OK; } @@ -379,14 +393,14 @@ public class SoundTriggerService extends SystemService { public boolean isRecognitionActive(ParcelUuid parcelUuid) { enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER); if (!isInitialized()) return false; - synchronized (mLock) { + synchronized (mCallbacksLock) { LocalSoundTriggerRecognitionStatusCallback callback = mIntentCallbacks.get(parcelUuid.getUuid()); if (callback == null) { return false; } - return mSoundTriggerHelper.isRecognitionRequested(parcelUuid.getUuid()); } + return mSoundTriggerHelper.isRecognitionRequested(parcelUuid.getUuid()); } } @@ -513,7 +527,7 @@ public class SoundTriggerService extends SystemService { private void removeCallback(boolean releaseWakeLock) { mCallbackIntent = null; - synchronized (mLock) { + synchronized (mCallbacksLock) { mIntentCallbacks.remove(mUuid); if (releaseWakeLock) { mWakelock.release(); @@ -523,7 +537,7 @@ public class SoundTriggerService extends SystemService { } private void grabWakeLock() { - synchronized (mLock) { + synchronized (mCallbacksLock) { if (mWakelock == null) { PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE)); mWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); @@ -537,7 +551,7 @@ public class SoundTriggerService extends SystemService { public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, String resultData, Bundle resultExtras) { // We're only ever invoked when the callback is done, so release the lock. - synchronized (mLock) { + synchronized (mCallbacksLock) { mWakelock.release(); } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e9feb8993471..926c013d5847 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1310,6 +1310,19 @@ public class CarrierConfigManager { */ public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call"; + + /** + * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has + * been remotely held. + * <p> + * When {@code true}, if the IMS stack indicates that the call session has been held, a signal + * will be sent from Telephony to play an audible "on-hold" tone played to the user. + * When {@code false}, a hold tone will only be played if the audio session becomes inactive. + * @hide + */ + public static final String KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL = + "always_play_remote_hold_tone_bool"; + /** * When true, indicates that adding a call is disabled when there is an ongoing video call * or when there is an ongoing call on wifi which was downgraded from video and VoWifi is @@ -1706,6 +1719,7 @@ public class CarrierConfigManager { static { sDefaults = new PersistableBundle(); sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true); + sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false); sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true); sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false); sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true); diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index c3a2ceb1a344..56e1e640373d 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -280,6 +280,36 @@ public class DisconnectCause { * {@hide} */ public static final int NORMAL_UNSPECIFIED = 65; + + /** + * Stk Call Control modified DIAL request to video DIAL request. + * {@hide} + */ + public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; + + /** + * Stk Call Control modified Video DIAL request to SS request. + * {@hide} + */ + public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; + + /** + * Stk Call Control modified Video DIAL request to USSD request. + * {@hide} + */ + public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; + + /** + * Stk Call Control modified Video DIAL request to DIAL request. + * {@hide} + */ + public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; + + /** + * Stk Call Control modified Video DIAL request to Video DIAL request. + * {@hide} + */ + public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; //********************************************************************************************* // When adding a disconnect type: // 1) Update toString() with the newly added disconnect type. @@ -382,6 +412,16 @@ public class DisconnectCause { return "DIAL_MODIFIED_TO_SS"; case DIAL_MODIFIED_TO_DIAL: return "DIAL_MODIFIED_TO_DIAL"; + case DIAL_MODIFIED_TO_DIAL_VIDEO: + return "DIAL_MODIFIED_TO_DIAL_VIDEO"; + case DIAL_VIDEO_MODIFIED_TO_SS: + return "DIAL_VIDEO_MODIFIED_TO_SS"; + case DIAL_VIDEO_MODIFIED_TO_USSD: + return "DIAL_VIDEO_MODIFIED_TO_USSD"; + case DIAL_VIDEO_MODIFIED_TO_DIAL: + return "DIAL_VIDEO_MODIFIED_TO_DIAL"; + case DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO: + return "DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO"; case ERROR_UNSPECIFIED: return "ERROR_UNSPECIFIED"; case OUTGOING_FAILURE: diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index a5d67c60e40b..dd2a6df2420e 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -83,7 +83,10 @@ public class SmsMessage { public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153; /** @hide */ - @StringDef({FORMAT_3GPP, FORMAT_3GPP2}) + @StringDef(prefix = { "FORMAT_" }, value = { + FORMAT_3GPP, + FORMAT_3GPP2 + }) @Retention(RetentionPolicy.SOURCE) public @interface Format {} diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java index d7b6142a5de6..942ea009f684 100644 --- a/telephony/java/android/telephony/Telephony.java +++ b/telephony/java/android/telephony/Telephony.java @@ -3342,6 +3342,12 @@ public final class Telephony { public static final String APN = "apn"; /** + * Prefix of Integrated Circuit Card Identifier. + * <P>Type: TEXT </P> + */ + public static final String ICCID_PREFIX = "iccid_prefix"; + + /** * User facing carrier name. * <P>Type: TEXT </P> */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 99fc9b33eba8..38819ce1d90b 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6867,6 +6867,8 @@ public class TelephonyManager { * @return true if phone is in emergency callback mode * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode() { return getEmergencyCallbackMode(getSubId()); } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 8cdad3f24ee5..da51c86151d2 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -116,7 +116,6 @@ public final class DataCallResponse implements Parcelable { */ public int getSuggestedRetryTime() { return mSuggestedRetryTime; } - /** * @return The unique id of the data connection. */ @@ -183,16 +182,57 @@ public final class DataCallResponse implements Parcelable { .append(" active=").append(mActive) .append(" type=").append(mType) .append(" ifname=").append(mIfname) - .append(" mtu=").append(mMtu) .append(" addresses=").append(mAddresses) .append(" dnses=").append(mDnses) .append(" gateways=").append(mGateways) .append(" pcscf=").append(mPcscfs) + .append(" mtu=").append(mMtu) .append("}"); return sb.toString(); } @Override + public boolean equals (Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof DataCallResponse)) { + return false; + } + + DataCallResponse other = (DataCallResponse) o; + return this.mStatus == other.mStatus + && this.mSuggestedRetryTime == other.mSuggestedRetryTime + && this.mCid == other.mCid + && this.mActive == other.mActive + && this.mType.equals(other.mType) + && this.mIfname.equals(other.mIfname) + && mAddresses.size() == other.mAddresses.size() + && mAddresses.containsAll(other.mAddresses) + && mDnses.size() == other.mDnses.size() + && mDnses.containsAll(other.mDnses) + && mGateways.size() == other.mGateways.size() + && mGateways.containsAll(other.mGateways) + && mPcscfs.size() == other.mPcscfs.size() + && mPcscfs.containsAll(other.mPcscfs) + && mMtu == other.mMtu; + } + + @Override + public int hashCode() { + return mStatus * 31 + + mSuggestedRetryTime * 37 + + mCid * 41 + + mActive * 43 + + mType.hashCode() * 47 + + mIfname.hashCode() * 53 + + mAddresses.hashCode() * 59 + + mDnses.hashCode() * 61 + + mGateways.hashCode() * 67 + + mPcscfs.hashCode() * 71 + + mMtu * 73; + } + + @Override public int describeContents() { return 0; } diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java index 947d0ff4693a..00d212a54c6f 100644 --- a/telephony/java/android/telephony/data/InterfaceAddress.java +++ b/telephony/java/android/telephony/data/InterfaceAddress.java @@ -78,6 +78,23 @@ public final class InterfaceAddress implements Parcelable { */ public int getNetworkPrefixLength() { return mPrefixLength; } + @Override + public boolean equals (Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof InterfaceAddress)) { + return false; + } + + InterfaceAddress other = (InterfaceAddress) o; + return this.mInetAddress.equals(other.mInetAddress) + && this.mPrefixLength == other.mPrefixLength; + } + + @Override + public int hashCode() { + return mInetAddress.hashCode() * 31 + mPrefixLength * 37; + } @Override public int describeContents() { diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 5a4db99ee0d0..1670e6b9ba5c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -16,15 +16,23 @@ package android.telephony.ims.stub; +import android.content.Context; +import android.content.Intent; import android.os.RemoteException; +import android.util.Log; import com.android.ims.ImsConfig; import com.android.ims.ImsConfigListener; import com.android.ims.internal.IImsConfig; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.ref.WeakReference; +import java.util.HashMap; + /** - * Base implementation of ImsConfig, which implements stub versions of the methods - * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports. + * Base implementation of ImsConfig. + * Override the methods that your implementation of ImsConfig supports. * * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you * will break other implementations of ImsConfig maintained by other ImsServices. @@ -34,10 +42,25 @@ import com.android.ims.internal.IImsConfig; * 1) Items provisioned by the operator. * 2) Items configured by user. Mainly service feature class. * + * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface. + * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes. + * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in + * during initialization, or times when a lot of configuration parameters are being set/get + * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed + * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be + * performed every time. * @hide */ -public class ImsConfigImplBase extends IImsConfig.Stub { +public class ImsConfigImplBase { + + static final private String TAG = "ImsConfigImplBase"; + + ImsConfigStub mImsConfigStub; + + public ImsConfigImplBase(Context context) { + mImsConfigStub = new ImsConfigStub(this, context); + } /** * Gets the value for ims service/capabilities parameters from the provisioned @@ -46,7 +69,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. * @return value in Integer format. */ - @Override public int getProvisionedValue(int item) throws RemoteException { return -1; } @@ -58,7 +80,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. * @return value in String format. */ - @Override public String getProvisionedStringValue(int item) throws RemoteException { return null; } @@ -72,7 +93,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param value in Integer format. * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. */ - @Override public int setProvisionedValue(int item, int value) throws RemoteException { return ImsConfig.OperationStatusConstants.FAILED; } @@ -86,7 +106,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param value in String format. * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. */ - @Override public int setProvisionedStringValue(int item, String value) throws RemoteException { return ImsConfig.OperationStatusConstants.FAILED; } @@ -100,7 +119,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX. * @param listener feature value returned asynchronously through listener. */ - @Override public void getFeatureValue(int feature, int network, ImsConfigListener listener) throws RemoteException { } @@ -115,7 +133,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants. * @param listener, provided if caller needs to be notified for set result. */ - @Override public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener) throws RemoteException { } @@ -124,7 +141,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * Gets the value for IMS VoLTE provisioned. * This should be the same as the operator provisioned value if applies. */ - @Override public boolean getVolteProvisioned() throws RemoteException { return false; } @@ -134,7 +150,6 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * * @param listener Video quality value returned asynchronously through listener. */ - @Override public void getVideoQuality(ImsConfigListener listener) throws RemoteException { } @@ -144,7 +159,233 @@ public class ImsConfigImplBase extends IImsConfig.Stub { * @param quality, defines the value of video quality. * @param listener, provided if caller needs to be notified for set result. */ - @Override public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException { } + + public IImsConfig getIImsConfig() { return mImsConfigStub; } + + /** + * Updates provisioning value and notifies the framework of the change. + * Doesn't call #setProvisionedValue and assumes the result succeeded. + * This should only be used by modem when they implicitly changed provisioned values. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + */ + public final void notifyProvisionedValueChanged(int item, int value) { + mImsConfigStub.updateCachedValue(item, value, true); + } + + /** + * Updates provisioning value and notifies the framework of the change. + * Doesn't call #setProvisionedValue and assumes the result succeeded. + * This should only be used by modem when they implicitly changed provisioned values. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + */ + public final void notifyProvisionedValueChanged(int item, String value) { + mImsConfigStub.updateCachedValue(item, value, true); + } + + /** + * Implements the IImsConfig AIDL interface, which is called by potentially many processes + * in order to get/set configuration parameters. + * + * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl + * with actual implementations from vendors. This class caches provisioned values from + * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in, + * it first checks cache layer. If missed, it will call the vendor implementation of + * ImsConfigImplBase API. + * and cache the return value if the set succeeds. + * + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include: + * 1) Items provisioned by the operator. + * 2) Items configured by user. Mainly service feature class. + * + * @hide + */ + @VisibleForTesting + static public class ImsConfigStub extends IImsConfig.Stub { + Context mContext; + WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference; + private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); + private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>(); + + @VisibleForTesting + public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) { + mContext = context; + mImsConfigImplBaseWeakReference = + new WeakReference<ImsConfigImplBase>(imsConfigImplBase); + } + + /** + * Gets the value for ims service/capabilities parameters. It first checks its local cache, + * if missed, it will call ImsConfigImplBase.getProvisionedValue. + * Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in Integer format. + */ + @Override + public synchronized int getProvisionedValue(int item) throws RemoteException { + if (mProvisionedIntValue.containsKey(item)) { + return mProvisionedIntValue.get(item); + } else { + int retVal = getImsConfigImpl().getProvisionedValue(item); + if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) { + updateCachedValue(item, retVal, false); + } + return retVal; + } + } + + /** + * Gets the value for ims service/capabilities parameters. It first checks its local cache, + * if missed, it will call #ImsConfigImplBase.getProvisionedValue. + * Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + */ + @Override + public synchronized String getProvisionedStringValue(int item) throws RemoteException { + if (mProvisionedIntValue.containsKey(item)) { + return mProvisionedStringValue.get(item); + } else { + String retVal = getImsConfigImpl().getProvisionedStringValue(item); + if (retVal != null) { + updateCachedValue(item, retVal, false); + } + return retVal; + } + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived, and write it into local cache. + * Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + @Override + public synchronized int setProvisionedValue(int item, int value) throws RemoteException { + mProvisionedIntValue.remove(item); + int retVal = getImsConfigImpl().setProvisionedValue(item, value); + if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { + updateCachedValue(item, retVal, true); + } else { + Log.d(TAG, "Set provision value of " + item + + " to " + value + " failed with error code " + retVal); + } + + return retVal; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived, and write it into local cache. + * Synchronous blocking call. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + @Override + public synchronized int setProvisionedStringValue(int item, String value) + throws RemoteException { + mProvisionedStringValue.remove(item); + int retVal = getImsConfigImpl().setProvisionedStringValue(item, value); + if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) { + updateCachedValue(item, retVal, true); + } + + return retVal; + } + + /** + * Wrapper function to call ImsConfigImplBase.getFeatureValue. + */ + @Override + public void getFeatureValue(int feature, int network, ImsConfigListener listener) + throws RemoteException { + getImsConfigImpl().getFeatureValue(feature, network, listener); + } + + /** + * Wrapper function to call ImsConfigImplBase.setFeatureValue. + */ + @Override + public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener) + throws RemoteException { + getImsConfigImpl().setFeatureValue(feature, network, value, listener); + } + + /** + * Wrapper function to call ImsConfigImplBase.getVolteProvisioned. + */ + @Override + public boolean getVolteProvisioned() throws RemoteException { + return getImsConfigImpl().getVolteProvisioned(); + } + + /** + * Wrapper function to call ImsConfigImplBase.getVideoQuality. + */ + @Override + public void getVideoQuality(ImsConfigListener listener) throws RemoteException { + getImsConfigImpl().getVideoQuality(listener); + } + + /** + * Wrapper function to call ImsConfigImplBase.setVideoQuality. + */ + @Override + public void setVideoQuality(int quality, ImsConfigListener listener) + throws RemoteException { + getImsConfigImpl().setVideoQuality(quality, listener); + } + + private ImsConfigImplBase getImsConfigImpl() throws RemoteException { + ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); + if (ref == null) { + throw new RemoteException("Fail to get ImsConfigImpl"); + } else { + return ref; + } + } + + private void sendImsConfigChangedIntent(int item, int value) { + sendImsConfigChangedIntent(item, Integer.toString(value)); + } + + private void sendImsConfigChangedIntent(int item, String value) { + Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED); + configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item); + configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value); + if (mContext != null) { + mContext.sendBroadcast(configChangedIntent); + } + } + + protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) { + mProvisionedIntValue.put(item, value); + if (notifyChange) { + sendImsConfigChangedIntent(item, value); + } + } + + protected synchronized void updateCachedValue( + int item, String value, boolean notifyChange) { + mProvisionedStringValue.put(item, value); + if (notifyChange) { + sendImsConfigChangedIntent(item, value); + } + } + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java index b371efb68030..daa74c8f6f88 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java @@ -21,6 +21,7 @@ import android.os.RemoteException; import com.android.ims.ImsCallForwardInfo; import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsSsData; import com.android.ims.ImsSsInfo; import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUtListener; @@ -85,4 +86,10 @@ public class ImsUtListenerImplBase extends IImsUtListener.Stub { public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo) throws RemoteException { } + + /** + * Notifies client when Supplementary Service indication is received + */ + @Override + public void onSupplementaryServiceIndication(ImsSsData ssData) {} } diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index cf4c47bf541f..cd0c4b115c0e 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -619,6 +619,7 @@ public class ImsConfig { Rlog.d(TAG, "setProvisionedValue(): item = " + item + " value = " + value + " ret = " + ret); } + return ret; } @@ -647,6 +648,7 @@ public class ImsConfig { Rlog.d(TAG, "setProvisionedStringValue(): item = " + item + ", value =" + value); } + return ret; } diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java index cdfc1fd85aab..6ad54c1f7c0f 100644 --- a/telephony/java/com/android/ims/ImsReasonInfo.java +++ b/telephony/java/com/android/ims/ImsReasonInfo.java @@ -111,6 +111,16 @@ public class ImsReasonInfo implements Parcelable { // and this capability is not supported by the network. public static final int CODE_IMEI_NOT_ACCEPTED = 243; + //STK CC errors + public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; + public static final int CODE_DIAL_MODIFIED_TO_SS = 245; + public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; + public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; + public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; + public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; + public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; + public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; + /** * STATUSCODE (SIP response code) (IMS -> Telephony) */ @@ -217,6 +227,11 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; public static final int CODE_UT_NETWORK_ERROR = 804; public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; + //STK CC errors + public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; + public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; + public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; + public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; /** * ECBM diff --git a/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java b/telephony/java/com/android/ims/ImsSsData.aidl index 8879ed03e8b4..33f83067a4df 100644 --- a/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java +++ b/telephony/java/com/android/ims/ImsSsData.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (c) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,6 @@ * limitations under the License. */ -package com.android.test.cantsavestate1; +package com.android.ims; -import android.app.Activity; -import android.os.Bundle; - -public class CantSave1Activity extends Activity { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.cant_save_1_activity); - } -} +parcelable ImsSsData; diff --git a/telephony/java/com/android/ims/ImsSsData.java b/telephony/java/com/android/ims/ImsSsData.java new file mode 100644 index 000000000000..7336c133af96 --- /dev/null +++ b/telephony/java/com/android/ims/ImsSsData.java @@ -0,0 +1,189 @@ +/* + * 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.ims; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; + +/** + * Provided STK Call Control Suplementary Service information + * + * {@hide} + */ +public class ImsSsData implements Parcelable { + + //ServiceType + public static final int SS_CFU = 0; + public static final int SS_CF_BUSY = 1; + public static final int SS_CF_NO_REPLY = 2; + public static final int SS_CF_NOT_REACHABLE = 3; + public static final int SS_CF_ALL = 4; + public static final int SS_CF_ALL_CONDITIONAL = 5; + public static final int SS_CFUT = 6; + public static final int SS_CLIP = 7; + public static final int SS_CLIR = 8; + public static final int SS_COLP = 9; + public static final int SS_COLR = 10; + public static final int SS_CNAP = 11; + public static final int SS_WAIT = 12; + public static final int SS_BAOC = 13; + public static final int SS_BAOIC = 14; + public static final int SS_BAOIC_EXC_HOME = 15; + public static final int SS_BAIC = 16; + public static final int SS_BAIC_ROAMING = 17; + public static final int SS_ALL_BARRING = 18; + public static final int SS_OUTGOING_BARRING = 19; + public static final int SS_INCOMING_BARRING = 20; + public static final int SS_INCOMING_BARRING_DN = 21; + public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; + + //SSRequestType + public static final int SS_ACTIVATION = 0; + public static final int SS_DEACTIVATION = 1; + public static final int SS_INTERROGATION = 2; + public static final int SS_REGISTRATION = 3; + public static final int SS_ERASURE = 4; + + //TeleserviceType + public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; + public static final int SS_ALL_TELESEVICES = 1; + public static final int SS_TELEPHONY = 2; + public static final int SS_ALL_DATA_TELESERVICES = 3; + public static final int SS_SMS_SERVICES = 4; + public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; + + // Refer to ServiceType + public int serviceType; + // Refere to SSRequestType + public int requestType; + // Refer to TeleserviceType + public int teleserviceType; + // Service Class + public int serviceClass; + // Error information + public int result; + + public int[] ssInfo; /* Valid for all supplementary services. + This field will be empty for RequestType SS_INTERROGATION + and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN, + SS_INCOMING_BARRING_ANONYMOUS.*/ + + public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services + ServiceType SS_CF_* and RequestType SS_INTERROGATION */ + + public ImsSsInfo[] imsSsInfo; /* Valid only for ServiceType SS_INCOMING_BARRING_DN and + ServiceType SS_INCOMING_BARRING_ANONYMOUS */ + + public ImsSsData() {} + + public ImsSsData(Parcel in) { + readFromParcel(in); + } + + public static final Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() { + @Override + public ImsSsData createFromParcel(Parcel in) { + return new ImsSsData(in); + } + + @Override + public ImsSsData[] newArray(int size) { + return new ImsSsData[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(serviceType); + out.writeInt(requestType); + out.writeInt(teleserviceType); + out.writeInt(serviceClass); + out.writeInt(result); + out.writeIntArray(ssInfo); + out.writeParcelableArray(cfInfo, 0); + } + + private void readFromParcel(Parcel in) { + serviceType = in.readInt(); + requestType = in.readInt(); + teleserviceType = in.readInt(); + serviceClass = in.readInt(); + result = in.readInt(); + ssInfo = in.createIntArray(); + cfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + public boolean isTypeCF() { + return (serviceType == SS_CFU || serviceType == SS_CF_BUSY || + serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE || + serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL); + } + + public boolean isTypeUnConditional() { + return (serviceType == SS_CFU || serviceType == SS_CF_ALL); + } + + public boolean isTypeCW() { + return (serviceType == SS_WAIT); + } + + public boolean isTypeClip() { + return (serviceType == SS_CLIP); + } + + public boolean isTypeColr() { + return (serviceType == SS_COLR); + } + + public boolean isTypeColp() { + return (serviceType == SS_COLP); + } + + public boolean isTypeClir() { + return (serviceType == SS_CLIR); + } + + public boolean isTypeIcb() { + return (serviceType == SS_INCOMING_BARRING_DN || + serviceType == SS_INCOMING_BARRING_ANONYMOUS); + } + + public boolean isTypeBarring() { + return (serviceType == SS_BAOC || serviceType == SS_BAOIC || + serviceType == SS_BAOIC_EXC_HOME || serviceType == SS_BAIC || + serviceType == SS_BAIC_ROAMING || serviceType == SS_ALL_BARRING || + serviceType == SS_OUTGOING_BARRING || serviceType == SS_INCOMING_BARRING); + } + + public boolean isTypeInterrogation() { + return (requestType == SS_INTERROGATION); + } + + public String toString() { + return "[ImsSsData] " + "ServiceType: " + serviceType + + " RequestType: " + requestType + + " TeleserviceType: " + teleserviceType + + " ServiceClass: " + serviceClass + + " Result: " + result; + } +} diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java index 250371f0f5c2..14c184a64061 100644 --- a/telephony/java/com/android/ims/ImsUtInterface.java +++ b/telephony/java/com/android/ims/ImsUtInterface.java @@ -16,6 +16,7 @@ package com.android.ims; +import android.os.Handler; import android.os.Message; /** @@ -188,4 +189,18 @@ public interface ImsUtInterface { * Updates the configuration of the COLP supplementary service. */ public void updateCOLP(boolean enable, Message result); + + /** + * Register for UNSOL_ON_SS indications. + * @param handler the {@link Handler} that is notified when there is an ss indication. + * @param event Supplimentary service indication event. + * @param Object user object. + */ + public void registerForSuppServiceIndication(Handler handler, int event, Object object); + + /** + * Deregister for UNSOL_ON_SS indications. + * @param handler the {@link Handler} that is notified when there is an ss indication. + */ + public void unregisterForSuppServiceIndication(Handler handler); } diff --git a/telephony/java/com/android/ims/internal/IImsUtListener.aidl b/telephony/java/com/android/ims/internal/IImsUtListener.aidl index 300273a7bd38..1bc036979356 100644 --- a/telephony/java/com/android/ims/internal/IImsUtListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsUtListener.aidl @@ -19,6 +19,7 @@ package com.android.ims.internal; import android.os.Bundle; import com.android.ims.ImsCallForwardInfo; +import com.android.ims.ImsSsData; import com.android.ims.ImsSsInfo; import com.android.ims.internal.IImsUt; import com.android.ims.ImsReasonInfo; @@ -56,4 +57,11 @@ oneway interface IImsUtListener { */ void utConfigurationCallWaitingQueried(in IImsUt ut, int id, in ImsSsInfo[] cwInfo); + + /** + * Notifies client when Supplementary Service indication is received + * + * @param ssData Details of SS request and response information + */ + void onSupplementaryServiceIndication(in ImsSsData ssData); } diff --git a/test-base/Android.bp b/test-base/Android.bp new file mode 100644 index 000000000000..a3fd3452aaef --- /dev/null +++ b/test-base/Android.bp @@ -0,0 +1,88 @@ +// +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Build the android.test.base library +// =================================== +// This contains the junit.framework and android.test classes that were in +// Android API level 25 excluding those from android.test.runner. +// Also contains the com.android.internal.util.Predicate[s] classes. +java_library { + name: "android.test.base", + + srcs: ["src/**/*.java"], + + no_framework_libs: true, + libs: [ + "framework", + ], + +} + +// Build the legacy-test library +// ============================= +// This contains the junit.framework and android.test classes that were in +// Android API level 25 excluding those from android.test.runner. +// Also contains the com.android.internal.util.Predicate[s] classes. +java_library { + name: "legacy-test", + static_libs: ["android.test.base"], + + no_framework_libs: true, + libs: [ + "framework", + ], +} + +// Build the repackaged.android.test.base library +// ============================================== +// This contains repackaged versions of the classes from legacy-test. +java_library_static { + name: "repackaged.android.test.base", + + static_libs: ["android.test.base"], + + no_framework_libs: true, + libs: [ + "framework", + ], + + jarjar_rules: "jarjar-rules.txt", +} + +// Build the legacy-android-test library +// ===================================== +// This contains the android.test classes that were in Android API level 25, +// including those from android.test.runner. +// Also contains the com.android.internal.util.Predicate[s] classes. +java_library_static { + name: "legacy-android-test", + + srcs: [ + "src/android/**/*.java", + "src/com/**/*.java", + ], + + static_libs: [ + "android.test.runner", + "android.test.mock", + ], + + no_framework_libs: true, + libs: [ + "framework", + "junit", + ], +} diff --git a/test-base/Android.mk b/test-base/Android.mk index 03bdcf237873..5e5d0401dd3c 100644 --- a/test-base/Android.mk +++ b/test-base/Android.mk @@ -16,50 +16,6 @@ LOCAL_PATH:= $(call my-dir) -# Build the android.test.base library -# =================================== -# This contains the junit.framework and android.test classes that were in -# Android API level 25 excluding those from android.test.runner. -# Also contains the com.android.internal.util.Predicate[s] classes. -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_MODULE := android.test.base -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework - -include $(BUILD_JAVA_LIBRARY) - -# Build the legacy-test library -# ============================= -# This contains the junit.framework and android.test classes that were in -# Android API level 25 excluding those from android.test.runner. -# Also contains the com.android.internal.util.Predicate[s] classes. -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_MODULE := legacy-test -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework - -include $(BUILD_JAVA_LIBRARY) - -# Build the repackaged.android.test.base library -# ============================================== -# This contains repackaged versions of the classes from legacy-test. -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_MODULE := repackaged.android.test.base -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt - -include $(BUILD_STATIC_JAVA_LIBRARY) - # For unbundled build we'll use the prebuilt jar from prebuilts/sdk. ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) @@ -156,24 +112,6 @@ update-android-test-base-api: $(ANDROID_TEST_BASE_OUTPUT_API_FILE) | $(ACP) endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true -# Build the legacy-android-test library -# ===================================== -# This contains the android.test classes that were in Android API level 25, -# including those from android.test.runner. -# Also contains the com.android.internal.util.Predicate[s] classes. -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src/android) \ - $(call all-java-files-under, ../test-runner/src/android) \ - $(call all-java-files-under, ../test-mock/src/android) \ - $(call all-java-files-under, src/com) -LOCAL_MODULE := legacy-android-test -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit - -include $(BUILD_STATIC_JAVA_LIBRARY) - # Build the legacy.test.stubs library # =================================== include $(CLEAR_VARS) diff --git a/test-mock/Android.bp b/test-mock/Android.bp new file mode 100644 index 000000000000..8eddec48611b --- /dev/null +++ b/test-mock/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2008 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. +// + +// Build the android.test.mock library +// =================================== +java_library { + name: "android.test.mock", + + srcs: ["src/**/*.java"], + + no_framework_libs: true, + libs: [ + "framework", + "legacy-test", + ], +} + +// Build the repackaged.android.test.mock library +// ============================================== +java_library_static { + name: "repackaged.android.test.mock", + + static_libs: ["android.test.mock"], + + jarjar_rules: "jarjar-rules.txt", +} diff --git a/test-mock/Android.mk b/test-mock/Android.mk index 2c079555877b..a761a070fa26 100644 --- a/test-mock/Android.mk +++ b/test-mock/Android.mk @@ -18,32 +18,6 @@ LOCAL_PATH:= $(call my-dir) android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock) -# Build the repackaged.android.test.mock library -# ============================================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework - -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt - -LOCAL_MODULE:= repackaged.android.test.mock - -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Build the android.test.mock library -# =================================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework - -LOCAL_MODULE:= android.test.mock - -include $(BUILD_JAVA_LIBRARY) - # For unbundled build we'll use the prebuilt jar from prebuilts/sdk. ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt new file mode 120000 index 000000000000..f6f79139d511 --- /dev/null +++ b/test-mock/jarjar-rules.txt @@ -0,0 +1 @@ +../test-base/jarjar-rules.txt
\ No newline at end of file diff --git a/test-runner/Android.bp b/test-runner/Android.bp new file mode 100644 index 000000000000..104ae8236368 --- /dev/null +++ b/test-runner/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2008 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. +// + +// Build the android.test.runner library +// ===================================== +java_library { + name: "android.test.runner", + + srcs: ["src/**/*.java"], + + no_framework_libs: true, + libs: [ + "framework", + "legacy-test", + "android.test.mock", + ], +} + +// Build the repackaged.android.test.runner library +// ================================================ +java_library_static { + name: "repackaged.android.test.runner", + + static_libs: ["android.test.runner"], + + jarjar_rules: "jarjar-rules.txt", +} diff --git a/test-runner/Android.mk b/test-runner/Android.mk index 87fe83127e2c..67f1354d52bc 100644 --- a/test-runner/Android.mk +++ b/test-runner/Android.mk @@ -16,42 +16,6 @@ LOCAL_PATH:= $(call my-dir) -# Build the android.test.runner library -# ===================================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := \ - core-oj \ - core-libart \ - framework \ - android.test.base \ - android.test.mock \ - -LOCAL_MODULE:= android.test.runner - -include $(BUILD_JAVA_LIBRARY) - -# Build the repackaged.android.test.runner library -# ================================================ -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := \ - core-oj \ - core-libart \ - framework \ - android.test.base \ - android.test.mock \ - -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt - -LOCAL_MODULE:= repackaged.android.test.runner - -include $(BUILD_STATIC_JAVA_LIBRARY) - # For unbundled build we'll use the prebuilt jar from prebuilts/sdk. ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) diff --git a/test-runner/jarjar-rules.txt b/test-runner/jarjar-rules.txt new file mode 120000 index 000000000000..f6f79139d511 --- /dev/null +++ b/test-runner/jarjar-rules.txt @@ -0,0 +1 @@ +../test-base/jarjar-rules.txt
\ No newline at end of file diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk index 59bc729a6280..57655751cfd8 100644 --- a/tests/BrowserPowerTest/Android.mk +++ b/tests/BrowserPowerTest/Android.mk @@ -18,8 +18,8 @@ include $(CLEAR_VARS) # We only want this apk build for tests. LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/CantSaveState1/Android.mk b/tests/CantSaveState1/Android.mk deleted file mode 100644 index 6e9db6e867e5..000000000000 --- a/tests/CantSaveState1/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := CantSaveState1 - -include $(BUILD_PACKAGE) diff --git a/tests/CantSaveState1/AndroidManifest.xml b/tests/CantSaveState1/AndroidManifest.xml deleted file mode 100644 index fadcaebdeddb..000000000000 --- a/tests/CantSaveState1/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.cantsavestate1"> - <application android:label="Can't Save 1" android:cantSaveState="true"> - <activity android:name="CantSave1Activity"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/CantSaveState1/res/layout/cant_save_1_activity.xml b/tests/CantSaveState1/res/layout/cant_save_1_activity.xml deleted file mode 100644 index c5bf657c7fb1..000000000000 --- a/tests/CantSaveState1/res/layout/cant_save_1_activity.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" -> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="25dp" - android:textAppearance="?android:attr/textAppearanceLarge" - android:text="This app #1 can't save its state" - /> - -</LinearLayout> diff --git a/tests/CantSaveState2/Android.mk b/tests/CantSaveState2/Android.mk deleted file mode 100644 index add9214e38c7..000000000000 --- a/tests/CantSaveState2/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := CantSaveState2 - -include $(BUILD_PACKAGE) diff --git a/tests/CantSaveState2/AndroidManifest.xml b/tests/CantSaveState2/AndroidManifest.xml deleted file mode 100644 index 8f4f01d19fc7..000000000000 --- a/tests/CantSaveState2/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.cantsavestate2"> - <application android:label="Can't Save 2" android:cantSaveState="true"> - <activity android:name="CantSave2Activity"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/CantSaveState2/res/layout/cant_save_2_activity.xml b/tests/CantSaveState2/res/layout/cant_save_2_activity.xml deleted file mode 100644 index c5b8e3d8bcd3..000000000000 --- a/tests/CantSaveState2/res/layout/cant_save_2_activity.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" -> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="25dp" - android:textAppearance="?android:attr/textAppearanceLarge" - android:text="This app #2 can't save its state" - /> - -</LinearLayout> diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk index 90de503ebd81..b071ec4c494b 100644 --- a/tests/CanvasCompare/Android.mk +++ b/tests/CanvasCompare/Android.mk @@ -23,7 +23,7 @@ LOCAL_PACKAGE_NAME := CanvasCompare LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/CoreTests/android/Android.mk b/tests/CoreTests/android/Android.mk index c9f11615381f..56d7918aa01e 100644 --- a/tests/CoreTests/android/Android.mk +++ b/tests/CoreTests/android/Android.mk @@ -6,8 +6,14 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt org.apache.http.legacy -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + bouncycastle \ + conscrypt \ + org.apache.http.legacy \ + android.test.base \ + +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := CoreTests diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk index 4e15729221a9..85f7edf7438d 100644 --- a/tests/DataIdleTest/Android.mk +++ b/tests/DataIdleTest/Android.mk @@ -20,8 +20,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := DataIdleTest -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SRC_FILES := $(call all-java-files-under, src) # We need to sign it to get access to the network usage history. diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk index d2ec75347531..1873cc1de8a9 100644 --- a/tests/FrameworkPerf/Android.mk +++ b/tests/FrameworkPerf/Android.mk @@ -7,8 +7,8 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := FrameworkPerf -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk index f8c865631f93..8550d703678c 100644 --- a/tests/HierarchyViewerTest/Android.mk +++ b/tests/HierarchyViewerTest/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := HierarchyViewerTest -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk index 60424712d02c..a0df959cf185 100644 --- a/tests/ImfTest/tests/Android.mk +++ b/tests/ImfTest/tests/Android.mk @@ -7,8 +7,8 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := ImfTestTests diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk index fc001e928e80..73181ec36e17 100644 --- a/tests/Internal/Android.mk +++ b/tests/Internal/Android.mk @@ -11,7 +11,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := junit \ - legacy-android-test \ android-support-test \ mockito-target-minus-junit4 diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk index 578e628987f1..e186e9fb03c9 100644 --- a/tests/MemoryUsage/Android.mk +++ b/tests/MemoryUsage/Android.mk @@ -9,8 +9,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := MemoryUsage LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk index dd9ff11971eb..6fb60259a2fc 100644 --- a/tests/NetworkSecurityConfigTest/Android.mk +++ b/tests/NetworkSecurityConfigTest/Android.mk @@ -5,8 +5,13 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + bouncycastle \ + conscrypt \ + android.test.base \ + +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk index 359484ee63d7..030d5f4738dc 100644 --- a/tests/SoundTriggerTests/Android.mk +++ b/tests/SoundTriggerTests/Android.mk @@ -27,8 +27,8 @@ else LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java endif -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := mockito-target +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := SoundTriggerTests diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk index d97c3f4cd66f..1962791c8392 100644 --- a/tests/SurfaceComposition/Android.mk +++ b/tests/SurfaceComposition/Android.mk @@ -27,7 +27,9 @@ LOCAL_PROGUARD_ENABLED := disabled LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit + +LOCAL_JAVA_LIBRARIES := android.test.runner.stubs LOCAL_PACKAGE_NAME := SurfaceComposition diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk index 3c3cd77813f7..2fa19508c32c 100644 --- a/tests/TtsTests/Android.mk +++ b/tests/TtsTests/Android.mk @@ -20,8 +20,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := mockito-target +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := TtsTests diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk index f356afb799a2..8aac8a15c705 100644 --- a/tests/WindowAnimationJank/Android.mk +++ b/tests/WindowAnimationJank/Android.mk @@ -27,7 +27,6 @@ LOCAL_PACKAGE_NAME := WindowAnimationJank LOCAL_STATIC_JAVA_LIBRARIES := \ ub-uiautomator \ ub-janktesthelper \ - legacy-android-test \ junit LOCAL_SDK_VERSION := current diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java index f849689abb23..54776dbd3c52 100644 --- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java +++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java @@ -18,10 +18,12 @@ package android.net.ip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.when; import android.net.util.SharedLog; +import android.os.Handler; +import android.os.Looper; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -42,14 +44,18 @@ public class IpReachabilityMonitorTest { @Mock IpReachabilityMonitor.Callback mCallback; @Mock IpReachabilityMonitor.Dependencies mDependencies; @Mock SharedLog mLog; + Handler mHandler; @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mLog.forSubComponent(anyString())).thenReturn(mLog); + mHandler = new Handler(Looper.getMainLooper()); } IpReachabilityMonitor makeMonitor() { - return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies); + return new IpReachabilityMonitor( + "fake0", 1, mHandler, mLog, mCallback, null, mDependencies); } @Test diff --git a/tests/net/java/android/net/netlink/NetlinkSocketTest.java b/tests/net/java/android/net/netlink/NetlinkSocketTest.java index bd36bac8d5e2..11be40b4ce33 100644 --- a/tests/net/java/android/net/netlink/NetlinkSocketTest.java +++ b/tests/net/java/android/net/netlink/NetlinkSocketTest.java @@ -16,6 +16,8 @@ package android.net.netlink; +import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; +import static android.system.OsConstants.NETLINK_ROUTE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -28,10 +30,12 @@ import android.support.test.runner.AndroidJUnit4; import android.support.test.filters.SmallTest; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; -import android.system.OsConstants; +import android.system.Os; import android.util.Log; +import libcore.io.IoUtils; import java.io.InterruptedIOException; +import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,29 +50,28 @@ public class NetlinkSocketTest { @Test public void testBasicWorkingGetNeighborsQuery() throws Exception { - NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - assertNotNull(s); + final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE); + assertNotNull(fd); - s.connectToKernel(); + NetlinkSocket.connectToKernel(fd); - NetlinkSocketAddress localAddr = s.getLocalAddress(); + final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd); assertNotNull(localAddr); assertEquals(0, localAddr.getGroupsMask()); assertTrue(0 != localAddr.getPortId()); final int TEST_SEQNO = 5; - final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO); - assertNotNull(request); + final byte[] req = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO); + assertNotNull(req); final long TIMEOUT = 500; - assertTrue(s.sendMessage(request, 0, request.length, TIMEOUT)); + assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT)); int neighMessageCount = 0; int doneMessageCount = 0; while (doneMessageCount == 0) { - ByteBuffer response = null; - response = s.recvMessage(TIMEOUT); + ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT); assertNotNull(response); assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit()); assertEquals(0, response.position()); @@ -100,30 +103,6 @@ public class NetlinkSocketTest { // TODO: make sure this test passes sanely in airplane mode. assertTrue(neighMessageCount > 0); - s.close(); - } - - @Test - public void testRepeatedCloseCallsAreQuiet() throws Exception { - // Create a working NetlinkSocket. - NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE); - assertNotNull(s); - s.connectToKernel(); - NetlinkSocketAddress localAddr = s.getLocalAddress(); - assertNotNull(localAddr); - assertEquals(0, localAddr.getGroupsMask()); - assertTrue(0 != localAddr.getPortId()); - // Close once. - s.close(); - // Test that it is closed. - boolean expectedErrorSeen = false; - try { - localAddr = s.getLocalAddress(); - } catch (ErrnoException e) { - expectedErrorSeen = true; - } - assertTrue(expectedErrorSeen); - // Close once more. - s.close(); + IoUtils.closeQuietly(fd); } } diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/PacketReaderTest.java index 29dfa4c3d1e1..dced7435ee74 100644 --- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java +++ b/tests/net/java/android/net/util/PacketReaderTest.java @@ -16,7 +16,7 @@ package android.net.util; -import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE; +import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE; import static android.system.OsConstants.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -53,13 +53,13 @@ import org.junit.Test; import libcore.io.IoBridge; /** - * Tests for BlockingSocketReader. + * Tests for PacketReader. * * @hide */ @RunWith(AndroidJUnit4.class) @SmallTest -public class BlockingSocketReaderTest { +public class PacketReaderTest { static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress(); static final StructTimeval TIMEO = StructTimeval.fromMillis(500); @@ -69,9 +69,9 @@ public class BlockingSocketReaderTest { protected byte[] mLastRecvBuf; protected boolean mStopped; protected HandlerThread mHandlerThread; - protected BlockingSocketReader mReceiver; + protected PacketReader mReceiver; - class UdpLoopbackReader extends BlockingSocketReader { + class UdpLoopbackReader extends PacketReader { public UdpLoopbackReader(Handler h) { super(h); } @@ -121,7 +121,7 @@ public class BlockingSocketReaderTest { mLastRecvBuf = null; mStopped = false; - mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName()); + mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName()); mHandlerThread.start(); } @@ -188,8 +188,8 @@ public class BlockingSocketReaderTest { mReceiver = null; } - class NullBlockingSocketReader extends BlockingSocketReader { - public NullBlockingSocketReader(Handler h, int recvbufsize) { + class NullPacketReader extends PacketReader { + public NullPacketReader(Handler h, int recvbufsize) { super(h, recvbufsize); } @@ -202,7 +202,7 @@ public class BlockingSocketReaderTest { final Handler h = mHandlerThread.getThreadHandler(); for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) { - final BlockingSocketReader b = new NullBlockingSocketReader(h, i); + final PacketReader b = new NullPacketReader(h, i); assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize()); } } diff --git a/tests/permission/Android.mk b/tests/permission/Android.mk index 54688c891046..7f81d9a3a894 100644 --- a/tests/permission/Android.mk +++ b/tests/permission/Android.mk @@ -7,8 +7,8 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := FrameworkPermissionTests include $(BUILD_PACKAGE) diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java index 498d517c104b..ffe721993a37 100644 --- a/tests/testables/src/android/testing/TestableContext.java +++ b/tests/testables/src/android/testing/TestableContext.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; @@ -69,6 +70,7 @@ public class TestableContext extends ContextWrapper implements TestRule { private LeakCheck.Tracker mService; private LeakCheck.Tracker mComponent; private TestableResources mTestableResources; + private TestablePermissions mTestablePermissions; public TestableContext(Context base) { this(base, null); @@ -302,6 +304,159 @@ public class TestableContext extends ContextWrapper implements TestRule { super.unregisterComponentCallbacks(callback); } + public TestablePermissions getTestablePermissions() { + if (mTestablePermissions == null) { + mTestablePermissions = new TestablePermissions(); + } + return mTestablePermissions; + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingOrSelfPermission(permission); + } + + @Override + public int checkCallingPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingPermission(permission); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid); + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid, callerToken); + } + + @Override + public int checkSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkSelfPermission(permission); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingOrSelfPermission(permission, message); + } + } + + @Override + public void enforceCallingPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingPermission(permission, message); + } + } + + @Override + public void enforcePermission(String permission, int pid, int uid, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforcePermission(permission, pid, uid, message); + } + } + + @Override + public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingOrSelfUriPermission(uri, modeFlags); + } + + @Override + public int checkCallingUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingUriPermission(uri, modeFlags); + } + + @Override + public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingOrSelfUriPermission(uri, modeFlags, message); + } + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags); + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags, callerToken); + } + + @Override + public int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, + int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); + } + + @Override + public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingUriPermission(uri, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, pid, uid, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, String readPermission, String writePermission, + int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags, + message); + } + } + @Override public Statement apply(Statement base, Description description) { return new TestWatcher() { diff --git a/tests/testables/src/android/testing/TestablePermissions.java b/tests/testables/src/android/testing/TestablePermissions.java new file mode 100644 index 000000000000..4f009e406ca7 --- /dev/null +++ b/tests/testables/src/android/testing/TestablePermissions.java @@ -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. + */ + +package android.testing; + +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.ArrayMap; + +/** + * Simple class for simulating basic permission states for tests. + * + * All enforce* and check* calls on TestableContext are considered the same + * and routed through the same check here. If more fine-grained control is + * required, then either a sub-class or spy on TestableContext is recommended. + */ +public class TestablePermissions { + + private final ArrayMap<String, Integer> mPermissions = new ArrayMap<>(); + private final ArrayMap<Uri, Integer> mUris = new ArrayMap<>(); + + /** + * Sets the return value for checkPermission* calls on TestableContext + * for a specific permission value. For all enforcePermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(String permission, int value) { + mPermissions.put(permission, value); + } + + /** + * Sets the return value for checkUriPermission* calls on TestableContext + * for a specific permission value. For all enforceUriPermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(Uri uri, int value) { + // TODO: Support modeFlags + mUris.put(uri, value); + } + + boolean wantsCall(String permission) { + return mPermissions.containsKey(permission); + } + + boolean wantsCall(Uri uri) { + return mUris.containsKey(uri); + } + + int check(String permission) { + return mPermissions.get(permission); + } + + int check(Uri uri, int modeFlags) { + // TODO: Support modeFlags + return mUris.get(uri); + } + + public void enforce(String permission) { + if (check(permission) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } + + public void enforce(Uri uri, int modeFlags) { + if (check(uri, modeFlags) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } +} diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk index 16fe5351c161..6e20d797fb5d 100644 --- a/tests/testables/tests/Android.mk +++ b/tests/testables/tests/Android.mk @@ -28,10 +28,9 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ mockito-target-minus-junit4 \ - legacy-android-test \ testables -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_CERTIFICATE := platform diff --git a/tests/testables/tests/src/android/testing/TestablePermissionsTest.java b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java new file mode 100644 index 000000000000..c56146e19a40 --- /dev/null +++ b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java @@ -0,0 +1,90 @@ +/* + * 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.testing; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static org.junit.Assert.assertEquals; + +import android.Manifest.permission; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.testing.TestableLooper.RunWithLooper; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TestablePermissionsTest { + + private static final Uri URI_1 = Uri.parse("content://my.authority/path1"); + private static final Uri URI_2 = Uri.parse("content://my.authority/path2"); + + @Rule + public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext()); + + @Test + public void testCheck() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS_FULL, + PERMISSION_DENIED); + assertEquals(PERMISSION_GRANTED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS, 0, 0)); + assertEquals(PERMISSION_DENIED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS_FULL, 0, 0)); + } + + @Test + public void testCheckUri() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(URI_2, PERMISSION_DENIED); + + assertEquals(PERMISSION_GRANTED, mContext.checkUriPermission(URI_1, 0, 0, 0)); + assertEquals(PERMISSION_DENIED, mContext.checkUriPermission(URI_2, 0, 0, 0)); + } + + @Test + public void testEnforceNoException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceWithException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_DENIED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test + public void testEnforceUriNoException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceUriWithException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_DENIED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + +}
\ No newline at end of file diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index 33b5a8b2686d..20a9f417228c 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -57,31 +57,30 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, I std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { - io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); - if (table_file == nullptr) { - diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath); - return {}; - } + std::unique_ptr<ResourceTable> table; - std::unique_ptr<io::InputStream> in = table_file->OpenInputStream(); - if (in == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); - return {}; - } + io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); + if (table_file != nullptr) { + pb::ResourceTable pb_table; + std::unique_ptr<io::InputStream> in = table_file->OpenInputStream(); + if (in == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); + return {}; + } - pb::ResourceTable pb_table; - io::ZeroCopyInputAdaptor adaptor(in.get()); - if (!pb_table.ParseFromZeroCopyStream(&adaptor)) { - diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); - return {}; - } + io::ZeroCopyInputAdaptor adaptor(in.get()); + if (!pb_table.ParseFromZeroCopyStream(&adaptor)) { + diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); + return {}; + } - std::string error; - std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); - if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { - diag->Error(DiagMessage(source) - << "failed to deserialize " << kProtoResourceTablePath << ": " << error); - return {}; + std::string error; + table = util::make_unique<ResourceTable>(); + if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { + diag->Error(DiagMessage(source) + << "failed to deserialize " << kProtoResourceTablePath << ": " << error); + return {}; + } } io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); @@ -103,6 +102,7 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection( return {}; } + std::string error; std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error); if (manifest == nullptr) { diag->Error(DiagMessage(source) @@ -115,24 +115,21 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection( std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection( const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) { - io::IFile* table_file = collection->FindFile(kApkResourceTablePath); - if (table_file == nullptr) { - diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath); - - return {}; - } + std::unique_ptr<ResourceTable> table; - std::unique_ptr<io::IData> data = table_file->OpenAsData(); - if (data == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); - return {}; - } - - std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); - BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), - collection.get()); - if (!parser.Parse()) { - return {}; + io::IFile* table_file = collection->FindFile(kApkResourceTablePath); + if (table_file != nullptr) { + table = util::make_unique<ResourceTable>(); + std::unique_ptr<io::IData> data = table_file->OpenAsData(); + if (data == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); + return {}; + } + BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), + collection.get()); + if (!parser.Parse()) { + return {}; + } } io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 2bd2405612e3..964dacfeafef 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -64,36 +64,38 @@ bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer return false; } - // Resource table - if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) { - context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "failed to serialize the resource table"); - return false; - } + if (apk->GetResourceTable() != nullptr) { + // Resource table + if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to serialize the resource table"); + return false; + } - // Resources - for (const auto& package : apk->GetResourceTable()->packages) { - for (const auto& type : package->types) { - for (const auto& entry : type->entries) { - for (const auto& config_value : entry->values) { - const FileReference* file = ValueCast<FileReference>(config_value->value.get()); - if (file != nullptr) { - if (file->file == nullptr) { - context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "no file associated with " << *file); - return false; - } - - if (!serializer->SerializeFile(file, writer)) { - context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "failed to serialize file " << *file->path); - return false; - } - } // file - } // config_value - } // entry - } // type - } // package + // Resources + for (const auto& package : apk->GetResourceTable()->packages) { + for (const auto& type : package->types) { + for (const auto& entry : type->entries) { + for (const auto& config_value : entry->values) { + const FileReference* file = ValueCast<FileReference>(config_value->value.get()); + if (file != nullptr) { + if (file->file == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "no file associated with " << *file); + return false; + } + + if (!serializer->SerializeFile(file, writer)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to serialize file " << *file->path); + return false; + } + } // file + } // config_value + } // entry + } // type + } // package + } // Other files std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator(); diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp index 6dd6059dcc4f..5f81a2eeb130 100644 --- a/tools/locked_region_code_injection/Android.bp +++ b/tools/locked_region_code_injection/Android.bp @@ -1,4 +1,4 @@ -java_library_host { +java_binary_host { name: "lockedregioncodeinjection", manifest: "manifest.txt", srcs: ["src/**/*.java"], diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java index 735e872eabdc..b4c690f4840d 100644 --- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java +++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java @@ -41,7 +41,7 @@ import java.util.List; * * @hide RTT_API */ -@SystemService(Context.WIFI_RTT2_SERVICE) +@SystemService(Context.WIFI_RTT_RANGING_SERVICE) public class WifiRttManager { private static final String TAG = "WifiRttManager"; private static final boolean VDBG = false; diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html index 221b94b77b94..a0d407a927a2 100644 --- a/wifi/java/android/net/wifi/rtt/package.html +++ b/wifi/java/android/net/wifi/rtt/package.html @@ -5,7 +5,7 @@ <p>The primary entry point to Wi-Fi RTT capabilities is the {@link android.net.wifi.rtt.WifiRttManager} class, which is acquired by calling {@link android.content.Context#getSystemService(String) - Context.getSystemService(Context.WIFI_RTT_SERVICE)}</p> + Context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE)}</p> <p>Some APIs may require the following user permissions:</p> <ul> |