diff --git a/Android.mk b/Android.mk index 7fbaefd57c144cf7be8c4063cdd527f890caae1e..d0ec016440b90b3f502d5196540022c4491cdb82 100644 --- a/Android.mk +++ b/Android.mk @@ -75,6 +75,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IAppTask.aidl \ core/java/android/app/ITaskStackListener.aidl \ core/java/android/app/IBackupAgent.aidl \ + core/java/android/app/IEphemeralResolver.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/IProcessObserver.aidl \ @@ -306,9 +307,9 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/app/IAppOpsService.aidl \ core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ - core/java/com/android/internal/app/IEphemeralResolver.aidl \ core/java/com/android/internal/app/ISoundTriggerService.aidl \ core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \ + core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl \ core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \ core/java/com/android/internal/app/IVoiceInteractor.aidl \ core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \ @@ -319,6 +320,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \ core/java/com/android/internal/backup/IBackupTransport.aidl \ core/java/com/android/internal/backup/IObbBackupService.aidl \ + core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \ core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \ core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \ core/java/com/android/internal/policy/IKeyguardService.aidl \ @@ -343,6 +345,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodManager.aidl \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/view/IInputSessionCallback.aidl \ + core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \ core/java/com/android/internal/widget/ILockSettings.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ @@ -469,6 +472,9 @@ LOCAL_SRC_FILES += \ ../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \ ../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \ +LOCAL_SRC_FILES += \ + ../../system/netd/server/binder/android/net/INetd.aidl \ + LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk @@ -702,6 +708,7 @@ aidl_files := \ frameworks/base/core/java/android/service/quicksettings/Tile.aidl \ frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \ system/netd/server/binder/android/net/UidRange.aidl \ + frameworks/base/telephony/java/android/telephony/PcoData.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) @@ -893,16 +900,16 @@ sample_groups := -samplegroup Admin \ ## SDK version identifiers used in the published docs # major[.minor] version for current SDK. (full releases only) -framework_docs_SDK_VERSION:=6.0 +framework_docs_SDK_VERSION:=7.0 # release version (ie "Release x") (full releases only) framework_docs_SDK_REL_ID:=1 framework_docs_LOCAL_DROIDDOC_OPTIONS += \ -hdf sdk.codename N \ - -hdf sdk.preview.version 2 \ + -hdf sdk.preview.version 5 \ -hdf sdk.version $(framework_docs_SDK_VERSION) \ -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \ - -hdf sdk.preview 1 + -hdf sdk.preview 0 # ==== the api stubs and current.xml =========================== include $(CLEAR_VARS) @@ -1052,6 +1059,42 @@ LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) LOCAL_MODULE := offline-sdk +LOCAL_DROIDDOC_OPTIONS:=\ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -offlinemode \ + -title "Android SDK" \ + -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \ + -sdkvalues $(OUT_DOCS) \ + -hdf android.whichdoc offline + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev + +include $(BUILD_DROIDDOC) + +static_doc_index_redirect := $(out_dir)/index.html +$(static_doc_index_redirect): \ + $(LOCAL_PATH)/docs/docs-preview-index.html | $(ACP) + $(hide) mkdir -p $(dir $@) + $(hide) $(ACP) $< $@ + +$(full_target): $(static_doc_index_redirect) +$(full_target): $(framework_built) + + +# ==== static html in the sdk ================================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) + +LOCAL_MODULE := offline-sdk-referenceonly + LOCAL_DROIDDOC_OPTIONS:=\ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ -offlinemode \ @@ -1061,13 +1104,13 @@ LOCAL_DROIDDOC_OPTIONS:=\ -hdf android.whichdoc offline \ -referenceonly -LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev include $(BUILD_DROIDDOC) static_doc_index_redirect := $(out_dir)/index.html $(static_doc_index_redirect): \ - $(LOCAL_PATH)/docs/docs-preview-index.html | $(ACP) + $(LOCAL_PATH)/docs/docs-documentation-redirect.html | $(ACP) $(hide) mkdir -p $(dir $@) $(hide) $(ACP) $< $@ @@ -1099,7 +1142,7 @@ LOCAL_DROIDDOC_OPTIONS:= \ -hdf android.hasSamples true \ -samplesdir $(samples_dir) -LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev include $(BUILD_DROIDDOC) @@ -1159,20 +1202,45 @@ LOCAL_MODULE := ds LOCAL_DROIDDOC_OPTIONS:= \ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ - -devsite \ - -hdf devsite true \ -toroot / \ -hdf android.whichdoc online \ + -devsite \ $(sample_groups) \ - -useUpdatedTemplates \ -hdf android.hasSamples true \ - -yaml _book.yaml \ -samplesdir $(samples_dir) LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev include $(BUILD_DROIDDOC) +# ==== docs for the web (on the devsite app engine server) ======================= +include $(CLEAR_VARS) +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) +# specify a second html input dir and an output path relative to OUT_DIR) +LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl / + +LOCAL_MODULE := ds-static + +LOCAL_DROIDDOC_OPTIONS:= \ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -hdf android.whichdoc online \ + -staticonly \ + -toroot / \ + -devsite \ + -ignoreJdLinks + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev + +include $(BUILD_DROIDDOC) + # ==== site updates for docs (on the androiddevdocs app engine server) ======================= include $(CLEAR_VARS) diff --git a/api/current.txt b/api/current.txt index 4f12ad4ed8c8944a28f9ad01a6fa207945343791..533b577034db0921003828891337b704c6a15581 100644 --- a/api/current.txt +++ b/api/current.txt @@ -397,6 +397,7 @@ package android { field public static final int colorPressedHighlight = 16843661; // 0x101038d field public static final int colorPrimary = 16843827; // 0x1010433 field public static final int colorPrimaryDark = 16843828; // 0x1010434 + field public static final int colorSecondary = 16844080; // 0x1010530 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -420,7 +421,9 @@ package android { field public static final int contentInsetStart = 16843859; // 0x1010453 field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522 field public static final int contextClickable = 16844007; // 0x10104e7 + field public static final int contextDescription = 16844078; // 0x101052e field public static final int contextPopupMenuStyle = 16844033; // 0x1010501 + field public static final int contextUri = 16844077; // 0x101052d field public static final int controlX1 = 16843772; // 0x10103fc field public static final int controlX2 = 16843774; // 0x10103fe field public static final int controlY1 = 16843773; // 0x10103fd @@ -1039,6 +1042,7 @@ package android { field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 + field public static final int roundIcon = 16844076; // 0x101052c field public static final int rowCount = 16843637; // 0x1010375 field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 @@ -1106,11 +1110,16 @@ package android { field public static final int shareInterpolator = 16843195; // 0x10101bb field public static final int sharedUserId = 16842763; // 0x101000b field public static final int sharedUserLabel = 16843361; // 0x1010261 + field public static final int shortcutDisabledMessage = 16844075; // 0x101052b + field public static final int shortcutId = 16844072; // 0x1010528 + field public static final int shortcutLongLabel = 16844074; // 0x101052a + field public static final int shortcutShortLabel = 16844073; // 0x1010529 field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int showAsAction = 16843481; // 0x10102d9 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 field public static final int showForAllUsers = 16844015; // 0x10104ef + field public static final int showMetadataInPreview = 16844079; // 0x101052f field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9 field public static final int showSilent = 16843259; // 0x10101fb field public static final int showText = 16843949; // 0x10104ad @@ -3696,6 +3705,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 @@ -5037,12 +5047,14 @@ package android.app { method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder); method public java.lang.CharSequence getCancelLabel(); method public java.lang.CharSequence getConfirmLabel(); + method public boolean getHintDisplayActionInline(); method public boolean getHintLaunchesActivity(); method public java.lang.CharSequence getInProgressLabel(); method public boolean isAvailableOffline(); method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean); method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence); method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); + method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean); method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean); method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); } @@ -5756,7 +5768,10 @@ package android.app { method public android.content.pm.ServiceInfo getServiceInfo(); method public java.lang.String getServiceName(); method public java.lang.String getSettingsActivity(); + method public boolean getShowMetadataInPreview(); method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); @@ -6484,11 +6499,13 @@ package android.app.usage { method public android.content.res.Configuration getConfiguration(); method public int getEventType(); method public java.lang.String getPackageName(); + method public java.lang.String getShortcutId(); method public long getTimeStamp(); field public static final int CONFIGURATION_CHANGE = 5; // 0x5 field public static final int MOVE_TO_BACKGROUND = 2; // 0x2 field public static final int MOVE_TO_FOREGROUND = 1; // 0x1 field public static final int NONE = 0; // 0x0 + field public static final int SHORTCUT_INVOCATION = 8; // 0x8 field public static final int USER_INTERACTION = 7; // 0x7 } @@ -8191,6 +8208,7 @@ package android.content { field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions"; field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; + field public static final java.lang.String SHORTCUT_SERVICE = "shortcut"; field public static final java.lang.String STORAGE_SERVICE = "storage"; field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final java.lang.String TELECOM_SERVICE = "telecom"; @@ -9500,13 +9518,20 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int); + method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); + method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle); + method public void pinShortcuts(java.lang.String, java.util.List, android.os.UserHandle); method public void registerCallback(android.content.pm.LauncherApps.Callback); method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); + method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9519,6 +9544,20 @@ package android.content.pm { method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle); method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle); + method public void onShortcutsChanged(java.lang.String, java.util.List, android.os.UserHandle); + } + + public static class LauncherApps.ShortcutQuery { + ctor public LauncherApps.ShortcutQuery(); + method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); + method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); + method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); + method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List); + field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 + field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 + field public static final int FLAG_MATCH_PINNED = 2; // 0x2 } public class PackageInfo implements android.os.Parcelable { @@ -10019,6 +10058,66 @@ package android.content.pm { field public java.lang.String permission; } + public final class ShortcutInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.content.ComponentName getActivity(); + method public java.util.Set getCategories(); + method public java.lang.CharSequence getDisabledMessage(); + method public android.os.PersistableBundle getExtras(); + method public java.lang.String getId(); + method public android.content.Intent getIntent(); + method public android.content.Intent[] getIntents(); + method public long getLastChangedTimestamp(); + method public java.lang.CharSequence getLongLabel(); + method public java.lang.String getPackage(); + method public int getRank(); + method public java.lang.CharSequence getShortLabel(); + method public android.os.UserHandle getUserHandle(); + method public boolean hasKeyFieldsOnly(); + method public boolean isDeclaredInManifest(); + method public boolean isDynamic(); + method public boolean isEnabled(); + method public boolean isImmutable(); + method public boolean isPinned(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; + } + + public static class ShortcutInfo.Builder { + ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo build(); + method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); + method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); + method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); + method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]); + method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setRank(int); + method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence); + } + + public class ShortcutManager { + method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, java.lang.CharSequence); + method public void enableShortcuts(java.util.List); + method public java.util.List getDynamicShortcuts(); + method public int getIconMaxHeight(); + method public int getIconMaxWidth(); + method public java.util.List getManifestShortcuts(); + method public int getMaxShortcutCountPerActivity(); + method public java.util.List getPinnedShortcuts(); + method public boolean isRateLimitingActive(); + method public void removeAllDynamicShortcuts(); + method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); + method public boolean setDynamicShortcuts(java.util.List); + method public boolean updateShortcuts(java.util.List); + } + public class Signature implements android.os.Parcelable { ctor public Signature(byte[]); ctor public Signature(java.lang.String); @@ -10225,7 +10324,7 @@ package android.content.res { } public class Resources { - ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); + ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); method public final void finishPreloading(); method public final void flushLayoutCache(); method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException; @@ -10276,7 +10375,7 @@ package android.content.res { method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException; method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException; method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); + method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); } public static class Resources.NotFoundException extends java.lang.RuntimeException { @@ -19556,6 +19655,7 @@ package android.media { field public static final android.os.Parcelable.Creator CREATOR; field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_DEFAULT = 1; // 0x1 + field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe field public static final int ENCODING_DTS = 7; // 0x7 field public static final int ENCODING_DTS_HD = 8; // 0x8 field public static final int ENCODING_E_AC3 = 6; // 0x6 @@ -28268,6 +28368,7 @@ package android.os { field public static final int LOLLIPOP_MR1 = 22; // 0x16 field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 + field public static final int N_MR1 = 25; // 0x19 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -29259,6 +29360,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); method public boolean isUserAGoat(); @@ -29498,6 +29600,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); + field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } public final class StorageVolume implements android.os.Parcelable { @@ -30640,6 +30743,7 @@ package android.provider { public static class CallLog.Calls implements android.provider.BaseColumns { ctor public CallLog.Calls(); method public static java.lang.String getLastOutgoingCall(android.content.Context); + field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7 field public static final int BLOCKED_TYPE = 6; // 0x6 field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number"; field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri"; @@ -30662,6 +30766,7 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER"; field public static final java.lang.String FEATURES = "features"; + field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2 field public static final int FEATURES_VIDEO = 1; // 0x1 field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location"; field public static final int INCOMING_TYPE = 1; // 0x1 @@ -32367,6 +32472,7 @@ package android.provider { field public static final java.lang.String DATA_ROAMING = "data_roaming"; field public static final java.lang.String DEBUG_APP = "debug_app"; field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; + field public static final java.lang.String DEVICE_NAME = "device_name"; field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned"; field public static final java.lang.String HTTP_PROXY = "http_proxy"; field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; @@ -32952,6 +33058,7 @@ package android.provider { field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL"; field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL"; field public static final java.lang.String AUTHORITY = "com.android.voicemail"; + field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE"; field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package"; } @@ -32960,6 +33067,9 @@ package android.provider { method public static android.net.Uri buildSourceUri(java.lang.String); field public static final java.lang.String CONFIGURATION_STATE = "configuration_state"; field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2 + field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3 + field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5 + field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4 field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1 field public static final int CONFIGURATION_STATE_OK = 0; // 0x0 field public static final android.net.Uri CONTENT_URI; @@ -32984,6 +33094,7 @@ package android.provider { field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff field public static final java.lang.String SETTINGS_URI = "settings_uri"; field public static final java.lang.String SOURCE_PACKAGE = "source_package"; + field public static final java.lang.String SOURCE_TYPE = "source_type"; field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; } @@ -35936,9 +36047,14 @@ package android.telecom { method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); + method public final void putExtras(android.os.Bundle); method public void registerCallback(android.telecom.Call.Callback); method public void registerCallback(android.telecom.Call.Callback, android.os.Handler); method public void reject(boolean, java.lang.String); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); + method public void sendCallEvent(java.lang.String, android.os.Bundle); method public void splitFromConference(); method public void stopDtmfTone(); method public void swapConference(); @@ -35952,6 +36068,7 @@ package android.telecom { field public static final int STATE_DISCONNECTING = 10; // 0xa field public static final int STATE_HOLDING = 3; // 0x3 field public static final int STATE_NEW = 0; // 0x0 + field public static final int STATE_PULLING_CALL = 11; // 0xb field public static final int STATE_RINGING = 2; // 0x2 field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8 } @@ -35962,6 +36079,7 @@ package android.telecom { method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List); method public void onChildrenChanged(android.telecom.Call, java.util.List); method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); @@ -35992,6 +36110,7 @@ package android.telecom { method public static java.lang.String propertiesToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80 @@ -36011,7 +36130,9 @@ package android.telecom { field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -36062,6 +36183,7 @@ package android.telecom { method public final android.telecom.CallAudioState getCallAudioState(); method public final java.util.List getConferenceableConnections(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final long getConnectionTime(); method public final java.util.List getConnections(); method public final android.telecom.DisconnectCause getDisconnectCause(); @@ -36074,6 +36196,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onConnectionAdded(android.telecom.Connection); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onMerge(android.telecom.Connection); method public void onMerge(); @@ -36082,10 +36205,14 @@ package android.telecom { method public void onStopDtmfTone(); method public void onSwap(); method public void onUnhold(); + method public final void putExtras(android.os.Bundle); method public final void removeConnection(android.telecom.Connection); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); method public final void setActive(); method public final void setConferenceableConnections(java.util.List); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setConnectionTime(long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); @@ -36115,6 +36242,7 @@ package android.telecom { method public final android.telecom.Conference getConference(); method public final java.util.List getConferenceables(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public final int getState(); @@ -36125,16 +36253,24 @@ package android.telecom { method public void onAnswer(int); method public void onAnswer(); method public void onCallAudioStateChanged(android.telecom.CallAudioState); + method public void onCallEvent(java.lang.String, android.os.Bundle); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onPlayDtmfTone(char); method public void onPostDialContinue(boolean); + method public void onPullExternalCall(); method public void onReject(); method public void onReject(java.lang.String); method public void onSeparate(); method public void onStateChanged(int); method public void onStopDtmfTone(); method public void onUnhold(); + method public static java.lang.String propertiesToString(int); + method public final void putExtras(android.os.Bundle); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); + method public void sendConnectionEvent(java.lang.String, android.os.Bundle); method public final void setActive(); method public final void setAddress(android.net.Uri, int); method public final void setAudioModeIsVoip(boolean); @@ -36142,6 +36278,7 @@ package android.telecom { method public final void setConferenceableConnections(java.util.List); method public final void setConferenceables(java.util.List); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(android.os.Bundle); @@ -36150,6 +36287,7 @@ package android.telecom { method public final void setNextPostDialChar(char); method public final void setOnHold(); method public final void setPostDialWait(java.lang.String); + method public final void setPulling(); method public final void setRingbackRequested(boolean); method public final void setRinging(); method public final void setStatusHints(android.telecom.StatusHints); @@ -36158,6 +36296,7 @@ package android.telecom { method public static java.lang.String stateToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000 field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -36175,15 +36314,21 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 + field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; + field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED"; + field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_DIALING = 3; // 0x3 field public static final int STATE_DISCONNECTED = 6; // 0x6 field public static final int STATE_HOLDING = 5; // 0x5 field public static final int STATE_INITIALIZING = 0; // 0x0 field public static final int STATE_NEW = 1; // 0x1 + field public static final int STATE_PULLING_CALL = 7; // 0x7 field public static final int STATE_RINGING = 2; // 0x2 } @@ -36261,7 +36406,9 @@ package android.telecom { method public java.lang.String getReason(); method public int getTone(); method public void writeToParcel(android.os.Parcel, int); + field public static final int ANSWERED_ELSEWHERE = 11; // 0xb field public static final int BUSY = 7; // 0x7 + field public static final int CALL_PULLED = 12; // 0xc field public static final int CANCELED = 4; // 0x4 field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa field public static final android.os.Parcelable.Creator CREATOR; @@ -36297,6 +36444,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onCallRemoved(android.telecom.Call); method public void onCanAddCallChanged(boolean); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public void onSilenceRinger(); method public final void setAudioRoute(int); method public final void setMuted(boolean); @@ -36419,6 +36567,7 @@ package android.telecom { method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List); method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int); method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onDestroyed(android.telecom.RemoteConference); method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause); @@ -36437,6 +36586,7 @@ package android.telecom { method public android.telecom.RemoteConference getConference(); method public java.util.List getConferenceableConnections(); method public int getConnectionCapabilities(); + method public int getConnectionProperties(); method public android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public int getState(); @@ -36448,6 +36598,7 @@ package android.telecom { method public boolean isVoipAudioMode(); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); method public void registerCallback(android.telecom.RemoteConnection.Callback); method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler); method public void reject(); @@ -36464,6 +36615,8 @@ package android.telecom { method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference); method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int); + method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int); method public void onDestroyed(android.telecom.RemoteConnection); method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause); method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle); @@ -36560,6 +36713,7 @@ package android.telecom { field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE"; + field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI"; field public static final int PRESENTATION_ALLOWED = 1; // 0x1 @@ -36614,9 +36768,11 @@ package android.telephony { field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; + field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; @@ -36648,6 +36804,7 @@ package android.telephony { field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; + field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; @@ -36705,6 +36862,7 @@ package android.telephony { field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; + field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool"; field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; @@ -37289,12 +37447,15 @@ package android.telephony { field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6 field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc field public static final int NETWORK_TYPE_GPRS = 1; // 0x1 + field public static final int NETWORK_TYPE_GSM = 16; // 0x10 field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8 field public static final int NETWORK_TYPE_HSPA = 10; // 0xa field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9 field public static final int NETWORK_TYPE_IDEN = 11; // 0xb + field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12 field public static final int NETWORK_TYPE_LTE = 13; // 0xd + field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11 field public static final int NETWORK_TYPE_UMTS = 3; // 0x3 field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0 field public static final int PHONE_TYPE_CDMA = 2; // 0x2 @@ -40095,7 +40256,10 @@ package android.util { method public boolean equals(android.util.DisplayMetrics); method public void setTo(android.util.DisplayMetrics); method public void setToDefaults(); + field public static final int DENSITY_260 = 260; // 0x104 field public static final int DENSITY_280 = 280; // 0x118 + field public static final int DENSITY_300 = 300; // 0x12c + field public static final int DENSITY_340 = 340; // 0x154 field public static final int DENSITY_360 = 360; // 0x168 field public static final int DENSITY_400 = 400; // 0x190 field public static final int DENSITY_420 = 420; // 0x1a4 @@ -41425,6 +41589,10 @@ package android.view { field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f field public static final int KEYCODE_SYM = 63; // 0x3f field public static final int KEYCODE_SYSRQ = 120; // 0x78 + field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119 + field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a + field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b + field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118 field public static final int KEYCODE_T = 48; // 0x30 field public static final int KEYCODE_TAB = 61; // 0x3d field public static final int KEYCODE_TV = 170; // 0xaa @@ -42307,6 +42475,7 @@ package android.view { method public float getPivotY(); method public android.view.PointerIcon getPointerIcon(); method public android.content.res.Resources getResources(); + method public final boolean getRevealOnFocusHint(); method public final int getRight(); method protected float getRightFadingEdgeStrength(); method protected int getRightPaddingOffset(); @@ -42594,6 +42763,7 @@ package android.view { method public void setPivotY(float); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); + method public final void setRevealOnFocusHint(boolean); method public final void setRight(int); method public void setRotation(float); method public void setRotationX(float); @@ -43743,6 +43913,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -44515,6 +44686,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -44624,6 +44796,7 @@ package android.view.inputmethod { field public static final int IME_NULL = 0; // 0x0 field public int actionId; field public java.lang.CharSequence actionLabel; + field public java.lang.String[] contentMimeTypes; field public android.os.Bundle extras; field public int fieldId; field public java.lang.String fieldName; @@ -44683,6 +44856,7 @@ package android.view.inputmethod { method public abstract boolean clearMetaKeyStates(int); method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); method public abstract boolean deleteSurroundingText(int, int); @@ -44708,6 +44882,7 @@ package android.view.inputmethod { field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1 field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1 + field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1 } public class InputConnectionWrapper implements android.view.inputmethod.InputConnection { @@ -44716,6 +44891,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -44740,6 +44916,19 @@ package android.view.inputmethod { method public void setTarget(android.view.inputmethod.InputConnection); } + public final class InputContentInfo implements android.os.Parcelable { + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); + method public int describeContents(); + method public android.net.Uri getContentUri(); + method public android.content.ClipDescription getDescription(); + method public android.net.Uri getLinkUri(); + method public void releasePermission(); + method public void requestPermission(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public abstract interface InputMethod { method public abstract void attachToken(android.os.IBinder); method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/api/system-current.txt b/api/system-current.txt index d1c03942229fa6ab892b3e36f16747d5a9866f01..c33fe6eed6cc983005903996552c3e3af4127704 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -504,6 +504,7 @@ package android { field public static final int colorPressedHighlight = 16843661; // 0x101038d field public static final int colorPrimary = 16843827; // 0x1010433 field public static final int colorPrimaryDark = 16843828; // 0x1010434 + field public static final int colorSecondary = 16844080; // 0x1010530 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -527,7 +528,9 @@ package android { field public static final int contentInsetStart = 16843859; // 0x1010453 field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522 field public static final int contextClickable = 16844007; // 0x10104e7 + field public static final int contextDescription = 16844078; // 0x101052e field public static final int contextPopupMenuStyle = 16844033; // 0x1010501 + field public static final int contextUri = 16844077; // 0x101052d field public static final int controlX1 = 16843772; // 0x10103fc field public static final int controlX2 = 16843774; // 0x10103fe field public static final int controlY1 = 16843773; // 0x10103fd @@ -1146,6 +1149,7 @@ package android { field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 + field public static final int roundIcon = 16844076; // 0x101052c field public static final int rowCount = 16843637; // 0x1010375 field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 @@ -1217,11 +1221,16 @@ package android { field public static final int shareInterpolator = 16843195; // 0x10101bb field public static final int sharedUserId = 16842763; // 0x101000b field public static final int sharedUserLabel = 16843361; // 0x1010261 + field public static final int shortcutDisabledMessage = 16844075; // 0x101052b + field public static final int shortcutId = 16844072; // 0x1010528 + field public static final int shortcutLongLabel = 16844074; // 0x101052a + field public static final int shortcutShortLabel = 16844073; // 0x1010529 field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int showAsAction = 16843481; // 0x10102d9 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 field public static final int showForAllUsers = 16844015; // 0x10104ef + field public static final int showMetadataInPreview = 16844079; // 0x101052f field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9 field public static final int showSilent = 16843259; // 0x10101fb field public static final int showText = 16843949; // 0x10104ad @@ -3832,6 +3841,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 @@ -4515,6 +4525,15 @@ package android.app { field public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3; // 0x3 } + public abstract class EphemeralResolverService extends android.app.Service { + ctor public EphemeralResolverService(); + method public final void attachBaseContext(android.content.Context); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract java.util.List onEphemeralResolveInfoList(int[], int); + field public static final java.lang.String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; + field public static final java.lang.String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE"; + } + public class ExpandableListActivity extends android.app.Activity implements android.widget.ExpandableListView.OnChildClickListener android.widget.ExpandableListView.OnGroupCollapseListener android.widget.ExpandableListView.OnGroupExpandListener android.view.View.OnCreateContextMenuListener { ctor public ExpandableListActivity(); method public android.widget.ExpandableListAdapter getExpandableListAdapter(); @@ -5185,12 +5204,14 @@ package android.app { method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder); method public java.lang.CharSequence getCancelLabel(); method public java.lang.CharSequence getConfirmLabel(); + method public boolean getHintDisplayActionInline(); method public boolean getHintLaunchesActivity(); method public java.lang.CharSequence getInProgressLabel(); method public boolean isAvailableOffline(); method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean); method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence); method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); + method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean); method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean); method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); } @@ -5904,7 +5925,10 @@ package android.app { method public android.content.pm.ServiceInfo getServiceInfo(); method public java.lang.String getServiceName(); method public java.lang.String getSettingsActivity(); + method public boolean getShowMetadataInPreview(); method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); @@ -6767,11 +6791,13 @@ package android.app.usage { method public android.content.res.Configuration getConfiguration(); method public int getEventType(); method public java.lang.String getPackageName(); + method public java.lang.String getShortcutId(); method public long getTimeStamp(); field public static final int CONFIGURATION_CHANGE = 5; // 0x5 field public static final int MOVE_TO_BACKGROUND = 2; // 0x2 field public static final int MOVE_TO_FOREGROUND = 1; // 0x1 field public static final int NONE = 0; // 0x0 + field public static final int SHORTCUT_INVOCATION = 8; // 0x8 field public static final int USER_INTERACTION = 7; // 0x7 } @@ -8515,6 +8541,7 @@ package android.content { field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions"; field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; + field public static final java.lang.String SHORTCUT_SERVICE = "shortcut"; field public static final java.lang.String STORAGE_SERVICE = "storage"; field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final java.lang.String TELECOM_SERVICE = "telecom"; @@ -9855,13 +9882,20 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int); + method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); + method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle); + method public void pinShortcuts(java.lang.String, java.util.List, android.os.UserHandle); method public void registerCallback(android.content.pm.LauncherApps.Callback); method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); + method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9874,6 +9908,20 @@ package android.content.pm { method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle); method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle); + method public void onShortcutsChanged(java.lang.String, java.util.List, android.os.UserHandle); + } + + public static class LauncherApps.ShortcutQuery { + ctor public LauncherApps.ShortcutQuery(); + method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); + method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); + method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); + method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List); + field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 + field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 + field public static final int FLAG_MATCH_PINNED = 2; // 0x2 } public class PackageInfo implements android.os.Parcelable { @@ -10444,6 +10492,66 @@ package android.content.pm { field public java.lang.String permission; } + public final class ShortcutInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.content.ComponentName getActivity(); + method public java.util.Set getCategories(); + method public java.lang.CharSequence getDisabledMessage(); + method public android.os.PersistableBundle getExtras(); + method public java.lang.String getId(); + method public android.content.Intent getIntent(); + method public android.content.Intent[] getIntents(); + method public long getLastChangedTimestamp(); + method public java.lang.CharSequence getLongLabel(); + method public java.lang.String getPackage(); + method public int getRank(); + method public java.lang.CharSequence getShortLabel(); + method public android.os.UserHandle getUserHandle(); + method public boolean hasKeyFieldsOnly(); + method public boolean isDeclaredInManifest(); + method public boolean isDynamic(); + method public boolean isEnabled(); + method public boolean isImmutable(); + method public boolean isPinned(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; + } + + public static class ShortcutInfo.Builder { + ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo build(); + method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); + method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); + method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); + method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]); + method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setRank(int); + method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence); + } + + public class ShortcutManager { + method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, java.lang.CharSequence); + method public void enableShortcuts(java.util.List); + method public java.util.List getDynamicShortcuts(); + method public int getIconMaxHeight(); + method public int getIconMaxWidth(); + method public java.util.List getManifestShortcuts(); + method public int getMaxShortcutCountPerActivity(); + method public java.util.List getPinnedShortcuts(); + method public boolean isRateLimitingActive(); + method public void removeAllDynamicShortcuts(); + method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); + method public boolean setDynamicShortcuts(java.util.List); + method public boolean updateShortcuts(java.util.List); + } + public class Signature implements android.os.Parcelable { ctor public Signature(byte[]); ctor public Signature(java.lang.String); @@ -10664,7 +10772,7 @@ package android.content.res { } public class Resources { - ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); + ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); method public final void finishPreloading(); method public final void flushLayoutCache(); method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException; @@ -10715,7 +10823,7 @@ package android.content.res { method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException; method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException; method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); + method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); } public static class Resources.NotFoundException extends java.lang.RuntimeException { @@ -21064,6 +21172,7 @@ package android.media { field public static final android.os.Parcelable.Creator CREATOR; field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_DEFAULT = 1; // 0x1 + field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe field public static final int ENCODING_DTS = 7; // 0x7 field public static final int ENCODING_DTS_HD = 8; // 0x8 field public static final int ENCODING_E_AC3 = 6; // 0x6 @@ -25916,6 +26025,33 @@ package android.net.http { package android.net.metrics { + public final class ApfProgramEvent implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_HAS_IPV4_ADDRESS = 1; // 0x1 + field public static final int FLAG_MULTICAST_FILTER_ON = 0; // 0x0 + field public final int currentRas; + field public final int filteredRas; + field public final int flags; + field public final long lifetime; + field public final int programLength; + } + + public final class ApfStats implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public final int droppedRas; + field public final long durationMs; + field public final int matchingRas; + field public final int maxProgramSize; + field public final int parseErrors; + field public final int programUpdates; + field public final int receivedRas; + field public final int zeroLifetimeRas; + } + public final class DefaultNetworkEvent implements android.os.Parcelable { method public int describeContents(); method public static void logEvent(int, int[], int, boolean, boolean); @@ -25933,6 +26069,7 @@ package android.net.metrics { method public static void logStateEvent(java.lang.String, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public final int durationMs; field public final java.lang.String ifName; field public final java.lang.String msg; } @@ -26024,6 +26161,18 @@ package android.net.metrics { field public final int netId; } + public final class RaEvent implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public final long dnsslLifetime; + field public final long prefixPreferredLifetime; + field public final long prefixValidLifetime; + field public final long rdnssLifetime; + field public final long routeInfoLifetime; + field public final long routerLifetime; + } + public final class ValidationProbeEvent implements android.os.Parcelable { method public int describeContents(); method public static void logEvent(int, long, int, int); @@ -30709,6 +30858,7 @@ package android.os { field public static final int LOLLIPOP_MR1 = 22; // 0x16 field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 + field public static final int N_MR1 = 25; // 0x19 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -31544,6 +31694,7 @@ package android.os { method public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException; method public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException; method public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException; + method public static void rebootWipeAb(android.content.Context, java.io.File, java.lang.String) throws java.io.IOException; method public static void rebootWipeCache(android.content.Context) throws java.io.IOException; method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException; method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; @@ -31784,6 +31935,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isManagedProfile(); method public boolean isManagedProfile(int); method public boolean isQuietModeEnabled(android.os.UserHandle); @@ -32032,6 +32184,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); + field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } public final class StorageVolume implements android.os.Parcelable { @@ -33213,6 +33366,7 @@ package android.provider { public static class CallLog.Calls implements android.provider.BaseColumns { ctor public CallLog.Calls(); method public static java.lang.String getLastOutgoingCall(android.content.Context); + field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7 field public static final int BLOCKED_TYPE = 6; // 0x6 field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number"; field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri"; @@ -33235,6 +33389,7 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER"; field public static final java.lang.String FEATURES = "features"; + field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2 field public static final int FEATURES_VIDEO = 1; // 0x1 field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location"; field public static final int INCOMING_TYPE = 1; // 0x1 @@ -35072,6 +35227,7 @@ package android.provider { field public static final java.lang.String DATA_ROAMING = "data_roaming"; field public static final java.lang.String DEBUG_APP = "debug_app"; field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; + field public static final java.lang.String DEVICE_NAME = "device_name"; field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned"; field public static final java.lang.String HTTP_PROXY = "http_proxy"; field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; @@ -35660,6 +35816,7 @@ package android.provider { field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL"; field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL"; field public static final java.lang.String AUTHORITY = "com.android.voicemail"; + field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE"; field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package"; } @@ -35668,6 +35825,9 @@ package android.provider { method public static android.net.Uri buildSourceUri(java.lang.String); field public static final java.lang.String CONFIGURATION_STATE = "configuration_state"; field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2 + field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3 + field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5 + field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4 field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1 field public static final int CONFIGURATION_STATE_OK = 0; // 0x0 field public static final android.net.Uri CONTENT_URI; @@ -35692,6 +35852,7 @@ package android.provider { field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff field public static final java.lang.String SETTINGS_URI = "settings_uri"; field public static final java.lang.String SOURCE_PACKAGE = "source_package"; + field public static final java.lang.String SOURCE_TYPE = "source_type"; field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; } @@ -38772,10 +38933,15 @@ package android.telecom { method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); + method public final void putExtras(android.os.Bundle); method public void registerCallback(android.telecom.Call.Callback); method public void registerCallback(android.telecom.Call.Callback, android.os.Handler); method public void reject(boolean, java.lang.String); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); method public deprecated void removeListener(android.telecom.Call.Listener); + method public void sendCallEvent(java.lang.String, android.os.Bundle); method public void splitFromConference(); method public void stopDtmfTone(); method public void swapConference(); @@ -38790,6 +38956,7 @@ package android.telecom { field public static final int STATE_HOLDING = 3; // 0x3 field public static final int STATE_NEW = 0; // 0x0 field public static final deprecated int STATE_PRE_DIAL_WAIT = 8; // 0x8 + field public static final int STATE_PULLING_CALL = 11; // 0xb field public static final int STATE_RINGING = 2; // 0x2 field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8 } @@ -38800,6 +38967,7 @@ package android.telecom { method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List); method public void onChildrenChanged(android.telecom.Call, java.util.List); method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); @@ -38830,6 +38998,7 @@ package android.telecom { method public static java.lang.String propertiesToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80 @@ -38849,7 +39018,9 @@ package android.telecom { field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -38906,6 +39077,7 @@ package android.telecom { method public final java.util.List getConferenceableConnections(); method public final deprecated long getConnectTimeMillis(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final long getConnectionTime(); method public final java.util.List getConnections(); method public final android.telecom.DisconnectCause getDisconnectCause(); @@ -38920,6 +39092,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onConnectionAdded(android.telecom.Connection); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onMerge(android.telecom.Connection); method public void onMerge(); @@ -38928,11 +39101,15 @@ package android.telecom { method public void onStopDtmfTone(); method public void onSwap(); method public void onUnhold(); + method public final void putExtras(android.os.Bundle); method public final void removeConnection(android.telecom.Connection); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); method public final void setActive(); method public final void setConferenceableConnections(java.util.List); method public final deprecated void setConnectTimeMillis(long); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setConnectionTime(long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); @@ -38963,6 +39140,7 @@ package android.telecom { method public final android.telecom.Conference getConference(); method public final java.util.List getConferenceables(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public final int getState(); @@ -38974,16 +39152,24 @@ package android.telecom { method public void onAnswer(); method public deprecated void onAudioStateChanged(android.telecom.AudioState); method public void onCallAudioStateChanged(android.telecom.CallAudioState); + method public void onCallEvent(java.lang.String, android.os.Bundle); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onPlayDtmfTone(char); method public void onPostDialContinue(boolean); + method public void onPullExternalCall(); method public void onReject(); method public void onReject(java.lang.String); method public void onSeparate(); method public void onStateChanged(int); method public void onStopDtmfTone(); method public void onUnhold(); + method public static java.lang.String propertiesToString(int); + method public final void putExtras(android.os.Bundle); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); + method public void sendConnectionEvent(java.lang.String, android.os.Bundle); method public final void setActive(); method public final void setAddress(android.net.Uri, int); method public final void setAudioModeIsVoip(boolean); @@ -38991,6 +39177,7 @@ package android.telecom { method public final void setConferenceableConnections(java.util.List); method public final void setConferenceables(java.util.List); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(android.os.Bundle); @@ -38999,6 +39186,7 @@ package android.telecom { method public final void setNextPostDialChar(char); method public final void setOnHold(); method public final void setPostDialWait(java.lang.String); + method public final void setPulling(); method public final void setRingbackRequested(boolean); method public final void setRinging(); method public final void setStatusHints(android.telecom.StatusHints); @@ -39007,6 +39195,7 @@ package android.telecom { method public static java.lang.String stateToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000 field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -39024,15 +39213,21 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 + field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; + field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED"; + field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_DIALING = 3; // 0x3 field public static final int STATE_DISCONNECTED = 6; // 0x6 field public static final int STATE_HOLDING = 5; // 0x5 field public static final int STATE_INITIALIZING = 0; // 0x0 field public static final int STATE_NEW = 1; // 0x1 + field public static final int STATE_PULLING_CALL = 7; // 0x7 field public static final int STATE_RINGING = 2; // 0x2 } @@ -39110,7 +39305,9 @@ package android.telecom { method public java.lang.String getReason(); method public int getTone(); method public void writeToParcel(android.os.Parcel, int); + field public static final int ANSWERED_ELSEWHERE = 11; // 0xb field public static final int BUSY = 7; // 0x7 + field public static final int CALL_PULLED = 12; // 0xc field public static final int CANCELED = 4; // 0x4 field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa field public static final android.os.Parcelable.Creator CREATOR; @@ -39147,6 +39344,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onCallRemoved(android.telecom.Call); method public void onCanAddCallChanged(boolean); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public deprecated void onPhoneCreated(android.telecom.Phone); method public deprecated void onPhoneDestroyed(android.telecom.Phone); method public void onSilenceRinger(); @@ -39184,14 +39382,16 @@ package android.telecom { } public class ParcelableCallAnalytics implements android.os.Parcelable { - ctor public ParcelableCallAnalytics(long, long, int, boolean, boolean, int, int, boolean, java.lang.String, boolean); + ctor public ParcelableCallAnalytics(long, long, int, boolean, boolean, int, int, boolean, java.lang.String, boolean, java.util.List, java.util.List); ctor public ParcelableCallAnalytics(android.os.Parcel); + method public java.util.List analyticsEvents(); method public int describeContents(); method public long getCallDurationMillis(); method public int getCallTechnologies(); method public int getCallTerminationCode(); method public int getCallType(); method public java.lang.String getConnectionService(); + method public java.util.List getEventTimings(); method public long getStartTimeMillis(); method public boolean isAdditionalCall(); method public boolean isCreatedFromExistingConnection(); @@ -39212,6 +39412,73 @@ package android.telecom { field public static final int THIRD_PARTY_PHONE = 16; // 0x10 } + public static final class ParcelableCallAnalytics.AnalyticsEvent implements android.os.Parcelable { + ctor public ParcelableCallAnalytics.AnalyticsEvent(int, long); + method public int describeContents(); + method public int getEventName(); + method public long getTimeSinceLastEvent(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int AUDIO_ROUTE_BT = 204; // 0xcc + field public static final int AUDIO_ROUTE_EARPIECE = 205; // 0xcd + field public static final int AUDIO_ROUTE_HEADSET = 206; // 0xce + field public static final int AUDIO_ROUTE_SPEAKER = 207; // 0xcf + field public static final int BIND_CS = 5; // 0x5 + field public static final int BLOCK_CHECK_FINISHED = 105; // 0x69 + field public static final int BLOCK_CHECK_INITIATED = 104; // 0x68 + field public static final int CONFERENCE_WITH = 300; // 0x12c + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int CS_BOUND = 6; // 0x6 + field public static final int DIRECT_TO_VM_FINISHED = 103; // 0x67 + field public static final int DIRECT_TO_VM_INITIATED = 102; // 0x66 + field public static final int FILTERING_COMPLETED = 107; // 0x6b + field public static final int FILTERING_INITIATED = 106; // 0x6a + field public static final int FILTERING_TIMED_OUT = 108; // 0x6c + field public static final int MUTE = 202; // 0xca + field public static final int REMOTELY_HELD = 402; // 0x192 + field public static final int REMOTELY_UNHELD = 403; // 0x193 + field public static final int REQUEST_ACCEPT = 7; // 0x7 + field public static final int REQUEST_HOLD = 400; // 0x190 + field public static final int REQUEST_PULL = 500; // 0x1f4 + field public static final int REQUEST_REJECT = 8; // 0x8 + field public static final int REQUEST_UNHOLD = 401; // 0x191 + field public static final int SCREENING_COMPLETED = 101; // 0x65 + field public static final int SCREENING_SENT = 100; // 0x64 + field public static final int SET_ACTIVE = 1; // 0x1 + field public static final int SET_DIALING = 4; // 0x4 + field public static final int SET_DISCONNECTED = 2; // 0x2 + field public static final int SET_HOLD = 404; // 0x194 + field public static final int SET_PARENT = 302; // 0x12e + field public static final int SET_SELECT_PHONE_ACCOUNT = 0; // 0x0 + field public static final int SILENCE = 201; // 0xc9 + field public static final int SKIP_RINGING = 200; // 0xc8 + field public static final int SPLIT_CONFERENCE = 301; // 0x12d + field public static final int START_CONNECTION = 3; // 0x3 + field public static final int SWAP = 405; // 0x195 + field public static final int UNMUTE = 203; // 0xcb + } + + public static final class ParcelableCallAnalytics.EventTiming implements android.os.Parcelable { + ctor public ParcelableCallAnalytics.EventTiming(int, long); + method public int describeContents(); + method public int getName(); + method public long getTime(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ACCEPT_TIMING = 0; // 0x0 + field public static final int BIND_CS_TIMING = 6; // 0x6 + field public static final int BLOCK_CHECK_FINISHED_TIMING = 9; // 0x9 + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int DIRECT_TO_VM_FINISHED_TIMING = 8; // 0x8 + field public static final int DISCONNECT_TIMING = 2; // 0x2 + field public static final int FILTERING_COMPLETED_TIMING = 10; // 0xa + field public static final int FILTERING_TIMED_OUT_TIMING = 11; // 0xb + field public static final int HOLD_TIMING = 3; // 0x3 + field public static final int INVALID = 999999; // 0xf423f + field public static final int OUTGOING_TIME_TO_DIALING_TIMING = 5; // 0x5 + field public static final int REJECT_TIMING = 1; // 0x1 + field public static final int SCREENING_COMPLETED_TIMING = 7; // 0x7 + field public static final int UNHOLD_TIMING = 4; // 0x4 + } + public final deprecated class Phone { method public final void addListener(android.telecom.Phone.Listener); method public final boolean canAddCall(); @@ -39324,6 +39591,7 @@ package android.telecom { method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List); method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int); method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onDestroyed(android.telecom.RemoteConference); method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause); @@ -39342,6 +39610,7 @@ package android.telecom { method public android.telecom.RemoteConference getConference(); method public java.util.List getConferenceableConnections(); method public int getConnectionCapabilities(); + method public int getConnectionProperties(); method public android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public int getState(); @@ -39353,6 +39622,7 @@ package android.telecom { method public boolean isVoipAudioMode(); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); method public void registerCallback(android.telecom.RemoteConnection.Callback); method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler); method public void reject(); @@ -39370,6 +39640,8 @@ package android.telecom { method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference); method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int); + method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int); method public void onDestroyed(android.telecom.RemoteConnection); method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause); method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle); @@ -39423,6 +39695,41 @@ package android.telecom { field public static final android.os.Parcelable.Creator CREATOR; } + public final class TelecomAnalytics implements android.os.Parcelable { + ctor public TelecomAnalytics(java.util.List, java.util.List); + method public int describeContents(); + method public java.util.List getCallAnalytics(); + method public java.util.List getSessionTimings(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class TelecomAnalytics.SessionTiming implements android.os.Parcelable { + ctor public TelecomAnalytics.SessionTiming(int, long); + method public int describeContents(); + method public java.lang.Integer getKey(); + method public long getTime(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int CSW_ADD_CONFERENCE_CALL = 108; // 0x6c + field public static final int CSW_HANDLE_CREATE_CONNECTION_COMPLETE = 100; // 0x64 + field public static final int CSW_REMOVE_CALL = 106; // 0x6a + field public static final int CSW_SET_ACTIVE = 101; // 0x65 + field public static final int CSW_SET_DIALING = 103; // 0x67 + field public static final int CSW_SET_DISCONNECTED = 104; // 0x68 + field public static final int CSW_SET_IS_CONFERENCED = 107; // 0x6b + field public static final int CSW_SET_ON_HOLD = 105; // 0x69 + field public static final int CSW_SET_RINGING = 102; // 0x66 + field public static final int ICA_ANSWER_CALL = 1; // 0x1 + field public static final int ICA_CONFERENCE = 8; // 0x8 + field public static final int ICA_DISCONNECT_CALL = 3; // 0x3 + field public static final int ICA_HOLD_CALL = 4; // 0x4 + field public static final int ICA_MUTE = 6; // 0x6 + field public static final int ICA_REJECT_CALL = 2; // 0x2 + field public static final int ICA_SET_AUDIO_ROUTE = 7; // 0x7 + field public static final int ICA_UNHOLD_CALL = 5; // 0x5 + } + public class TelecomManager { method public void acceptRingingCall(); method public void acceptRingingCall(int); @@ -39432,7 +39739,7 @@ package android.telecom { method public deprecated void clearAccounts(); method public void clearPhoneAccounts(); method public android.content.Intent createManageBlockedNumbersIntent(); - method public java.util.List dumpAnalytics(); + method public android.telecom.TelecomAnalytics dumpAnalytics(); method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean); method public boolean endCall(); method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle); @@ -39493,6 +39800,7 @@ package android.telecom { field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE"; + field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI"; field public static final int PRESENTATION_ALLOWED = 1; // 0x1 @@ -39549,9 +39857,11 @@ package android.telephony { field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; + field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; @@ -39583,6 +39893,7 @@ package android.telephony { field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; + field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; @@ -39640,6 +39951,7 @@ package android.telephony { field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; + field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool"; field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; @@ -40135,6 +40447,26 @@ package android.telephony { method public void onSubscriptionsChanged(); } + public final class TelephonyHistogram implements android.os.Parcelable { + ctor public TelephonyHistogram(int, int, int); + ctor public TelephonyHistogram(android.telephony.TelephonyHistogram); + ctor public TelephonyHistogram(android.os.Parcel); + method public void addTimeTaken(int); + method public int describeContents(); + method public int getAverageTime(); + method public int getBucketCount(); + method public int[] getBucketCounters(); + method public int[] getBucketEndPoints(); + method public int getCategory(); + method public int getId(); + method public int getMaxTime(); + method public int getMinTime(); + method public int getSampleCount(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1 + } + public class TelephonyManager { method public void answerRingingCall(); method public void call(java.lang.String, java.lang.String); @@ -40184,6 +40516,7 @@ package android.telephony { method public java.lang.String getSimSerialNumber(); method public int getSimState(); method public java.lang.String getSubscriberId(); + method public java.util.List getTelephonyHistograms(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailNumber(); method public int getVoiceNetworkType(); @@ -40207,6 +40540,7 @@ package android.telephony { method public boolean isSmsCapable(); method public boolean isTtyModeSupported(); method public boolean isVideoCallingEnabled(); + method public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method public boolean isVoiceCapable(); method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); @@ -40220,6 +40554,7 @@ package android.telephony { method public boolean setPreferredNetworkTypeToGlobal(); method public boolean setRadio(boolean); method public boolean setRadioPower(boolean); + method public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean); method public boolean setVoiceMailNumber(java.lang.String, java.lang.String); method public void silenceRinger(); method public boolean supplyPin(java.lang.String); @@ -40272,12 +40607,15 @@ package android.telephony { field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6 field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc field public static final int NETWORK_TYPE_GPRS = 1; // 0x1 + field public static final int NETWORK_TYPE_GSM = 16; // 0x10 field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8 field public static final int NETWORK_TYPE_HSPA = 10; // 0xa field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9 field public static final int NETWORK_TYPE_IDEN = 11; // 0xb + field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12 field public static final int NETWORK_TYPE_LTE = 13; // 0xd + field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11 field public static final int NETWORK_TYPE_UMTS = 3; // 0x3 field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0 field public static final int PHONE_TYPE_CDMA = 2; // 0x2 @@ -43095,7 +43433,10 @@ package android.util { method public boolean equals(android.util.DisplayMetrics); method public void setTo(android.util.DisplayMetrics); method public void setToDefaults(); + field public static final int DENSITY_260 = 260; // 0x104 field public static final int DENSITY_280 = 280; // 0x118 + field public static final int DENSITY_300 = 300; // 0x12c + field public static final int DENSITY_340 = 340; // 0x154 field public static final int DENSITY_360 = 360; // 0x168 field public static final int DENSITY_400 = 400; // 0x190 field public static final int DENSITY_420 = 420; // 0x1a4 @@ -44425,6 +44766,10 @@ package android.view { field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f field public static final int KEYCODE_SYM = 63; // 0x3f field public static final int KEYCODE_SYSRQ = 120; // 0x78 + field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119 + field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a + field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b + field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118 field public static final int KEYCODE_T = 48; // 0x30 field public static final int KEYCODE_TAB = 61; // 0x3d field public static final int KEYCODE_TV = 170; // 0xaa @@ -45307,6 +45652,7 @@ package android.view { method public float getPivotY(); method public android.view.PointerIcon getPointerIcon(); method public android.content.res.Resources getResources(); + method public final boolean getRevealOnFocusHint(); method public final int getRight(); method protected float getRightFadingEdgeStrength(); method protected int getRightPaddingOffset(); @@ -45594,6 +45940,7 @@ package android.view { method public void setPivotY(float); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); + method public final void setRevealOnFocusHint(boolean); method public final void setRight(int); method public void setRotation(float); method public void setRotationX(float); @@ -46746,6 +47093,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -47518,6 +47866,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -47627,6 +47976,7 @@ package android.view.inputmethod { field public static final int IME_NULL = 0; // 0x0 field public int actionId; field public java.lang.CharSequence actionLabel; + field public java.lang.String[] contentMimeTypes; field public android.os.Bundle extras; field public int fieldId; field public java.lang.String fieldName; @@ -47686,6 +48036,7 @@ package android.view.inputmethod { method public abstract boolean clearMetaKeyStates(int); method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); method public abstract boolean deleteSurroundingText(int, int); @@ -47711,6 +48062,7 @@ package android.view.inputmethod { field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1 field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1 + field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1 } public class InputConnectionWrapper implements android.view.inputmethod.InputConnection { @@ -47719,6 +48071,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -47743,6 +48096,19 @@ package android.view.inputmethod { method public void setTarget(android.view.inputmethod.InputConnection); } + public final class InputContentInfo implements android.os.Parcelable { + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); + method public int describeContents(); + method public android.net.Uri getContentUri(); + method public android.content.ClipDescription getDescription(); + method public android.net.Uri getLinkUri(); + method public void releasePermission(); + method public void requestPermission(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public abstract interface InputMethod { method public abstract void attachToken(android.os.IBinder); method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/api/test-current.txt b/api/test-current.txt index a70e9e3f7e19ead077bfdadf6e121325c51273a3..3b5c2231ccf2ac281668bd86d75ba12024fcb36e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -397,6 +397,7 @@ package android { field public static final int colorPressedHighlight = 16843661; // 0x101038d field public static final int colorPrimary = 16843827; // 0x1010433 field public static final int colorPrimaryDark = 16843828; // 0x1010434 + field public static final int colorSecondary = 16844080; // 0x1010530 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -420,7 +421,9 @@ package android { field public static final int contentInsetStart = 16843859; // 0x1010453 field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522 field public static final int contextClickable = 16844007; // 0x10104e7 + field public static final int contextDescription = 16844078; // 0x101052e field public static final int contextPopupMenuStyle = 16844033; // 0x1010501 + field public static final int contextUri = 16844077; // 0x101052d field public static final int controlX1 = 16843772; // 0x10103fc field public static final int controlX2 = 16843774; // 0x10103fe field public static final int controlY1 = 16843773; // 0x10103fd @@ -1039,6 +1042,7 @@ package android { field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 + field public static final int roundIcon = 16844076; // 0x101052c field public static final int rowCount = 16843637; // 0x1010375 field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 @@ -1106,11 +1110,16 @@ package android { field public static final int shareInterpolator = 16843195; // 0x10101bb field public static final int sharedUserId = 16842763; // 0x101000b field public static final int sharedUserLabel = 16843361; // 0x1010261 + field public static final int shortcutDisabledMessage = 16844075; // 0x101052b + field public static final int shortcutId = 16844072; // 0x1010528 + field public static final int shortcutLongLabel = 16844074; // 0x101052a + field public static final int shortcutShortLabel = 16844073; // 0x1010529 field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int showAsAction = 16843481; // 0x10102d9 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 field public static final int showForAllUsers = 16844015; // 0x10104ef + field public static final int showMetadataInPreview = 16844079; // 0x101052f field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9 field public static final int showSilent = 16843259; // 0x10101fb field public static final int showText = 16843949; // 0x10104ad @@ -3696,6 +3705,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 @@ -5038,12 +5048,14 @@ package android.app { method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder); method public java.lang.CharSequence getCancelLabel(); method public java.lang.CharSequence getConfirmLabel(); + method public boolean getHintDisplayActionInline(); method public boolean getHintLaunchesActivity(); method public java.lang.CharSequence getInProgressLabel(); method public boolean isAvailableOffline(); method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean); method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence); method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); + method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean); method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean); method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); } @@ -5762,7 +5774,10 @@ package android.app { method public android.content.pm.ServiceInfo getServiceInfo(); method public java.lang.String getServiceName(); method public java.lang.String getSettingsActivity(); + method public boolean getShowMetadataInPreview(); method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; + method public android.net.Uri loadContextUri(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException; method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); @@ -6490,11 +6505,13 @@ package android.app.usage { method public android.content.res.Configuration getConfiguration(); method public int getEventType(); method public java.lang.String getPackageName(); + method public java.lang.String getShortcutId(); method public long getTimeStamp(); field public static final int CONFIGURATION_CHANGE = 5; // 0x5 field public static final int MOVE_TO_BACKGROUND = 2; // 0x2 field public static final int MOVE_TO_FOREGROUND = 1; // 0x1 field public static final int NONE = 0; // 0x0 + field public static final int SHORTCUT_INVOCATION = 8; // 0x8 field public static final int USER_INTERACTION = 7; // 0x7 } @@ -8199,6 +8216,7 @@ package android.content { field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions"; field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; + field public static final java.lang.String SHORTCUT_SERVICE = "shortcut"; field public static final java.lang.String STORAGE_SERVICE = "storage"; field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final java.lang.String TELECOM_SERVICE = "telecom"; @@ -9512,13 +9530,20 @@ package android.content.pm { public class LauncherApps { ctor public LauncherApps(android.content.Context); method public java.util.List getActivityList(java.lang.String, android.os.UserHandle); + method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int); + method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int); + method public java.util.List getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle); + method public boolean hasShortcutHostPermission(); method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle); method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle); + method public void pinShortcuts(java.lang.String, java.util.List, android.os.UserHandle); method public void registerCallback(android.content.pm.LauncherApps.Callback); method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); + method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); + method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle); method public void unregisterCallback(android.content.pm.LauncherApps.Callback); } @@ -9531,6 +9556,20 @@ package android.content.pm { method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle); method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle); + method public void onShortcutsChanged(java.lang.String, java.util.List, android.os.UserHandle); + } + + public static class LauncherApps.ShortcutQuery { + ctor public LauncherApps.ShortcutQuery(); + method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); + method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); + method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); + method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List); + field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 + field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 + field public static final int FLAG_MATCH_PINNED = 2; // 0x2 } public class PackageInfo implements android.os.Parcelable { @@ -10032,6 +10071,67 @@ package android.content.pm { field public java.lang.String permission; } + public final class ShortcutInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.content.ComponentName getActivity(); + method public java.util.Set getCategories(); + method public java.lang.CharSequence getDisabledMessage(); + method public android.os.PersistableBundle getExtras(); + method public java.lang.String getId(); + method public android.content.Intent getIntent(); + method public android.content.Intent[] getIntents(); + method public long getLastChangedTimestamp(); + method public java.lang.CharSequence getLongLabel(); + method public java.lang.String getPackage(); + method public int getRank(); + method public java.lang.CharSequence getShortLabel(); + method public android.os.UserHandle getUserHandle(); + method public boolean hasKeyFieldsOnly(); + method public boolean isDeclaredInManifest(); + method public boolean isDynamic(); + method public boolean isEnabled(); + method public boolean isImmutable(); + method public boolean isPinned(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; + } + + public static class ShortcutInfo.Builder { + ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo build(); + method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); + method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set); + method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); + method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); + method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setIntents(android.content.Intent[]); + method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence); + method public android.content.pm.ShortcutInfo.Builder setRank(int); + method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence); + } + + public class ShortcutManager { + ctor public ShortcutManager(android.content.Context); + method public boolean addDynamicShortcuts(java.util.List); + method public void disableShortcuts(java.util.List); + method public void disableShortcuts(java.util.List, java.lang.CharSequence); + method public void enableShortcuts(java.util.List); + method public java.util.List getDynamicShortcuts(); + method public int getIconMaxHeight(); + method public int getIconMaxWidth(); + method public java.util.List getManifestShortcuts(); + method public int getMaxShortcutCountPerActivity(); + method public java.util.List getPinnedShortcuts(); + method public boolean isRateLimitingActive(); + method public void removeAllDynamicShortcuts(); + method public void removeDynamicShortcuts(java.util.List); + method public void reportShortcutUsed(java.lang.String); + method public boolean setDynamicShortcuts(java.util.List); + method public boolean updateShortcuts(java.util.List); + } + public class Signature implements android.os.Parcelable { ctor public Signature(byte[]); ctor public Signature(java.lang.String); @@ -10238,7 +10338,7 @@ package android.content.res { } public class Resources { - ctor public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); + ctor public deprecated Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); method public final void finishPreloading(); method public final void flushLayoutCache(); method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException; @@ -10289,7 +10389,7 @@ package android.content.res { method public android.content.res.AssetFileDescriptor openRawResourceFd(int) throws android.content.res.Resources.NotFoundException; method public void parseBundleExtra(java.lang.String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException; method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); + method public deprecated void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); } public static class Resources.NotFoundException extends java.lang.RuntimeException { @@ -19625,6 +19725,7 @@ package android.media { field public static final android.os.Parcelable.Creator CREATOR; field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_DEFAULT = 1; // 0x1 + field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe field public static final int ENCODING_DTS = 7; // 0x7 field public static final int ENCODING_DTS_HD = 8; // 0x8 field public static final int ENCODING_E_AC3 = 6; // 0x6 @@ -28337,6 +28438,7 @@ package android.os { field public static final int LOLLIPOP_MR1 = 22; // 0x16 field public static final int M = 23; // 0x17 field public static final int N = 24; // 0x18 + field public static final int N_MR1 = 25; // 0x19 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -29126,6 +29228,7 @@ package android.os { method public static final long getStartElapsedRealtime(); method public static final long getStartUptimeMillis(); method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException; + method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException; method public static final int getUidForName(java.lang.String); method public static final boolean is64Bit(); method public static boolean isApplicationUid(int); @@ -29329,6 +29432,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); method public boolean isUserAGoat(); @@ -29568,6 +29672,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); + field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; } public final class StorageVolume implements android.os.Parcelable { @@ -30713,6 +30818,7 @@ package android.provider { public static class CallLog.Calls implements android.provider.BaseColumns { ctor public CallLog.Calls(); method public static java.lang.String getLastOutgoingCall(android.content.Context); + field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7 field public static final int BLOCKED_TYPE = 6; // 0x6 field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number"; field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri"; @@ -30735,6 +30841,7 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER"; field public static final java.lang.String FEATURES = "features"; + field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2 field public static final int FEATURES_VIDEO = 1; // 0x1 field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location"; field public static final int INCOMING_TYPE = 1; // 0x1 @@ -32440,6 +32547,7 @@ package android.provider { field public static final java.lang.String DATA_ROAMING = "data_roaming"; field public static final java.lang.String DEBUG_APP = "debug_app"; field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; + field public static final java.lang.String DEVICE_NAME = "device_name"; field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned"; field public static final java.lang.String HTTP_PROXY = "http_proxy"; field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; @@ -33028,6 +33136,7 @@ package android.provider { field public static final java.lang.String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL"; field public static final java.lang.String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL"; field public static final java.lang.String AUTHORITY = "com.android.voicemail"; + field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.provider.extra.PHONE_ACCOUNT_HANDLE"; field public static final java.lang.String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; field public static final java.lang.String PARAM_KEY_SOURCE_PACKAGE = "source_package"; } @@ -33036,6 +33145,9 @@ package android.provider { method public static android.net.Uri buildSourceUri(java.lang.String); field public static final java.lang.String CONFIGURATION_STATE = "configuration_state"; field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2 + field public static final int CONFIGURATION_STATE_CONFIGURING = 3; // 0x3 + field public static final int CONFIGURATION_STATE_DISABLED = 5; // 0x5 + field public static final int CONFIGURATION_STATE_FAILED = 4; // 0x4 field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1 field public static final int CONFIGURATION_STATE_OK = 0; // 0x0 field public static final android.net.Uri CONTENT_URI; @@ -33060,6 +33172,7 @@ package android.provider { field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff field public static final java.lang.String SETTINGS_URI = "settings_uri"; field public static final java.lang.String SOURCE_PACKAGE = "source_package"; + field public static final java.lang.String SOURCE_TYPE = "source_type"; field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; } @@ -36013,9 +36126,14 @@ package android.telecom { method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); + method public final void putExtras(android.os.Bundle); method public void registerCallback(android.telecom.Call.Callback); method public void registerCallback(android.telecom.Call.Callback, android.os.Handler); method public void reject(boolean, java.lang.String); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); + method public void sendCallEvent(java.lang.String, android.os.Bundle); method public void splitFromConference(); method public void stopDtmfTone(); method public void swapConference(); @@ -36029,6 +36147,7 @@ package android.telecom { field public static final int STATE_DISCONNECTING = 10; // 0xa field public static final int STATE_HOLDING = 3; // 0x3 field public static final int STATE_NEW = 0; // 0x0 + field public static final int STATE_PULLING_CALL = 11; // 0xb field public static final int STATE_RINGING = 2; // 0x2 field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8 } @@ -36039,6 +36158,7 @@ package android.telecom { method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List); method public void onChildrenChanged(android.telecom.Call, java.util.List); method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); @@ -36069,6 +36189,7 @@ package android.telecom { method public static java.lang.String propertiesToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 8388608; // 0x800000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 field public static final int CAPABILITY_HOLD = 1; // 0x1 field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80 @@ -36088,7 +36209,9 @@ package android.telecom { field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -36139,6 +36262,7 @@ package android.telecom { method public final android.telecom.CallAudioState getCallAudioState(); method public final java.util.List getConferenceableConnections(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final long getConnectionTime(); method public final java.util.List getConnections(); method public final android.telecom.DisconnectCause getDisconnectCause(); @@ -36151,6 +36275,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onConnectionAdded(android.telecom.Connection); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onMerge(android.telecom.Connection); method public void onMerge(); @@ -36159,10 +36284,14 @@ package android.telecom { method public void onStopDtmfTone(); method public void onSwap(); method public void onUnhold(); + method public final void putExtras(android.os.Bundle); method public final void removeConnection(android.telecom.Connection); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); method public final void setActive(); method public final void setConferenceableConnections(java.util.List); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setConnectionTime(long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); @@ -36192,6 +36321,7 @@ package android.telecom { method public final android.telecom.Conference getConference(); method public final java.util.List getConferenceables(); method public final int getConnectionCapabilities(); + method public final int getConnectionProperties(); method public final android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public final int getState(); @@ -36202,16 +36332,24 @@ package android.telecom { method public void onAnswer(int); method public void onAnswer(); method public void onCallAudioStateChanged(android.telecom.CallAudioState); + method public void onCallEvent(java.lang.String, android.os.Bundle); method public void onDisconnect(); + method public void onExtrasChanged(android.os.Bundle); method public void onHold(); method public void onPlayDtmfTone(char); method public void onPostDialContinue(boolean); + method public void onPullExternalCall(); method public void onReject(); method public void onReject(java.lang.String); method public void onSeparate(); method public void onStateChanged(int); method public void onStopDtmfTone(); method public void onUnhold(); + method public static java.lang.String propertiesToString(int); + method public final void putExtras(android.os.Bundle); + method public final void removeExtras(java.util.List); + method public final void removeExtras(java.lang.String...); + method public void sendConnectionEvent(java.lang.String, android.os.Bundle); method public final void setActive(); method public final void setAddress(android.net.Uri, int); method public final void setAudioModeIsVoip(boolean); @@ -36219,6 +36357,7 @@ package android.telecom { method public final void setConferenceableConnections(java.util.List); method public final void setConferenceables(java.util.List); method public final void setConnectionCapabilities(int); + method public final void setConnectionProperties(int); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(android.os.Bundle); @@ -36227,6 +36366,7 @@ package android.telecom { method public final void setNextPostDialChar(char); method public final void setOnHold(); method public final void setPostDialWait(java.lang.String); + method public final void setPulling(); method public final void setRingbackRequested(boolean); method public final void setRinging(); method public final void setStatusHints(android.telecom.StatusHints); @@ -36235,6 +36375,7 @@ package android.telecom { method public static java.lang.String stateToString(int); field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000 field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 + field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000 field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000 field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 @@ -36252,15 +36393,21 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 + field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; + field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED"; + field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; + field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 + field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_DIALING = 3; // 0x3 field public static final int STATE_DISCONNECTED = 6; // 0x6 field public static final int STATE_HOLDING = 5; // 0x5 field public static final int STATE_INITIALIZING = 0; // 0x0 field public static final int STATE_NEW = 1; // 0x1 + field public static final int STATE_PULLING_CALL = 7; // 0x7 field public static final int STATE_RINGING = 2; // 0x2 } @@ -36338,7 +36485,9 @@ package android.telecom { method public java.lang.String getReason(); method public int getTone(); method public void writeToParcel(android.os.Parcel, int); + field public static final int ANSWERED_ELSEWHERE = 11; // 0xb field public static final int BUSY = 7; // 0x7 + field public static final int CALL_PULLED = 12; // 0xc field public static final int CANCELED = 4; // 0x4 field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa field public static final android.os.Parcelable.Creator CREATOR; @@ -36374,6 +36523,7 @@ package android.telecom { method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onCallRemoved(android.telecom.Call); method public void onCanAddCallChanged(boolean); + method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle); method public void onSilenceRinger(); method public final void setAudioRoute(int); method public final void setMuted(boolean); @@ -36496,6 +36646,7 @@ package android.telecom { method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List); method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int); method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection); method public void onDestroyed(android.telecom.RemoteConference); method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause); @@ -36514,6 +36665,7 @@ package android.telecom { method public android.telecom.RemoteConference getConference(); method public java.util.List getConferenceableConnections(); method public int getConnectionCapabilities(); + method public int getConnectionProperties(); method public android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); method public int getState(); @@ -36525,6 +36677,7 @@ package android.telecom { method public boolean isVoipAudioMode(); method public void playDtmfTone(char); method public void postDialContinue(boolean); + method public void pullExternalCall(); method public void registerCallback(android.telecom.RemoteConnection.Callback); method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler); method public void reject(); @@ -36541,6 +36694,8 @@ package android.telecom { method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference); method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List); method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int); + method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle); + method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int); method public void onDestroyed(android.telecom.RemoteConnection); method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause); method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle); @@ -36637,6 +36792,7 @@ package android.telecom { field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE"; + field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING"; field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI"; field public static final int PRESENTATION_ALLOWED = 1; // 0x1 @@ -36691,9 +36847,11 @@ package android.telephony { field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; + field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; @@ -36725,6 +36883,7 @@ package android.telephony { field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; + field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool"; field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int"; field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; @@ -36782,6 +36941,7 @@ package android.telephony { field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; + field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool"; field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; @@ -37366,12 +37526,15 @@ package android.telephony { field public static final int NETWORK_TYPE_EVDO_A = 6; // 0x6 field public static final int NETWORK_TYPE_EVDO_B = 12; // 0xc field public static final int NETWORK_TYPE_GPRS = 1; // 0x1 + field public static final int NETWORK_TYPE_GSM = 16; // 0x10 field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8 field public static final int NETWORK_TYPE_HSPA = 10; // 0xa field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9 field public static final int NETWORK_TYPE_IDEN = 11; // 0xb + field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12 field public static final int NETWORK_TYPE_LTE = 13; // 0xd + field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11 field public static final int NETWORK_TYPE_UMTS = 3; // 0x3 field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0 field public static final int PHONE_TYPE_CDMA = 2; // 0x2 @@ -40174,7 +40337,10 @@ package android.util { method public boolean equals(android.util.DisplayMetrics); method public void setTo(android.util.DisplayMetrics); method public void setToDefaults(); + field public static final int DENSITY_260 = 260; // 0x104 field public static final int DENSITY_280 = 280; // 0x118 + field public static final int DENSITY_300 = 300; // 0x12c + field public static final int DENSITY_340 = 340; // 0x154 field public static final int DENSITY_360 = 360; // 0x168 field public static final int DENSITY_400 = 400; // 0x190 field public static final int DENSITY_420 = 420; // 0x1a4 @@ -41504,6 +41670,10 @@ package android.view { field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f field public static final int KEYCODE_SYM = 63; // 0x3f field public static final int KEYCODE_SYSRQ = 120; // 0x78 + field public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; // 0x119 + field public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; // 0x11a + field public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; // 0x11b + field public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; // 0x118 field public static final int KEYCODE_T = 48; // 0x30 field public static final int KEYCODE_TAB = 61; // 0x3d field public static final int KEYCODE_TV = 170; // 0xaa @@ -42386,6 +42556,7 @@ package android.view { method public float getPivotY(); method public android.view.PointerIcon getPointerIcon(); method public android.content.res.Resources getResources(); + method public final boolean getRevealOnFocusHint(); method public final int getRight(); method protected float getRightFadingEdgeStrength(); method protected int getRightPaddingOffset(); @@ -42673,6 +42844,7 @@ package android.view { method public void setPivotY(float); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); + method public final void setRevealOnFocusHint(boolean); method public final void setRight(int); method public void setRotation(float); method public void setRotationX(float); @@ -43822,6 +43994,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -44594,6 +44767,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -44703,6 +44877,7 @@ package android.view.inputmethod { field public static final int IME_NULL = 0; // 0x0 field public int actionId; field public java.lang.CharSequence actionLabel; + field public java.lang.String[] contentMimeTypes; field public android.os.Bundle extras; field public int fieldId; field public java.lang.String fieldName; @@ -44762,6 +44937,7 @@ package android.view.inputmethod { method public abstract boolean clearMetaKeyStates(int); method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public abstract boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); method public abstract boolean deleteSurroundingText(int, int); @@ -44787,6 +44963,7 @@ package android.view.inputmethod { field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1 field public static final int GET_TEXT_WITH_STYLES = 1; // 0x1 + field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1 } public class InputConnectionWrapper implements android.view.inputmethod.InputConnection { @@ -44795,6 +44972,7 @@ package android.view.inputmethod { method public boolean clearMetaKeyStates(int); method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); + method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); method public boolean deleteSurroundingText(int, int); @@ -44819,6 +44997,19 @@ package android.view.inputmethod { method public void setTarget(android.view.inputmethod.InputConnection); } + public final class InputContentInfo implements android.os.Parcelable { + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); + ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); + method public int describeContents(); + method public android.net.Uri getContentUri(); + method public android.content.ClipDescription getDescription(); + method public android.net.Uri getLinkUri(); + method public void releasePermission(); + method public void requestPermission(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public abstract interface InputMethod { method public abstract void attachToken(android.os.IBinder); method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 8ccd5d2ebcbadf28589b4f57f10bac98a4703f7e..d6c00589e7c2ab414b58d587d909cd36f001236d 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -723,10 +723,10 @@ public class Am extends BaseCommand { System.out.println("Complete"); } mRepeat--; - if (mRepeat > 1) { + if (mRepeat > 0) { mAm.unhandledBack(); } - } while (mRepeat > 1); + } while (mRepeat > 0); } private void runForceStop() throws Exception { diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 7f03eaf65e335b44e30639421ee6193e566a87dd..e9d1b6083b0184b9dc6e4740f95b3cae0dce5459 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -3,14 +3,16 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ - AudioPlayer.cpp \ + audioplay.cpp \ BootAnimation.cpp LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -LOCAL_C_INCLUDES += external/tinyalsa/include +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + frameworks/wilhelm/include LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -23,6 +25,7 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv1_CM \ libgui \ + libOpenSLES \ libtinyalsa \ libregionalization diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp deleted file mode 100644 index 293213008d582ca6d5048a1a585da9fb4f570d62..0000000000000000000000000000000000000000 --- a/cmds/bootanimation/AudioPlayer.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_NDEBUG 0 -#define LOG_TAG "BootAnim_AudioPlayer" - -#include "AudioPlayer.h" - -#include -#include -#include -#include - -#define ID_RIFF 0x46464952 -#define ID_WAVE 0x45564157 -#define ID_FMT 0x20746d66 -#define ID_DATA 0x61746164 - -// Maximum line length for audio_conf.txt -// We only accept lines less than this length to avoid overflows using sscanf() -#define MAX_LINE_LENGTH 1024 - -struct riff_wave_header { - uint32_t riff_id; - uint32_t riff_sz; - uint32_t wave_id; -}; - -struct chunk_header { - uint32_t id; - uint32_t sz; -}; - -struct chunk_fmt { - uint16_t audio_format; - uint16_t num_channels; - uint32_t sample_rate; - uint32_t byte_rate; - uint16_t block_align; - uint16_t bits_per_sample; -}; - - -namespace android { - -AudioPlayer::AudioPlayer() - : mCard(-1), - mDevice(-1), - mPeriodSize(0), - mPeriodCount(0), - mCurrentFile(NULL) -{ -} - -AudioPlayer::~AudioPlayer() { -} - -static bool setMixerValue(struct mixer* mixer, const char* name, const char* values) -{ - if (!mixer) { - ALOGE("no mixer in setMixerValue"); - return false; - } - struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name); - if (!ctl) { - ALOGE("mixer_get_ctl_by_name failed for %s", name); - return false; - } - - enum mixer_ctl_type type = mixer_ctl_get_type(ctl); - int numValues = mixer_ctl_get_num_values(ctl); - int intValue; - char stringValue[MAX_LINE_LENGTH]; - - for (int i = 0; i < numValues && values; i++) { - // strip leading space - while (*values == ' ') values++; - if (*values == 0) break; - - switch (type) { - case MIXER_CTL_TYPE_BOOL: - case MIXER_CTL_TYPE_INT: - if (sscanf(values, "%d", &intValue) == 1) { - if (mixer_ctl_set_value(ctl, i, intValue) != 0) { - ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue); - } - } else { - ALOGE("Could not parse %s as int for %s", values, name); - } - break; - case MIXER_CTL_TYPE_ENUM: - if (sscanf(values, "%s", stringValue) == 1) { - if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) { - ALOGE("mixer_ctl_set_enum_by_string failed for %s %s", name, stringValue); - } - } else { - ALOGE("Could not parse %s as enum for %s", values, name); - } - break; - default: - ALOGE("unsupported mixer type %d for %s", type, name); - break; - } - - values = strchr(values, ' '); - } - - return true; -} - - -/* - * Parse the audio configuration file. - * The file is named audio_conf.txt and must begin with the following header: - * - * card= - * device= - * period_size= - * period_count= - * - * This header is followed by zero or more mixer settings, each with the format: - * mixer "" = - * Since mixer names can contain spaces, the name must be enclosed in double quotes. - * The values in the value list can be integers, booleans (represented by 0 or 1) - * or strings for enum values. - */ -bool AudioPlayer::init(const char* config) -{ - int tempInt; - struct mixer* mixer = NULL; - char name[MAX_LINE_LENGTH]; - - for (;;) { - const char* endl = strstr(config, "\n"); - if (!endl) break; - String8 line(config, endl - config); - if (line.length() >= MAX_LINE_LENGTH) { - ALOGE("Line too long in audio_conf.txt"); - return false; - } - const char* l = line.string(); - - if (sscanf(l, "card=%d", &tempInt) == 1) { - ALOGD("card=%d", tempInt); - mCard = tempInt; - - mixer = mixer_open(mCard); - if (!mixer) { - ALOGE("could not open mixer for card %d", mCard); - return false; - } - } else if (sscanf(l, "device=%d", &tempInt) == 1) { - ALOGD("device=%d", tempInt); - mDevice = tempInt; - } else if (sscanf(l, "period_size=%d", &tempInt) == 1) { - ALOGD("period_size=%d", tempInt); - mPeriodSize = tempInt; - } else if (sscanf(l, "period_count=%d", &tempInt) == 1) { - ALOGD("period_count=%d", tempInt); - mPeriodCount = tempInt; - } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) { - const char* values = strchr(l, '='); - if (values) { - values++; // skip '=' - ALOGD("name: \"%s\" = %s", name, values); - setMixerValue(mixer, name, values); - } else { - ALOGE("values missing for name: \"%s\"", name); - } - } - config = ++endl; - } - - mixer_close(mixer); - - if (mCard >= 0 && mDevice >= 0) { - return true; - } - - return false; -} - -void AudioPlayer::playFile(FileMap* fileMap) { - // stop any currently playing sound - requestExitAndWait(); - - mCurrentFile = fileMap; - run("bootanim audio", PRIORITY_URGENT_AUDIO); -} - -bool AudioPlayer::threadLoop() -{ - struct pcm_config config; - struct pcm *pcm = NULL; - bool moreChunks = true; - const struct chunk_fmt* chunkFmt = NULL; - int bufferSize; - const uint8_t* wavData; - size_t wavLength; - const struct riff_wave_header* wavHeader; - - if (mCurrentFile == NULL) { - ALOGE("mCurrentFile is NULL"); - return false; - } - - wavData = (const uint8_t *)mCurrentFile->getDataPtr(); - if (!wavData) { - ALOGE("Could not access WAV file data"); - goto exit; - } - wavLength = mCurrentFile->getDataLength(); - - wavHeader = (const struct riff_wave_header *)wavData; - if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) || - (wavHeader->wave_id != ID_WAVE)) { - ALOGE("Error: audio file is not a riff/wave file\n"); - goto exit; - } - wavData += sizeof(*wavHeader); - wavLength -= sizeof(*wavHeader); - - do { - const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData; - if (wavLength < sizeof(*chunkHeader)) { - ALOGE("EOF reading chunk headers"); - goto exit; - } - - wavData += sizeof(*chunkHeader); - wavLength -= sizeof(*chunkHeader); - - switch (chunkHeader->id) { - case ID_FMT: - chunkFmt = (const struct chunk_fmt *)wavData; - wavData += chunkHeader->sz; - wavLength -= chunkHeader->sz; - break; - case ID_DATA: - /* Stop looking for chunks */ - moreChunks = 0; - break; - default: - /* Unknown chunk, skip bytes */ - wavData += chunkHeader->sz; - wavLength -= chunkHeader->sz; - } - } while (moreChunks); - - if (!chunkFmt) { - ALOGE("format not found in WAV file"); - goto exit; - } - - - memset(&config, 0, sizeof(config)); - config.channels = chunkFmt->num_channels; - config.rate = chunkFmt->sample_rate; - config.period_size = mPeriodSize; - config.period_count = mPeriodCount; - config.start_threshold = mPeriodSize / 4; - config.stop_threshold = INT_MAX; - config.avail_min = config.start_threshold; - if (chunkFmt->bits_per_sample != 16) { - ALOGE("only 16 bit WAV files are supported"); - goto exit; - } - config.format = PCM_FORMAT_S16_LE; - - pcm = pcm_open(mCard, mDevice, PCM_OUT, &config); - if (!pcm || !pcm_is_ready(pcm)) { - ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm)); - goto exit; - } - - bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); - - while (wavLength > 0) { - if (exitPending()) goto exit; - size_t count = bufferSize; - if (count > wavLength) - count = wavLength; - - if (pcm_write(pcm, wavData, count)) { - ALOGE("pcm_write failed (%s)", pcm_get_error(pcm)); - goto exit; - } - wavData += count; - wavLength -= count; - } - -exit: - if (pcm) - pcm_close(pcm); - delete mCurrentFile; - mCurrentFile = NULL; - return false; -} - -} // namespace android diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h deleted file mode 100644 index 1def0aeac8d12d28bee7aadfc10c296ca6bd971e..0000000000000000000000000000000000000000 --- a/cmds/bootanimation/AudioPlayer.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _BOOTANIMATION_AUDIOPLAYER_H -#define _BOOTANIMATION_AUDIOPLAYER_H - -#include -#include - -namespace android { - -class AudioPlayer : public Thread -{ -public: - AudioPlayer(); - virtual ~AudioPlayer(); - bool init(const char* config); - - void playFile(FileMap* fileMap); - -private: - virtual bool threadLoop(); - -private: - int mCard; // ALSA card to use - int mDevice; // ALSA device to use - int mPeriodSize; - int mPeriodCount; - - FileMap* mCurrentFile; -}; - -} // namespace android - -#endif // _BOOTANIMATION_AUDIOPLAYER_H diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index b8bd60776688bb257aa3c46fdaa1f969dbc5d547..7fd6347130b8e75094d6ed896f8e819b6365fe07 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -18,6 +18,9 @@ #define LOG_TAG "BootAnimation" #include +#include +#include +#include #include #include #include @@ -56,7 +59,7 @@ #include #include "BootAnimation.h" -#include "AudioPlayer.h" +#include "audioplay.h" #include @@ -68,7 +71,29 @@ namespace android { +static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; +static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip"; +static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip"; +static const char SYSTEM_DATA_DIR_PATH[] = "/data/system"; +static const char SYSTEM_TIME_DIR_NAME[] = "time"; +static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time"; +static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change"; +static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change"; +static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate"; +static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate"; +// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00. +static const long long ACCURATE_TIME_EPOCH = 946684800000; +static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; +static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound"; static const int ANIM_ENTRY_NAME_MAX = 256; +static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed"; +static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason"; +// bootreasons list in "system/core/bootstat/bootstat.cpp". +static const std::vector PLAY_SOUND_BOOTREASON_BLACKLIST { + "kernel_panic", + "Panic", + "Watchdog", +}; // --------------------------------------------------------------------------- @@ -128,13 +153,16 @@ static unsigned long getFreeMemory(void) return numFound > 0 ? mem : -1; } -BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) { +BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), + mTimeCheckThread(NULL) { mSession = new SurfaceComposerClient(); -} -BootAnimation::~BootAnimation() { + // If the system has already booted, the animation is not being used for a boot. + mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0); } +BootAnimation::~BootAnimation() {} + void BootAnimation::onFirstRef() { status_t err = mSession->linkToComposerDeath(this); ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); @@ -157,9 +185,7 @@ void BootAnimation::binderDied(const wp&) // might be blocked on a condition variable that will never be updated. kill( getpid(), SIGKILL ); requestExit(); - if (mAudioPlayer != NULL) { - mAudioPlayer->requestExit(); - } + audioplay::destroy(); } status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, @@ -262,25 +288,25 @@ status_t BootAnimation::initTexture(SkBitmap *bitmap) switch (bitmap->colorType()) { case kN32_SkColorType: - if (tw != w || th != h) { + if (!mUseNpotTextures && (tw != w || th != h)) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); } break; case kRGB_565_SkColorType: - if (tw != w || th != h) { + if (!mUseNpotTextures && (tw != w || th != h)) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } break; @@ -518,9 +544,6 @@ void BootAnimation::checkExit() { int exitnow = atoi(value); if (exitnow) { requestExit(); - if (mAudioPlayer != NULL) { - mAudioPlayer->requestExit(); - } } } @@ -642,16 +665,6 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) } char const* s = desString.string(); - // Create and initialize an AudioPlayer if we have an audio_conf.txt file - String8 audioConf; - if (readFile(animation.zip, "audio_conf.txt", audioConf)) { - mAudioPlayer = new AudioPlayer; - if (!mAudioPlayer->init(audioConf.string())) { - ALOGE("mAudioPlayer.init failed"); - mAudioPlayer = NULL; - } - } - // Parse the description file for (;;) { const char* endl = strstr(s, "\n"); @@ -682,7 +695,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) part.pause = pause; part.path = path; part.clockPosY = clockPosY; - part.audioFile = NULL; + part.audioData = NULL; part.animation = NULL; if (!parseColor(color, part.backgroundColor)) { ALOGE("> invalid color '#%s'", color); @@ -698,7 +711,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) part.playUntilComplete = false; part.count = 1; part.pause = 0; - part.audioFile = NULL; + part.audioData = NULL; part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE)); if (part.animation != NULL) animation.parts.add(part); @@ -714,15 +727,16 @@ bool BootAnimation::preloadZip(Animation& animation) // read all the data structures const size_t pcount = animation.parts.size(); void *cookie = NULL; - ZipFileRO* mZip = animation.zip; - if (!mZip->startIteration(&cookie)) { + ZipFileRO* zip = animation.zip; + if (!zip->startIteration(&cookie)) { return false; } + Animation::Part* partWithAudio = NULL; ZipEntryRO entry; char name[ANIM_ENTRY_NAME_MAX]; - while ((entry = mZip->nextEntry(cookie)) != NULL) { - const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); + while ((entry = zip->nextEntry(cookie)) != NULL) { + const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) { ALOGE("Error fetching entry file name"); continue; @@ -732,25 +746,36 @@ bool BootAnimation::preloadZip(Animation& animation) const String8 path(entryName.getPathDir()); const String8 leaf(entryName.getPathLeaf()); if (leaf.size() > 0) { - for (size_t j=0 ; jgetEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { + if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { if (method == ZipFileRO::kCompressStored) { - FileMap* map = mZip->createEntryFileMap(entry); + FileMap* map = zip->createEntryFileMap(entry); if (map) { Animation::Part& part(animation.parts.editItemAt(j)); if (leaf == "audio.wav") { // a part may have at most one audio file - part.audioFile = map; + part.audioData = (uint8_t *)map->getDataPtr(); + part.audioLength = map->getDataLength(); + partWithAudio = ∂ + } else if (leaf == "trim.txt") { + part.trimData.setTo((char const*)map->getDataPtr(), + map->getDataLength()); } else { Animation::Frame frame; frame.name = leaf; frame.map = map; + frame.trimWidth = animation.width; + frame.trimHeight = animation.height; + frame.trimX = 0; + frame.trimY = 0; part.frames.add(frame); } } + } else { + ALOGE("bootanimation.zip is compressed; must be only stored"); } } } @@ -758,18 +783,76 @@ bool BootAnimation::preloadZip(Animation& animation) } } - mZip->endIteration(cookie); + // If there is trimData present, override the positioning defaults. + for (Animation::Part& part : animation.parts) { + const char* trimDataStr = part.trimData.string(); + for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) { + const char* endl = strstr(trimDataStr, "\n"); + // No more trimData for this part. + if (endl == NULL) { + break; + } + String8 line(trimDataStr, endl - trimDataStr); + const char* lineStr = line.string(); + trimDataStr = ++endl; + int width = 0, height = 0, x = 0, y = 0; + if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) { + Animation::Frame& frame(part.frames.editItemAt(frameIdx)); + frame.trimWidth = width; + frame.trimHeight = height; + frame.trimX = x; + frame.trimY = y; + } else { + ALOGE("Error parsing trim.txt, line: %s", lineStr); + break; + } + } + } + + // Create and initialize audioplay if there is a wav file in any of the animations. + if (partWithAudio != NULL) { + ALOGD("found audio.wav, creating playback engine"); + if (!audioplay::create(partWithAudio->audioData, partWithAudio->audioLength)) { + return false; + } + } + + zip->endIteration(cookie); return true; } bool BootAnimation::movie() { - Animation* animation = loadAnimation(mZipFileName); if (animation == NULL) return false; + bool anyPartHasClock = false; + for (size_t i=0; i < animation->parts.size(); i++) { + if(animation->parts[i].clockPosY >= 0) { + anyPartHasClock = true; + break; + } + } + if (!anyPartHasClock) { + mClockEnabled = false; + } + + // Check if npot textures are supported + mUseNpotTextures = false; + String8 gl_extensions; + const char* exts = reinterpret_cast(glGetString(GL_EXTENSIONS)); + if (!exts) { + glGetError(); + } else { + gl_extensions.setTo(exts); + if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) || + (gl_extensions.find("GL_OES_texture_npot") != -1)) { + mUseNpotTextures = true; + } + } + // Blend required to draw time on top of animation frames. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glShadeModel(GL_FLAT); @@ -791,7 +874,18 @@ bool BootAnimation::movie() mClockEnabled = clockTextureInitialized; } + if (mClockEnabled && !updateIsTimeAccurate()) { + mTimeCheckThread = new TimeCheckThread(this); + mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL); + } + playAnimation(*animation); + + if (mTimeCheckThread != NULL) { + mTimeCheckThread->requestExit(); + mTimeCheckThread = NULL; + } + releaseAnimation(animation); if (clockTextureInitialized) { @@ -804,12 +898,9 @@ bool BootAnimation::movie() bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); - const int xc = (mWidth - animation.width) / 2; - const int yc = ((mHeight - animation.height) / 2); nsecs_t frameDuration = s2ns(1) / animation.fps; - - Region clearReg(Rect(mWidth, mHeight)); - clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); + const int animationX = (mWidth - animation.width) / 2; + const int animationY = (mHeight - animation.height) / 2; for (size_t i=0 ; iplayFile(part.audioFile); + if (r == 0 && part.audioData && playSoundsAllowed()) { + ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength); + audioplay::playClip(part.audioData, part.audioLength); } glClearColor( @@ -894,23 +986,26 @@ bool BootAnimation::playAnimation(const Animation& animation) #endif } + const int xc = animationX + frame.trimX; + const int yc = animationY + frame.trimY; + Region clearReg(Rect(mWidth, mHeight)); + clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight)); if (!clearReg.isEmpty()) { Region::const_iterator head(clearReg.begin()); Region::const_iterator tail(clearReg.end()); glEnable(GL_SCISSOR_TEST); while (head != tail) { const Rect& r2(*head++); - glScissor(r2.left, mHeight - r2.bottom, - r2.width(), r2.height()); + glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height()); glClear(GL_COLOR_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); } - // specify the y center as ceiling((mHeight - animation.height) / 2) - // which is equivalent to mHeight - (yc + animation.height) - glDrawTexiOES(xc, mHeight - (yc + animation.height), - 0, animation.width, animation.height); - if (mClockEnabled && part.clockPosY >= 0) { + // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) + // which is equivalent to mHeight - (yc + frame.trimHeight) + glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight), + 0, frame.trimWidth, frame.trimHeight); + if (mClockEnabled && mTimeIsAccurate && part.clockPosY >= 0) { drawTime(mClock, part.clockPosY); } @@ -947,9 +1042,13 @@ bool BootAnimation::playAnimation(const Animation& animation) break; } - // free the textures for this part - if (!needSaveMem && part.count != 1) { - for (size_t j=0 ; jfileName); parseAnimationDesc(*animation); - preloadZip(*animation); + if (!preloadZip(*animation)) { + return NULL; + } + mLoadedFiles.remove(fn); return animation; @@ -1111,6 +1218,156 @@ bool FrameManager::DecodeThread::threadLoop() } #endif +bool BootAnimation::playSoundsAllowed() const { + // Only play sounds for system boots, not runtime restarts. + if (!mSystemBoot) { + return false; + } + + // Read the system property to see if we should play the sound. + // If it's not present, default to allowed. + if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) { + return false; + } + + // Don't play sounds if this is a reboot due to an error. + char bootreason[PROPERTY_VALUE_MAX]; + if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) { + for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) { + if (strcasecmp(str.c_str(), bootreason) == 0) { + return false; + } + } + } + return true; +} + +bool BootAnimation::updateIsTimeAccurate() { + static constexpr long long MAX_TIME_IN_PAST = 60000LL * 60LL * 24LL * 30LL; // 30 days + static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL; // 90 minutes + + if (mTimeIsAccurate) { + return true; + } + + struct stat statResult; + if(stat(ACCURATE_TIME_FLAG_FILE_PATH, &statResult) == 0) { + mTimeIsAccurate = true; + return true; + } + + FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r"); + if (file != NULL) { + long long lastChangedTime = 0; + fscanf(file, "%lld", &lastChangedTime); + fclose(file); + if (lastChangedTime > 0) { + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + // Match the Java timestamp format + long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL); + if (ACCURATE_TIME_EPOCH < rtcNow + && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST) + && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) { + mTimeIsAccurate = true; + } + } + } + + return mTimeIsAccurate; +} + +BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false), + mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {} + +BootAnimation::TimeCheckThread::~TimeCheckThread() { + // mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD. + close(mInotifyFd); +} + +bool BootAnimation::TimeCheckThread::threadLoop() { + bool shouldLoop = doThreadLoop() && !mBootAnimation->mTimeIsAccurate + && mBootAnimation->mClockEnabled; + if (!shouldLoop) { + close(mInotifyFd); + mInotifyFd = -1; + } + return shouldLoop; +} + +bool BootAnimation::TimeCheckThread::doThreadLoop() { + static constexpr int BUFF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1)); + + // Poll instead of doing a blocking read so the Thread can exit if requested. + struct pollfd pfd = { mInotifyFd, POLLIN, 0 }; + ssize_t pollResult = poll(&pfd, 1, 1000); + + if (pollResult == 0) { + return true; + } else if (pollResult < 0) { + ALOGE("Could not poll inotify events"); + return false; + } + + char buff[BUFF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));; + ssize_t length = read(mInotifyFd, buff, BUFF_LEN); + if (length == 0) { + return true; + } else if (length < 0) { + ALOGE("Could not read inotify events"); + return false; + } + + const struct inotify_event *event; + for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) { + event = (const struct inotify_event *) ptr; + if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) { + addTimeDirWatch(); + } else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0 + || strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) { + return !mBootAnimation->updateIsTimeAccurate(); + } + } + + return true; +} + +void BootAnimation::TimeCheckThread::addTimeDirWatch() { + mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH, + IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB); + if (mTimeWd > 0) { + // No need to watch for the time directory to be created if it already exists + inotify_rm_watch(mInotifyFd, mSystemWd); + mSystemWd = -1; + } +} + +status_t BootAnimation::TimeCheckThread::readyToRun() { + mInotifyFd = inotify_init(); + if (mInotifyFd < 0) { + ALOGE("Could not initialize inotify fd"); + return NO_INIT; + } + + mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB); + if (mSystemWd < 0) { + close(mInotifyFd); + mInotifyFd = -1; + ALOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH); + return NO_INIT; + } + + addTimeDirWatch(); + + if (mBootAnimation->updateIsTimeAccurate()) { + close(mInotifyFd); + mInotifyFd = -1; + return ALREADY_EXISTS; + } + + return NO_ERROR; +} + // --------------------------------------------------------------------------- } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 5856e9e7433b43b1e133cc74837c94eb7c5749b4..b70cc0eed2675df26f5e4675d8a93e53dc809d5d 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -32,7 +32,6 @@ class SkBitmap; namespace android { -class AudioPlayer; class Surface; class SurfaceComposerClient; class SurfaceControl; @@ -59,6 +58,24 @@ private: virtual void onFirstRef(); virtual void binderDied(const wp& who); + bool updateIsTimeAccurate(); + + class TimeCheckThread : public Thread { + public: + TimeCheckThread(BootAnimation* bootAnimation); + virtual ~TimeCheckThread(); + private: + virtual status_t readyToRun(); + virtual bool threadLoop(); + bool doThreadLoop(); + void addTimeDirWatch(); + + int mInotifyFd; + int mSystemWd; + int mTimeWd; + BootAnimation* mBootAnimation; + }; + struct Texture { GLint w; GLint h; @@ -69,6 +86,10 @@ private: struct Frame { String8 name; FileMap* map; + int trimX; + int trimY; + int trimWidth; + int trimHeight; mutable GLuint tid; bool operator < (const Frame& rhs) const { return name < rhs.name; @@ -80,10 +101,12 @@ private: int clockPosY; // The y position of the clock, in pixels, from the bottom of the // display (the clock is centred horizontally). -1 to disable the clock String8 path; + String8 trimData; SortedVector frames; bool playUntilComplete; float backgroundColor[3]; - FileMap* audioFile; + uint8_t* audioData; + int audioLength; Animation* animation; }; int fps; @@ -113,26 +136,30 @@ private: void releaseAnimation(Animation*) const; bool parseAnimationDesc(Animation&); bool preloadZip(Animation &animation); + bool playSoundsAllowed() const; void checkExit(); static SkBitmap *decode(const Animation::Frame& frame); sp mSession; - sp mAudioPlayer; AssetManager mAssets; Texture mAndroid[2]; Texture mClock; int mWidth; int mHeight; + bool mUseNpotTextures = false; EGLDisplay mDisplay; EGLDisplay mContext; EGLDisplay mSurface; sp mFlingerSurfaceControl; sp mFlingerSurface; bool mClockEnabled; + bool mTimeIsAccurate; + bool mSystemBoot; String8 mZipFileName; SortedVector mLoadedFiles; + sp mTimeCheckThread; }; #ifdef MULTITHREAD_DECODE diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md new file mode 100644 index 0000000000000000000000000000000000000000..9ea6fea966f2521489c4abade4c1d1b3e3192fc4 --- /dev/null +++ b/cmds/bootanimation/FORMAT.md @@ -0,0 +1,101 @@ +# bootanimation format + +## zipfile paths + +The system selects a boot animation zipfile from the following locations, in order: + + /system/media/bootanimation-encrypted.zip (if getprop("vold.decrypt") = '1') + /system/media/bootanimation.zip + /oem/media/bootanimation.zip + +## zipfile layout + +The `bootanimation.zip` archive file includes: + + desc.txt - a text file + part0 \ + part1 \ directories full of PNG frames + ... / + partN / + +## desc.txt format + +The first line defines the general parameters of the animation: + + WIDTH HEIGHT FPS + + * **WIDTH:** animation width (pixels) + * **HEIGHT:** animation height (pixels) + * **FPS:** frames per second, e.g. 60 + +It is followed by a number of rows of the form: + + TYPE COUNT PAUSE PATH [#RGBHEX CLOCK] + + * **TYPE:** a single char indicating what type of animation segment this is: + + `p` -- this part will play unless interrupted by the end of the boot + + `c` -- this part will play to completion, no matter what + * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete + * **PAUSE:** number of FRAMES to delay after this part ends + * **PATH:** directory in which to find the frames for this part (e.g. `part0`) + * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB` + * **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches) + +There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip` +and plays that. + +## loading and playing frames + +Each part is scanned and loaded directly from the zip archive. Within a part directory, every file +(except `trim.txt` and `audio.wav`; see next sections) is expected to be a PNG file that represents +one frame in that part (at the specified resolution). For this reason it is important that frames be +named sequentially (e.g. `part000.png`, `part001.png`, ...) and added to the zip archive in that +order. + +## trim.txt + +To save on memory, textures may be trimmed by their background color. trim.txt sequentially lists +the trim output for each frame in its directory, so the frames may be properly positioned. +Output should be of the form: `WxH+X+Y`. Example: + + 713x165+388+914 + 708x152+388+912 + 707x139+388+911 + 649x92+388+910 + +If the file is not present, each frame is assumed to be the same size as the animation. + +## audio.wav + +Each part may optionally play a `wav` sample when it starts. To enable this, add a file +with the name `audio.wav` in the part directory. + +## exiting + +The system will end the boot animation (first completing any incomplete or even entirely unplayed +parts that are of type `c`) when the system is finished booting. (This is accomplished by setting +the system property `service.bootanim.exit` to a nonzero string.) + +## protips + +### PNG compression + +Use `zopflipng` if you have it, otherwise `pngcrush` will do. e.g.: + + for fn in *.png ; do + zopflipng -m ${fn}s ${fn}s.new && mv -f ${fn}s.new ${fn} + # or: pngcrush -q .... + done + +Some animations benefit from being reduced to 256 colors: + + pngquant --force --ext .png *.png + # alternatively: mogrify -colors 256 anim-tmp/*/*.png + +### creating the ZIP archive + + cd + zip -0qry -i \*.txt \*.png \*.wav @ ../bootanimation.zip *.txt part* + +Note that the ZIP archive is not actually compressed! The PNG files are already as compressed +as they can reasonably get, and there is unlikely to be any redundancy between files. diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4983b9ac4236e65f28d00c41e18395ddfa41b544 --- /dev/null +++ b/cmds/bootanimation/audioplay.cpp @@ -0,0 +1,386 @@ +/* + * 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. + * + */ + +// cribbed from samples/native-audio + +#include "audioplay.h" + +#define CHATTY ALOGD +#define LOG_TAG "audioplay" + +#include + +#include + +// for native audio +#include +#include + +namespace audioplay { +namespace { + +// engine interfaces +static SLObjectItf engineObject = NULL; +static SLEngineItf engineEngine; + +// output mix interfaces +static SLObjectItf outputMixObject = NULL; + +// buffer queue player interfaces +static SLObjectItf bqPlayerObject = NULL; +static SLPlayItf bqPlayerPlay; +static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; +static SLMuteSoloItf bqPlayerMuteSolo; +static SLVolumeItf bqPlayerVolume; + +// pointer and size of the next player buffer to enqueue, and number of remaining buffers +static const uint8_t* nextBuffer; +static unsigned nextSize; + +static const uint32_t ID_RIFF = 0x46464952; +static const uint32_t ID_WAVE = 0x45564157; +static const uint32_t ID_FMT = 0x20746d66; +static const uint32_t ID_DATA = 0x61746164; + +struct RiffWaveHeader { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t wave_id; +}; + +struct ChunkHeader { + uint32_t id; + uint32_t sz; +}; + +struct ChunkFormat { + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; +}; + +// this callback handler is called every time a buffer finishes playing +void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { + (void)bq; + (void)context; + audioplay::setPlaying(false); +} + +bool hasPlayer() { + return (engineObject != NULL && bqPlayerObject != NULL); +} + +// create the engine and output mix objects +bool createEngine() { + SLresult result; + + // create engine + result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); + if (result != SL_RESULT_SUCCESS) { + ALOGE("slCreateEngine failed with result %d", result); + return false; + } + (void)result; + + // realize the engine + result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl engine Realize failed with result %d", result); + return false; + } + (void)result; + + // get the engine interface, which is needed in order to create other objects + result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl engine GetInterface failed with result %d", result); + return false; + } + (void)result; + + // create output mix + result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl engine CreateOutputMix failed with result %d", result); + return false; + } + (void)result; + + // realize the output mix + result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl outputMix Realize failed with result %d", result); + return false; + } + (void)result; + + return true; +} + +// create buffer queue audio player +bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) { + SLresult result; + + // configure audio source + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1}; + + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, + chunkFormat->num_channels, + chunkFormat->sample_rate * 1000, // convert to milliHz + chunkFormat->bits_per_sample, + 16, + SL_SPEAKER_FRONT_CENTER, + SL_BYTEORDER_LITTLEENDIAN + }; + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + // configure audio sink + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; + SLDataSink audioSnk = {&loc_outmix, NULL}; + + // create audio player + const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION}; + const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, + 3, ids, req); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl CreateAudioPlayer failed with result %d", result); + return false; + } + (void)result; + + // Use the System stream for boot sound playback. + SLAndroidConfigurationItf playerConfig; + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, + SL_IID_ANDROIDCONFIGURATION, &playerConfig); + if (result != SL_RESULT_SUCCESS) { + ALOGE("config GetInterface failed with result %d", result); + return false; + } + SLint32 streamType = SL_ANDROID_STREAM_SYSTEM; + result = (*playerConfig)->SetConfiguration(playerConfig, + SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); + if (result != SL_RESULT_SUCCESS) { + ALOGE("SetConfiguration failed with result %d", result); + return false; + } + // use normal performance mode as low latency is not needed. This is not mandatory so + // do not bail if we fail + SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE; + result = (*playerConfig)->SetConfiguration( + playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32)); + ALOGW_IF(result != SL_RESULT_SUCCESS, + "could not set performance mode on player, error %d", result); + (void)result; + + // realize the player + result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl player Realize failed with result %d", result); + return false; + } + (void)result; + + // get the play interface + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl player GetInterface failed with result %d", result); + return false; + } + (void)result; + + // get the buffer queue interface + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, + &bqPlayerBufferQueue); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl playberBufferQueue GetInterface failed with result %d", result); + return false; + } + (void)result; + + // register callback on the buffer queue + result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result); + return false; + } + (void)result; + + // get the volume interface + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); + if (result != SL_RESULT_SUCCESS) { + ALOGE("sl volume GetInterface failed with result %d", result); + return false; + } + (void)result; + + // set the player's state to playing + audioplay::setPlaying(true); + CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue); + return true; +} + +bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat, + const uint8_t** oSoundBuf, unsigned* oSoundBufSize) { + *oSoundBuf = clipBuf; + *oSoundBufSize = clipBufSize; + *oChunkFormat = NULL; + const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf; + if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) || + (wavHeader->wave_id != ID_WAVE)) { + ALOGE("Error: audio file is not a riff/wave file\n"); + return false; + } + *oSoundBuf += sizeof(*wavHeader); + *oSoundBufSize -= sizeof(*wavHeader); + + while (true) { + const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf; + if (*oSoundBufSize < sizeof(*chunkHeader)) { + ALOGE("EOF reading chunk headers"); + return false; + } + + *oSoundBuf += sizeof(*chunkHeader); + *oSoundBufSize -= sizeof(*chunkHeader); + + bool endLoop = false; + switch (chunkHeader->id) { + case ID_FMT: + *oChunkFormat = (const ChunkFormat*)*oSoundBuf; + *oSoundBuf += chunkHeader->sz; + *oSoundBufSize -= chunkHeader->sz; + break; + case ID_DATA: + /* Stop looking for chunks */ + *oSoundBufSize = chunkHeader->sz; + endLoop = true; + break; + default: + /* Unknown chunk, skip bytes */ + *oSoundBuf += chunkHeader->sz; + *oSoundBufSize -= chunkHeader->sz; + } + if (endLoop) { + break; + } + } + + if (*oChunkFormat == NULL) { + ALOGE("format not found in WAV file"); + return false; + } + return true; +} + +} // namespace + +bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) { + if (!createEngine()) { + return false; + } + + // Parse the example clip. + const ChunkFormat* chunkFormat; + const uint8_t* soundBuf; + unsigned soundBufSize; + if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) { + return false; + } + + // Initialize the BufferQueue based on this clip's format. + if (!createBufferQueueAudioPlayer(chunkFormat)) { + return false; + } + return true; +} + +bool playClip(const uint8_t* buf, int size) { + // Parse the WAV header + const ChunkFormat* chunkFormat; + if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) { + return false; + } + + if (!hasPlayer()) { + ALOGD("cannot play clip %p without a player", buf); + return false; + } + + CHATTY("playClip on player %p: buf=%p size=%d nextSize %d", + bqPlayerBufferQueue, buf, size, nextSize); + + if (nextSize > 0) { + // here we only enqueue one buffer because it is a long clip, + // but for streaming playback we would typically enqueue at least 2 buffers to start + SLresult result; + result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize); + if (SL_RESULT_SUCCESS != result) { + return false; + } + audioplay::setPlaying(true); + } + + return true; +} + +// set the playing state for the buffer queue audio player +void setPlaying(bool isPlaying) { + if (!hasPlayer()) return; + + SLresult result; + + if (NULL != bqPlayerPlay) { + // set the player's state + result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, + isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED); + } + +} + +void destroy() { + // destroy buffer queue audio player object, and invalidate all associated interfaces + if (bqPlayerObject != NULL) { + CHATTY("destroying audio player"); + (*bqPlayerObject)->Destroy(bqPlayerObject); + bqPlayerObject = NULL; + bqPlayerPlay = NULL; + bqPlayerBufferQueue = NULL; + bqPlayerMuteSolo = NULL; + bqPlayerVolume = NULL; + } + + // destroy output mix object, and invalidate all associated interfaces + if (outputMixObject != NULL) { + (*outputMixObject)->Destroy(outputMixObject); + outputMixObject = NULL; + } + + // destroy engine object, and invalidate all associated interfaces + if (engineObject != NULL) { + CHATTY("destroying audio engine"); + (*engineObject)->Destroy(engineObject); + engineObject = NULL; + engineEngine = NULL; + } +} + +} // namespace audioplay diff --git a/cmds/bootanimation/audioplay.h b/cmds/bootanimation/audioplay.h new file mode 100644 index 0000000000000000000000000000000000000000..0e5705af0ad03513d0dc800790c5b2764e1828a7 --- /dev/null +++ b/cmds/bootanimation/audioplay.h @@ -0,0 +1,37 @@ +/* + * 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. + * + */ + +#ifndef AUDIOPLAY_H_ +#define AUDIOPLAY_H_ + +#include + +namespace audioplay { + +// Initializes the engine with an example of the type of WAV clip to play. +// All buffers passed to playClip are assumed to be in the same format. +bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize); + +// Plays a WAV contained in buf. +// Should not be called while a clip is still playing. +bool playClip(const uint8_t* buf, int size); +void setPlaying(bool isPlaying); +void destroy(); + +} + +#endif // AUDIOPLAY_H_ diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index ee0d0b8c042f199082d285611b11812456a986f4..7344ba74f70b643fa0fefab4551fa3ce87f825dd 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -4,3 +4,4 @@ service bootanim /system/bin/bootanimation group graphics audio disabled oneshot + writepid /dev/stune/top-app/tasks \ No newline at end of file diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index c6834f940554797962ef6c5b6159f8b53c2f9fc7..32a8088e9c4e618d3a313633e5fee3db1b855b92 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -922,6 +922,8 @@ public final class Pm { flags |= UserInfo.FLAG_EPHEMERAL; } else if ("--guest".equals(opt)) { flags |= UserInfo.FLAG_GUEST; + } else if ("--demo".equals(opt)) { + flags |= UserInfo.FLAG_DEMO; } else { System.err.println("Error: unknown option " + opt); return showUsage(); diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 7841d29b5700dc9f59897121394040826e5290dd..053ba7d5fa0ad276c10c5641d2a716eaebab7e23 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1064,15 +1064,15 @@ public final class AnimatorSet extends Animator { /** * @hide * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order - * if defined (i.e. sequential or together), then we can use the flag instead of calculate - * dynamically. + * if defined (i.e. sequential or together), then we can use the flag instead of calculating + * dynamically. Note that when AnimatorSet is empty this method returns true. * @return whether all the animators in the set are supposed to play together */ public boolean shouldPlayTogether() { updateAnimatorsDuration(); createDependencyGraph(); // All the child nodes are set out to play right after the delay animation - return mRootNode.mChildNodes.size() == mNodes.size() - 1; + return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1; } @Override diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 5c4b979ccb3cb1159634758feb2b1778fe3a82c5..9a2aa302a4bafe34435e97257da1c1a11b9c76f2 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -977,7 +977,7 @@ public final class ObjectAnimator extends ValueAnimator { @Override void animateValue(float fraction) { final Object target = getTarget(); - if (mTarget != null && target == null) { + if (target == null) { // We lost the target reference, cancel and clean up. cancel(); return; diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 224823ee2ff11afc9aef9d9876a25716c22667de..ba16e673ea6991f0f5b6a6a73163cf5655a01f9e 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -1095,8 +1095,12 @@ public class PropertyValuesHolder implements Cloneable { } // TODO: We need a better way to get data out of keyframes. if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase - || mKeyframes instanceof PathKeyframes.IntKeyframesBase) { - // property values will animate based on external data source (e.g. Path) + || mKeyframes instanceof PathKeyframes.IntKeyframesBase + || (mKeyframes.getKeyframes() != null && mKeyframes.getKeyframes().size() > 2)) { + // When a pvh has more than 2 keyframes, that means there are intermediate values in + // addition to start/end values defined for animators. Another case where such + // intermediate values are defined is when animator has a path to animate along. In + // these cases, a data source is needed to capture these intermediate values. values.dataSource = new PropertyValues.DataSource() { @Override public Object getValueAtFraction(float fraction) { @@ -1108,6 +1112,13 @@ public class PropertyValuesHolder implements Cloneable { } } + /** + * @hide + */ + public Class getValueType() { + return mValueType; + } + @Override public String toString() { return mPropertyName + ": " + mKeyframes.toString(); @@ -1177,6 +1188,15 @@ public class PropertyValuesHolder implements Cloneable { } } + @Override + public void setProperty(Property property) { + if (property instanceof IntProperty) { + mIntProperty = (IntProperty) property; + } else { + super.setProperty(property); + } + } + @Override public void setIntValues(int... values) { super.setIntValues(values); @@ -1315,6 +1335,15 @@ public class PropertyValuesHolder implements Cloneable { } } + @Override + public void setProperty(Property property) { + if (property instanceof FloatProperty) { + mFloatProperty = (FloatProperty) property; + } else { + super.setProperty(property); + } + } + @Override public void setFloatValues(float... values) { super.setFloatValues(values); @@ -1516,7 +1545,7 @@ public class PropertyValuesHolder implements Cloneable { } propertyMap.put(mPropertyName, mJniSetter); } - } + } } } diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 0c7ee2c8c2fec4453a325748e499121a0d0ab3bf..e3f8fa49f9dde472486438a967286d5ee92c68da 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -982,6 +982,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio mStarted = true; mPaused = false; mRunning = false; + mAnimationEndRequested = false; // Resets mLastFrameTime when start() is called, so that if the animation was running, // calling start() would put the animation in the // started-but-not-yet-reached-the-first-frame phase. diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ae3e0ce201a4856904827f7a104dd85d3898e54d..e4880b0f6a43b817c18585a1492c23b9249b8058 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -175,11 +175,11 @@ import java.util.List; * part of the platform's application model. For a detailed perspective on the structure of an * Android application and how activities behave, please read the * Application Fundamentals and - * Tasks and Back Stack + * Tasks and Back Stack * developer guides.

* *

You can also find a detailed discussion about how to create activities in the - * Activities + * Activities * developer guide.

* * @@ -3366,7 +3366,7 @@ public class Activity extends ContextThemeWrapper * should override the method {@link #onPrepareNavigateUpTaskStack(TaskStackBuilder)} * to supply those arguments.

* - *

See Tasks and Back Stack + *

See Tasks and Back Stack * from the developer guide and Navigation * from the design guide for more information about navigating within your app.

* @@ -4220,6 +4220,7 @@ public class Activity extends ContextThemeWrapper public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, @@ -4268,6 +4269,17 @@ public class Activity extends ContextThemeWrapper } } + private Bundle transferSpringboardActivityOptions(Bundle options) { + if (options == null && (mWindow != null && !mWindow.isActive())) { + final ActivityOptions activityOptions = getActivityOptions(); + if (activityOptions != null && + activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + return activityOptions.toBundle(); + } + } + return options; + } + /** * @hide Implement to provide correct calling token. */ @@ -4283,6 +4295,7 @@ public class Activity extends ContextThemeWrapper if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options, user); @@ -4318,6 +4331,7 @@ public class Activity extends ContextThemeWrapper if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, @@ -4350,6 +4364,7 @@ public class Activity extends ContextThemeWrapper if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivityAsCaller( this, mMainThread.getApplicationThread(), mToken, this, @@ -4467,7 +4482,7 @@ public class Activity extends ContextThemeWrapper * * @throws android.content.ActivityNotFoundException * - * @see {@link #startActivity(Intent, Bundle)} + * @see #startActivity(Intent, Bundle) * @see #startActivityForResult */ @Override @@ -4494,7 +4509,7 @@ public class Activity extends ContextThemeWrapper * * @throws android.content.ActivityNotFoundException * - * @see {@link #startActivity(Intent)} + * @see #startActivity(Intent) * @see #startActivityForResult */ @Override @@ -4516,7 +4531,7 @@ public class Activity extends ContextThemeWrapper * * @throws android.content.ActivityNotFoundException * - * @see {@link #startActivities(Intent[], Bundle)} + * @see #startActivities(Intent[], Bundle) * @see #startActivityForResult */ @Override @@ -4543,7 +4558,7 @@ public class Activity extends ContextThemeWrapper * * @throws android.content.ActivityNotFoundException * - * @see {@link #startActivities(Intent[])} + * @see #startActivities(Intent[]) * @see #startActivityForResult */ @Override @@ -4789,6 +4804,7 @@ public class Activity extends ContextThemeWrapper */ public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, child, @@ -4854,6 +4870,7 @@ public class Activity extends ContextThemeWrapper if (referrer != null) { intent.putExtra(Intent.EXTRA_REFERRER, referrer); } + options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, who, @@ -4923,7 +4940,7 @@ public class Activity extends ContextThemeWrapper *

As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative * to using this with starting activities is to supply the desired animation * information through a {@link ActivityOptions} bundle to - * {@link #startActivity(Intent, Bundle) or a related function. This allows + * {@link #startActivity(Intent, Bundle)} or a related function. This allows * you to specify a custom animation even when starting an activity from * outside the context of the current top activity. * @@ -6105,7 +6122,6 @@ public class Activity extends ContextThemeWrapper * the return value must be checked. * * @see #onVisibleBehindCanceled() - * @see #onBackgroundVisibleBehindChanged(boolean) */ public boolean requestVisibleBehind(boolean visible) { if (!mResumed) { @@ -6133,7 +6149,6 @@ public class Activity extends ContextThemeWrapper * process. Otherwise {@link #onStop()} will be called following return. * * @see #requestVisibleBehind(boolean) - * @see #onBackgroundVisibleBehindChanged(boolean) */ @CallSuper public void onVisibleBehindCanceled() { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 3f3a2000a10e3a87d0d2ac3e20dcc3b93dc51a58..b0e6dadbcb967fe9ff0216af4686843412603b15 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -93,7 +93,8 @@ public class ActivityManager { @IntDef({ BUGREPORT_OPTION_FULL, BUGREPORT_OPTION_INTERACTIVE, - BUGREPORT_OPTION_REMOTE + BUGREPORT_OPTION_REMOTE, + BUGREPORT_OPTION_WEAR }) public @interface BugreportMode {} /** @@ -114,6 +115,11 @@ public class ActivityManager { * @hide */ public static final int BUGREPORT_OPTION_REMOTE = 2; + /** + * Takes a bugreport on a wearable device. + * @hide + */ + public static final int BUGREPORT_OPTION_WEAR = 3; /** * {@code @@ -3633,6 +3639,24 @@ public class ActivityManager { } } + /** + * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads. Only one + * thread can be a VR thread in a process at a time, and that thread may be subject to + * restrictions on the amount of time it can run. + * + * To reset the VR thread for an application, a tid of 0 can be passed. + * + * @see android.os.Process#myTid() + * @param tid tid of the VR thread + */ + public static void setVrThread(int tid) { + try { + ActivityManagerNative.getDefault().setVrThread(tid); + } catch (RemoteException e) { + // pass + } + } + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 3a70a4cd4b99d8bc53844850db2b0cf6a13a3a11..0ba937a3f16b2028cc38ec59b6ed4c027fb32f50 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -19,6 +19,9 @@ package android.app; import android.annotation.NonNull; import android.content.ComponentName; import android.content.IIntentSender; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.IBinder; import android.service.voice.IVoiceInteractionSession; @@ -151,4 +154,29 @@ public abstract class ActivityManagerInternal { * such as Power Save mode. */ public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration); + + /** + * Updates and persists the {@link Configuration} for a given user. + * + * @param values the configuration to update + * @param userId the user to update the configuration for + */ + public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values, + int userId); + + /** + * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it. + * + * @return error codes used by {@link IActivityManager#startActivity} and its siblings. + */ + public abstract int startActivitiesAsPackage(String packageName, + int userId, Intent[] intents, Bundle bOptions); + + /** + * Get the procstate for the UID. The return value will be between + * {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}. + * Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT} + * (-1). + */ + public abstract int getUidProcessState(int uid); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index dcac633c4579d7431183bbf1402a72f7d3edb8d2..aacd5da58640069b3c4416e5a74ad64fd289afce 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2371,7 +2371,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface( data.readStrongBinder()); - registerUserSwitchObserver(observer); + String name = data.readString(); + registerUserSwitchObserver(observer, name); reply.writeNoException(); return true; } @@ -2995,6 +2996,27 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeInt(result); return true; } + case SET_VR_THREAD_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final int tid = data.readInt(); + setVrThread(tid); + reply.writeNoException(); + return true; + } + case SET_RENDER_THREAD_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final int tid = data.readInt(); + setRenderThread(tid); + reply.writeNoException(); + return true; + } + case SET_HAS_TOP_UI: { + data.enforceInterface(IActivityManager.descriptor); + final boolean hasTopUi = data.readInt() != 0; + setHasTopUi(hasTopUi); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -6060,11 +6082,13 @@ class ActivityManagerProxy implements IActivityManager return result; } - public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException { + public void registerUserSwitchObserver(IUserSwitchObserver observer, + String name) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(observer != null ? observer.asBinder() : null); + data.writeString(name); mRemote.transact(REGISTER_USER_SWITCH_OBSERVER_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -7028,5 +7052,45 @@ class ActivityManagerProxy implements IActivityManager return res; } + @Override + public void setVrThread(int tid) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(tid); + mRemote.transact(SET_VR_THREAD_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + return; + } + + public void setRenderThread(int tid) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(tid); + mRemote.transact(SET_RENDER_THREAD_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + return; + } + + public void setHasTopUi(boolean hasTopUi) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(hasTopUi ? 1 : 0); + mRemote.transact(SET_HAS_TOP_UI, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + return; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 4c8ddc7eb6b139bdfec6d15ce4661660dad9c9c6..d9a46903ee38e514e18a8a08cd6e65689ef03958 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -31,10 +31,13 @@ import android.os.IRemoteCallback; import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; +import android.transition.Transition; +import android.transition.TransitionManager; import android.util.Pair; import android.util.Slog; import android.view.AppTransitionAnimationSpec; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import java.util.ArrayList; @@ -190,6 +193,7 @@ public class ActivityOptions { = "android:activity.exitCoordinatorIndex"; private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; + private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint"; /** @hide */ public static final int ANIM_NONE = 0; @@ -241,6 +245,7 @@ public class ActivityOptions { private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; private boolean mTaskOverlay; private AppTransitionAnimationSpec mAnimSpecs[]; + private int mRotationAnimationHint = -1; /** * Create an ActivityOptions specifying a custom animation to run when @@ -640,10 +645,71 @@ public class ActivityOptions { public static ActivityOptions makeSceneTransitionAnimation(Activity activity, Pair... sharedElements) { ActivityOptions opts = new ActivityOptions(); - if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { - opts.mAnimationType = ANIM_DEFAULT; + makeSceneTransitionAnimation(activity, activity.getWindow(), opts, + activity.mExitTransitionListener, sharedElements); + return opts; + } + + /** + * Call this immediately prior to startActivity to begin a shared element transition + * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS. + * The exit transition will start immediately and the shared element transition will + * start once the launched Activity's shared element is ready. + *

+ * When all transitions have completed and the shared element has been transfered, + * the window's decor View will have its visibility set to View.GONE. + * + * @hide + */ + @SafeVarargs + public static ActivityOptions startSharedElementAnimation(Window window, + Pair... sharedElements) { + ActivityOptions opts = new ActivityOptions(); + final View decorView = window.getDecorView(); + if (decorView == null) { return opts; } + final ExitTransitionCoordinator exit = + makeSceneTransitionAnimation(null, window, opts, null, sharedElements); + if (exit != null) { + HideWindowListener listener = new HideWindowListener(window, exit); + exit.setHideSharedElementsCallback(listener); + exit.startExit(); + } + return opts; + } + + /** + * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])} + * animation must be stopped and the Views reset. This can happen if there was an error + * from startActivity or a springboard activity and the animation should stop and reset. + * + * @hide + */ + public static void stopSharedElementAnimation(Window window) { + final View decorView = window.getDecorView(); + if (decorView == null) { + return; + } + final ExitTransitionCoordinator exit = (ExitTransitionCoordinator) + decorView.getTag(com.android.internal.R.id.cross_task_transition); + if (exit != null) { + exit.cancelPendingTransitions(); + decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null); + TransitionManager.endTransitions((ViewGroup) decorView); + exit.resetViews(); + exit.clearState(); + decorView.setVisibility(View.VISIBLE); + } + } + + static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window, + ActivityOptions opts, SharedElementCallback callback, + Pair[] sharedElements) { + if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { + opts.mAnimationType = ANIM_DEFAULT; + return null; + } opts.mAnimationType = ANIM_SCENE_TRANSITION; ArrayList names = new ArrayList(); @@ -665,18 +731,22 @@ public class ActivityOptions { } } - ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names, - views, false); + ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window, + callback, names, names, views, false); opts.mTransitionReceiver = exit; opts.mSharedElementNames = names; - opts.mIsReturning = false; - opts.mExitCoordinatorIndex = - activity.mActivityTransitionState.addExitTransitionCoordinator(exit); - return opts; + opts.mIsReturning = (activity == null); + if (activity == null) { + opts.mExitCoordinatorIndex = -1; + } else { + opts.mExitCoordinatorIndex = + activity.mActivityTransitionState.addExitTransitionCoordinator(exit); + } + return exit; } /** @hide */ - public static ActivityOptions makeSceneTransitionAnimation(Activity activity, + static ActivityOptions makeSceneTransitionAnimation(Activity activity, ExitTransitionCoordinator exitCoordinator, ArrayList sharedElementNames, int resultCode, Intent resultData) { ActivityOptions opts = new ActivityOptions(); @@ -795,6 +865,7 @@ public class ActivityOptions { mAnimationFinishedListener = IRemoteCallback.Stub.asInterface( opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER)); } + mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT); } /** @@ -900,6 +971,16 @@ public class ActivityOptions { return mIsReturning; } + /** + * Returns whether or not the ActivityOptions was created with + * {@link #startSharedElementAnimation(Window, Pair[])}. + * + * @hide + */ + boolean isCrossTask() { + return mExitCoordinatorIndex < 0; + } + /** @hide */ public ArrayList getSharedElementNames() { return mSharedElementNames; @@ -1138,6 +1219,7 @@ public class ActivityOptions { if (mAnimationFinishedListener != null) { b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder()); } + b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint); return b; } @@ -1184,6 +1266,27 @@ public class ActivityOptions { return null; } + /** + * Returns the rotation animation set by {@link setRotationAnimationHint} or -1 + * if unspecified. + * @hide + */ + public int getRotationAnimationHint() { + return mRotationAnimationHint; + } + + + /** + * Set a rotation animation to be used if launching the activity + * triggers an orientation change, or -1 to clear. See + * {@link android.view.WindowManager.LayoutParams} for rotation + * animation values. + * @hide + */ + public void setRotationAnimationHint(int hint) { + mRotationAnimationHint = hint; + } + /** @hide */ @Override public String toString() { @@ -1191,4 +1294,65 @@ public class ActivityOptions { + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY=" + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight; } + + private static class HideWindowListener extends Transition.TransitionListenerAdapter + implements ExitTransitionCoordinator.HideSharedElementsCallback { + private final Window mWindow; + private final ExitTransitionCoordinator mExit; + private final boolean mWaitingForTransition; + private boolean mTransitionEnded; + private boolean mSharedElementHidden; + private ArrayList mSharedElements; + + public HideWindowListener(Window window, ExitTransitionCoordinator exit) { + mWindow = window; + mExit = exit; + mSharedElements = new ArrayList<>(exit.mSharedElements); + Transition transition = mWindow.getExitTransition(); + if (transition != null) { + transition.addListener(this); + mWaitingForTransition = true; + } else { + mWaitingForTransition = false; + } + View decorView = mWindow.getDecorView(); + if (decorView != null) { + if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) { + throw new IllegalStateException( + "Cannot start a transition while one is running"); + } + decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit); + } + } + + @Override + public void onTransitionEnd(Transition transition) { + mTransitionEnded = true; + hideWhenDone(); + transition.removeListener(this); + } + + @Override + public void hideSharedElements() { + mSharedElementHidden = true; + hideWhenDone(); + } + + private void hideWhenDone() { + if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) { + mExit.resetViews(); + int numSharedElements = mSharedElements.size(); + for (int i = 0; i < numSharedElements; i++) { + View view = mSharedElements.get(i); + view.requestLayout(); + } + View decorView = mWindow.getDecorView(); + if (decorView != null) { + decorView.setTagInternal( + com.android.internal.R.id.cross_task_transition, null); + decorView.setVisibility(View.GONE); + } + } + } + } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0728bdfc4016f8b2dc6c34b2bd9ebd7b99c7a18d..2c5f881321c070018726438f49baced940894b16 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -236,6 +236,7 @@ public final class ActivityThread { boolean mSystemThread = false; boolean mJitEnabled = false; boolean mSomeActivitiesChanged = false; + boolean mUpdatingSystemConfig = false; // These can be accessed by multiple threads; mPackages is the lock. // XXX For now we keep around information about all packages we have @@ -1574,7 +1575,9 @@ public final class ActivityThread { case CONFIGURATION_CHANGED: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi; + mUpdatingSystemConfig = true; handleConfigurationChanged((Configuration)msg.obj, null); + mUpdatingSystemConfig = false; Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case CLEAN_UP_CONTEXT: @@ -4624,12 +4627,20 @@ public final class ActivityThread { if ((activity == null) || (activity.mCurrentConfig == null)) { shouldChangeConfig = true; } else { - // If the new config is the same as the config this Activity - // is already running with then don't bother calling - // onConfigurationChanged + // If the new config is the same as the config this Activity is already + // running with and the override config also didn't change, then don't + // bother calling onConfigurationChanged. int diff = activity.mCurrentConfig.diff(newConfig); - if (diff != 0) { - shouldChangeConfig = true; + if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken, + amOverrideConfig)) { + // Always send the task-level config changes. For system-level configuration, if + // this activity doesn't handle any of the config changes, then don't bother + // calling onConfigurationChanged as we're going to destroy it. + if (!mUpdatingSystemConfig + || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0 + || !reportToActivity) { + shouldChangeConfig = true; + } } } diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 02eb4d3ded1c17872ca52ac6ad6fb5f0dd1aa9a2..aef1d0c31f9df0facc1b0b4b72c3edf128bbd559 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -185,7 +185,12 @@ class ActivityTransitionState { activity.getWindow().getDecorView().setVisibility(View.VISIBLE); } mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, - resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning()); + resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(), + mEnterActivityOptions.isCrossTask()); + if (mEnterActivityOptions.isCrossTask()) { + mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames()); + mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames()); + } if (!mIsEnterPostponed) { startEnter(); @@ -275,7 +280,8 @@ class ActivityTransitionState { } private void restoreReenteringViews() { - if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) { + if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() && + !mEnterTransitionCoordinator.isCrossTask()) { mEnterTransitionCoordinator.forceViewsToAppear(); mExitingFrom = null; mExitingTo = null; @@ -302,8 +308,9 @@ class ActivityTransitionState { } } - mReturnExitCoordinator = - new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true); + mReturnExitCoordinator = new ExitTransitionCoordinator(activity, + activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames, + null, null, true); if (enterViewsTransition != null && decor != null) { enterViewsTransition.resume(decor); } @@ -330,11 +337,12 @@ class ActivityTransitionState { } public void startExitOutTransition(Activity activity, Bundle options) { - if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { + mEnterTransitionCoordinator = null; + if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) || + mExitTransitionCoordinators == null) { return; } ActivityOptions activityOptions = new ActivityOptions(options); - mEnterTransitionCoordinator = null; if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { int key = activityOptions.getExitCoordinatorKey(); int index = mExitTransitionCoordinators.indexOfKey(key); diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 782407262c1986f33cd98822424fe20f7475a556..9928512de09f09438897d6bdc5cebac52119f2a5 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -201,7 +201,7 @@ public class AlertDialog extends Dialog implements DialogInterface { createContextThemeWrapper); mWindow.alwaysReadCloseOnTouchAttr(); - mAlert = new AlertController(getContext(), this, getWindow()); + mAlert = AlertController.create(getContext(), this, getWindow()); } static int resolveDialogTheme(Context context, int themeResId) { diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 8692336439f91dda4f000393e6e56ed123c36307..9fa8a5d2faee06e9c597a4626afab450954c0ea4 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -345,7 +345,7 @@ public class ApplicationErrorReport implements Parcelable { PrintWriter pw = new FastPrintWriter(sw, false, 256); tr.printStackTrace(pw); pw.flush(); - stackTrace = sw.toString(); + stackTrace = sanitizeString(sw.toString()); exceptionMessage = tr.getMessage(); // Populate fields with the "root cause" exception @@ -374,6 +374,29 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = "unknown"; throwLineNumber = 0; } + + exceptionMessage = sanitizeString(exceptionMessage); + } + + /** + * Ensure that the string is of reasonable size, truncating from the middle if needed. + */ + private String sanitizeString(String s) { + int prefixLength = 10 * 1024; + int suffixLength = 10 * 1024; + int acceptableLength = prefixLength + suffixLength; + + if (s != null && s.length() > acceptableLength) { + String replacement = + "\n[TRUNCATED " + (s.length() - acceptableLength) + " CHARS]\n"; + + StringBuilder sb = new StringBuilder(acceptableLength + replacement.length()); + sb.append(s.substring(0, prefixLength)); + sb.append(replacement); + sb.append(s.substring(s.length() - suffixLength)); + return sb.toString(); + } + return s; } /** diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 1058b0bc01c3d5fcedadb183e5ed3ba04278d80e..9cea49102925428d202e3db45a22a95c75adfff5 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1245,18 +1245,16 @@ public class ApplicationPackageManager extends PackageManager { return mContext.mMainThread.getSystemContext().getResources(); } final boolean sameUid = (app.uid == Process.myUid()); - try { - return mContext.mMainThread.getTopLevelResources( + final Resources r = mContext.mMainThread.getTopLevelResources( sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY, mContext.mPackageInfo); - } catch (Resources.NotFoundException cause) { - final NameNotFoundException ex = - new NameNotFoundException("Unable to open " + app.publicSourceDir); - ex.initCause(cause); - throw ex; + if (r != null) { + return r; } + throw new NameNotFoundException("Unable to open " + app.publicSourceDir); + } @Override diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 1e2cc266201821795e67702d94ab2b10b3f73358..a8d332b8378cfc313a235504a109124d85b321b2 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -17,6 +17,7 @@ package android.app; import android.graphics.Rect; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -852,7 +853,9 @@ final class BackStackRecord extends FragmentTransaction implements * Ensure that fragments that are entering are at least at the CREATED state * so that they may load Transitions using TransitionInflater. */ - if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) { + if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED && + mManager.mHost.getContext().getApplicationInfo().targetSdkVersion >= + Build.VERSION_CODES.N) { mManager.makeActive(fragment); mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index e6ca52072079955fa22908921fcd9fe797257e2c..8f424676dbe505e0dd4765fbf20bc999a8c3926e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -185,15 +185,6 @@ class ContextImpl extends Context { @GuardedBy("mSync") private File mCodeCacheDir; - @GuardedBy("mSync") - private File[] mExternalObbDirs; - @GuardedBy("mSync") - private File[] mExternalFilesDirs; - @GuardedBy("mSync") - private File[] mExternalCacheDirs; - @GuardedBy("mSync") - private File[] mExternalMediaDirs; - // The system service cache for the system services that are cached per-ContextImpl. final Object[] mServiceCache = SystemServiceRegistry.createServiceCache(); @@ -562,17 +553,10 @@ class ContextImpl extends Context { @Override public File[] getExternalFilesDirs(String type) { synchronized (mSync) { - if (mExternalFilesDirs == null) { - mExternalFilesDirs = Environment.buildExternalStorageAppFilesDirs(getPackageName()); - } - - // Splice in requested type, if any - File[] dirs = mExternalFilesDirs; + File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName()); if (type != null) { dirs = Environment.buildPaths(dirs, type); } - - // Create dirs if needed return ensureExternalDirsExistOrFilter(dirs); } } @@ -586,12 +570,8 @@ class ContextImpl extends Context { @Override public File[] getObbDirs() { synchronized (mSync) { - if (mExternalObbDirs == null) { - mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName()); - } - - // Create dirs if needed - return ensureExternalDirsExistOrFilter(mExternalObbDirs); + File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName()); + return ensureExternalDirsExistOrFilter(dirs); } } @@ -624,24 +604,16 @@ class ContextImpl extends Context { @Override public File[] getExternalCacheDirs() { synchronized (mSync) { - if (mExternalCacheDirs == null) { - mExternalCacheDirs = Environment.buildExternalStorageAppCacheDirs(getPackageName()); - } - - // Create dirs if needed - return ensureExternalDirsExistOrFilter(mExternalCacheDirs); + File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName()); + return ensureExternalDirsExistOrFilter(dirs); } } @Override public File[] getExternalMediaDirs() { synchronized (mSync) { - if (mExternalMediaDirs == null) { - mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName()); - } - - // Create dirs if needed - return ensureExternalDirsExistOrFilter(mExternalMediaDirs); + File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName()); + return ensureExternalDirsExistOrFilter(dirs); } } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 8bf1e9a97e4847001a9edd1c47f318bf8cd1e7ab..5d12b0da6ab81b6ac3fe9b61fe45e4b76dce3cf5 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -59,12 +59,14 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mIsViewsTransitionStarted; private Transition mEnterViewsTransition; private OnPreDrawListener mViewsReadyListener; + private final boolean mIsCrossTask; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, - ArrayList sharedElementNames, boolean isReturning) { + ArrayList sharedElementNames, boolean isReturning, boolean isCrossTask) { super(activity.getWindow(), sharedElementNames, - getListener(activity, isReturning), isReturning); + getListener(activity, isReturning && !isCrossTask), isReturning); mActivity = activity; + mIsCrossTask = isCrossTask; setResultReceiver(resultReceiver); prepareEnter(); Bundle resultReceiverBundle = new Bundle(); @@ -85,6 +87,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } + boolean isCrossTask() { + return mIsCrossTask; + } + public void viewInstancesReady(ArrayList accepted, ArrayList localNames, ArrayList localViews) { boolean remap = false; @@ -325,7 +331,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { if (mActivity == null || decorView == null) { return; } - mActivity.overridePendingTransition(0, 0); + if (!isCrossTask()) { + mActivity.overridePendingTransition(0, 0); + } if (!mIsReturning) { mWasOpaque = mActivity.convertToTranslucent(null, null); Drawable background = decorView.getBackground(); diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/java/android/app/EphemeralResolveInfo.aidl similarity index 95% rename from core/java/com/android/internal/app/EphemeralResolveInfo.aidl rename to core/java/android/app/EphemeralResolveInfo.aidl index 529527bd394b99e95956a5c7769f2f0290fabd6c..db71d250ade23cd73c7f82e67abcc5caf047979f 100644 --- a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl +++ b/core/java/android/app/EphemeralResolveInfo.aidl @@ -14,6 +14,6 @@ ** limitations under the License. */ -package com.android.internal.app; +package android.app; parcelable EphemeralResolveInfo; diff --git a/core/java/com/android/internal/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java similarity index 69% rename from core/java/com/android/internal/app/EphemeralResolverService.java rename to core/java/android/app/EphemeralResolverService.java index 6ba04a98aa2a5441a79d410025ba2539969923de..ba791085d617a81f903897306b1bf987b9ce27bd 100644 --- a/core/java/com/android/internal/app/EphemeralResolverService.java +++ b/core/java/android/app/EphemeralResolverService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.app; +package android.app; import android.annotation.SystemApi; import android.app.Service; @@ -37,19 +37,24 @@ import java.util.List; */ @SystemApi public abstract class EphemeralResolverService extends Service { - public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO"; - public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE"; + public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; + public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE"; + private static final String EXTRA_PREFIX = "android.app.PREFIX"; private Handler mHandler; /** * Called to retrieve resolve info for ephemeral applications. * * @param digestPrefix The hash prefix of the ephemeral's domain. + * @param prefixMask A mask that was applied to each digest prefix. This should + * be used when comparing against the digest prefixes as all bits might + * not be set. */ - protected abstract List getEphemeralResolveInfoList(int digestPrefix); + public abstract List onEphemeralResolveInfoList( + int digestPrefix[], int prefixMask); @Override - protected final void attachBaseContext(Context base) { + public final void attachBaseContext(Context base) { super.attachBaseContext(base); mHandler = new ServiceHandler(base.getMainLooper()); } @@ -59,10 +64,13 @@ public abstract class EphemeralResolverService extends Service { return new IEphemeralResolver.Stub() { @Override public void getEphemeralResolveInfoList( - IRemoteCallback callback, int digestPrefix, int sequence) { - mHandler.obtainMessage(ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, - digestPrefix, sequence, callback) - .sendToTarget(); + IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) { + final Message msg = mHandler.obtainMessage( + ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback); + final Bundle data = new Bundle(); + data.putIntArray(EXTRA_PREFIX, digestPrefix); + msg.setData(data); + msg.sendToTarget(); } }; } @@ -81,8 +89,9 @@ public abstract class EphemeralResolverService extends Service { switch (action) { case MSG_GET_EPHEMERAL_RESOLVE_INFO: { final IRemoteCallback callback = (IRemoteCallback) message.obj; + final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); final List resolveInfo = - getEphemeralResolveInfoList(message.arg1); + onEphemeralResolveInfoList(digestPrefix, message.arg1); final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, message.arg2); data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo); diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 0404288773714f766a6d4c29307d8b8a8b71def4..160c28592582aef69a1330df138e010236722644 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -35,6 +35,7 @@ import android.transition.TransitionManager; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.Window; import java.util.ArrayList; @@ -59,18 +60,20 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private Bundle mExitSharedElementBundle; private boolean mIsExitStarted; private boolean mSharedElementsHidden; + private HideSharedElementsCallback mHideSharedElementsCallback; - public ExitTransitionCoordinator(Activity activity, ArrayList names, + public ExitTransitionCoordinator(Activity activity, Window window, + SharedElementCallback listener, ArrayList names, ArrayList accepted, ArrayList mapped, boolean isReturning) { - super(activity.getWindow(), names, getListener(activity, isReturning), isReturning); + super(window, names, listener, isReturning); viewsReady(mapSharedElements(accepted, mapped)); stripOffscreenViews(); mIsBackgroundReady = !isReturning; mActivity = activity; } - private static SharedElementCallback getListener(Activity activity, boolean isReturning) { - return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener; + void setHideSharedElementsCallback(HideSharedElementsCallback callback) { + mHideSharedElementsCallback = callback; } @Override @@ -188,6 +191,9 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private void hideSharedElements() { moveSharedElementsFromOverlay(); + if (mHideSharedElementsCallback != null) { + mHideSharedElementsCallback.hideSharedElements(); + } if (!mIsHidden) { hideViews(mSharedElements); } @@ -207,7 +213,11 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { startTransition(new Runnable() { @Override public void run() { - beginTransitions(); + if (mActivity != null) { + beginTransitions(); + } else { + startExitTransition(); + } } }); } @@ -508,4 +518,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { return getWindow().getSharedElementExitTransition(); } } + + interface HideSharedElementsCallback { + void hideSharedElements(); + } } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index a637ef4f9c5499f4a72b605a8eced8aacbd3348e..8afca784b06727d97c15368adf283493f44107c8 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1483,9 +1483,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * at this point. If you want to do work once the activity itself is * created, see {@link #onActivityCreated(Bundle)}. * - *

If your app's targetSdkVersion is 23 or lower, child fragments - * being restored from the savedInstanceState are restored after onCreate - * returns. When targeting N or above and running on an N or newer platform version + *

If your app's targetSdkVersion is {@link android.os.Build.VERSION_CODES#M} + * or lower, child fragments being restored from the savedInstanceState are restored after + * onCreate returns. When targeting {@link android.os.Build.VERSION_CODES#N} or + * above and running on an N or newer platform version * they are restored by Fragment.onCreate.

* * @param savedInstanceState If the fragment is being re-created from diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index e435580d55fef9626ff9195af921550cbb834d3c..633e85b781184bd78fd39c29f6db090b2b62381a 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -17,7 +17,8 @@ import java.lang.annotation.RetentionPolicy; * */ public abstract class FragmentTransaction { @@ -25,17 +26,17 @@ public abstract class FragmentTransaction { * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId. */ public abstract FragmentTransaction add(Fragment fragment, String tag); - + /** * Calls {@link #add(int, Fragment, String)} with a null tag. */ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment); - + /** * Add a fragment to the activity state. This fragment may optionally * also have its view (if {@link Fragment#onCreateView Fragment.onCreateView} * returns non-null) inserted into a container view of the activity. - * + * * @param containerViewId Optional identifier of the container this fragment is * to be placed in. If 0, it will not be placed in a container. * @param fragment The fragment to be added. This fragment must not already @@ -43,64 +44,64 @@ public abstract class FragmentTransaction { * @param tag Optional tag name for the fragment, to later retrieve the * fragment with {@link FragmentManager#findFragmentByTag(String) * FragmentManager.findFragmentByTag(String)}. - * + * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment, String tag); - + /** * Calls {@link #replace(int, Fragment, String)} with a null tag. */ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment); - + /** * Replace an existing fragment that was added to a container. This is * essentially the same as calling {@link #remove(Fragment)} for all * currently added fragments that were added with the same containerViewId * and then {@link #add(int, Fragment, String)} with the same arguments * given here. - * + * * @param containerViewId Identifier of the container whose fragment(s) are * to be replaced. * @param fragment The new fragment to place in the container. * @param tag Optional tag name for the fragment, to later retrieve the * fragment with {@link FragmentManager#findFragmentByTag(String) * FragmentManager.findFragmentByTag(String)}. - * + * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment, String tag); - + /** * Remove an existing fragment. If it was added to a container, its view * is also removed from that container. - * + * * @param fragment The fragment to be removed. - * + * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction remove(Fragment fragment); - + /** * Hides an existing fragment. This is only relevant for fragments whose * views have been added to a container, as this will cause the view to * be hidden. - * + * * @param fragment The fragment to be hidden. - * + * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction hide(Fragment fragment); - + /** * Shows a previously hidden fragment. This is only relevant for fragments whose * views have been added to a container, as this will cause the view to * be shown. - * + * * @param fragment The fragment to be shown. - * + * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction show(Fragment fragment); @@ -135,17 +136,17 @@ public abstract class FragmentTransaction { * false otherwise. */ public abstract boolean isEmpty(); - + /** * Bit mask that is set for all enter transitions. */ public static final int TRANSIT_ENTER_MASK = 0x1000; - + /** * Bit mask that is set for all exit transitions. */ public static final int TRANSIT_EXIT_MASK = 0x2000; - + /** Not set up for a transition. */ public static final int TRANSIT_UNSET = -1; /** No animation for transition. */ @@ -202,7 +203,7 @@ public abstract class FragmentTransaction { * animations. */ public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes); - + /** * Add this transaction to the back stack. This means that the transaction * will be remembered after it is committed, and will reverse its operation @@ -269,7 +270,7 @@ public abstract class FragmentTransaction { * because the state after the commit can be lost if the activity needs to * be restored from its state. See {@link #commitAllowingStateLoss()} for * situations where it may be okay to lose the commit.

- * + * * @return Returns the identifier of this transaction's back stack entry, * if {@link #addToBackStack(String)} had been called. Otherwise, returns * a negative number. diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 80ba3eb02233e78c2f9bc8cd4b840a2a1512c2f5..e411e03a8a949da41a83c5ec6fe17f96836d64de 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -512,7 +512,8 @@ public interface IActivityManager extends IInterface { public int getLaunchedFromUid(IBinder activityToken) throws RemoteException; public String getLaunchedFromPackage(IBinder activityToken) throws RemoteException; - public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; + public void registerUserSwitchObserver(IUserSwitchObserver observer, + String name) throws RemoteException; public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; public void requestBugReport(int bugreportType) throws RemoteException; @@ -657,6 +658,19 @@ public interface IActivityManager extends IInterface { IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) throws RemoteException; + public void setVrThread(int tid) throws RemoteException; + public void setRenderThread(int tid) throws RemoteException; + + /** + * Lets activity manager know whether the calling process is currently showing "top-level" UI + * that is not an activity, i.e. windows on the screen the user is currently interacting with. + * + *

This flag can only be set for persistent processes. + * + * @param hasTopUi Whether the calling process has "top-level" UI. + */ + public void setHasTopUi(boolean hasTopUi) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -1043,4 +1057,9 @@ public interface IActivityManager extends IInterface { int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374; int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375; int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376; + + // Start of N MR1 transactions + int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377; + int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378; + int SET_HAS_TOP_UI = IBinder.FIRST_CALL_TRANSACTION + 379; } diff --git a/core/java/com/android/internal/app/IEphemeralResolver.aidl b/core/java/android/app/IEphemeralResolver.aidl similarity index 88% rename from core/java/com/android/internal/app/IEphemeralResolver.aidl rename to core/java/android/app/IEphemeralResolver.aidl index 40429ee2cccbf725bf46921180f669c61249205d..ee869eaa79365190e61be012a34f4d25438b2b1f 100644 --- a/core/java/com/android/internal/app/IEphemeralResolver.aidl +++ b/core/java/android/app/IEphemeralResolver.aidl @@ -14,11 +14,12 @@ * limitations under the License. */ -package com.android.internal.app; +package android.app; -import android.content.Intent; import android.os.IRemoteCallback; +/** @hide */ oneway interface IEphemeralResolver { - void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence); + void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix, + int prefixMask, int sequence); } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index a0762b94a040144d89e0226837c9ef10c1dd71a7..75a5bf7f77a2201df48b6999de45e0be22109173 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -44,12 +44,12 @@ interface IWallpaperManager { */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage, in Rect cropHint, boolean allowBackup, out Bundle extras, int which, - IWallpaperManagerCallback completion); + IWallpaperManagerCallback completion, int userId); /** * Set the live wallpaper. This only affects the system wallpaper. */ - void setWallpaperComponentChecked(in ComponentName name, in String callingPackage); + void setWallpaperComponentChecked(in ComponentName name, in String callingPackage, int userId); /** * Set the live wallpaper. This only affects the system wallpaper. @@ -72,7 +72,7 @@ interface IWallpaperManager { * information about that wallpaper. Otherwise, if it is a static image, * simply return null. */ - WallpaperInfo getWallpaperInfo(); + WallpaperInfo getWallpaperInfo(int userId); /** * Clear the system wallpaper. @@ -128,7 +128,7 @@ interface IWallpaperManager { /* * Backup: is the current system wallpaper image eligible for off-device backup? */ - boolean isWallpaperBackupEligible(int userId); + boolean isWallpaperBackupEligible(int which, int userId); /* * Keyguard: register a callback for being notified that lock-state relevant diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0745f16135e944d506b3fb673924f3335d669630..7ef84f45252de6380624023de10f3989f7b10bd2 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1396,18 +1396,6 @@ public final class LoadedApk { } public void death(ComponentName name, IBinder service) { - ServiceDispatcher.ConnectionInfo old; - - synchronized (this) { - old = mActiveConnections.remove(name); - if (old == null || old.binder != service) { - // Death for someone different than who we last - // reported... just ignore it. - return; - } - old.binder.unlinkToDeath(old.deathMonitor, 0); - } - if (mActivityThread != null) { mActivityThread.post(new RunConnection(name, service, 1)); } else { @@ -1456,7 +1444,7 @@ public final class LoadedApk { } } - // If there was an old service, it is not disconnected. + // If there was an old service, it is now disconnected. if (old != null) { mConnection.onServiceDisconnected(name); } @@ -1467,6 +1455,17 @@ public final class LoadedApk { } public void doDeath(ComponentName name, IBinder service) { + synchronized (this) { + ConnectionInfo old = mActiveConnections.get(name); + if (old == null || old.binder != service) { + // Death for someone different than who we last + // reported... just ignore it. + return; + } + mActiveConnections.remove(name); + old.binder.unlinkToDeath(old.deathMonitor, 0); + } + mConnection.onServiceDisconnected(name); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 1c2a6aecca24e3780012fcb872d2e6f0f7e24e0f..7c28664b386d2a390d5f987b6d03a888a2300449 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -39,7 +39,6 @@ import android.media.AudioManager; import android.media.session.MediaSession; import android.net.Uri; import android.os.BadParcelableException; -import android.os.BaseBundle; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -51,7 +50,9 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.AbsoluteSizeSpan; +import android.text.style.BackgroundColorSpan; import android.text.style.CharacterStyle; +import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; import android.text.style.TextAppearanceSpan; import android.util.ArraySet; @@ -73,7 +74,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -1314,6 +1314,7 @@ public class Notification implements Parcelable // Flags bitwise-ored to mFlags private static final int FLAG_AVAILABLE_OFFLINE = 0x1; private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1; + private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2; // Default value for flags integer private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE; @@ -1500,6 +1501,29 @@ public class Notification implements Parcelable public boolean getHintLaunchesActivity() { return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0; } + + /** + * Set a hint that this Action should be displayed inline. + * + * @param hintDisplayInline {@code true} if action should be displayed inline, false + * otherwise + * @return this object for method chaining + */ + public WearableExtender setHintDisplayActionInline( + boolean hintDisplayInline) { + setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline); + return this; + } + + /** + * Get a hint that this Action should be displayed inline. + * + * @return {@code true} if the Action should be displayed inline, {@code false} + * otherwise. The default value is {@code false} if this was never set. + */ + public boolean getHintDisplayActionInline() { + return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0; + } } } @@ -3239,7 +3263,8 @@ public class Notification implements Parcelable * Resets the notification header to its original state */ private void resetNotificationHeader(RemoteViews contentView) { - contentView.setImageViewResource(R.id.icon, 0); + // Small icon doesn't need to be reset, as it's always set. Resetting would prevent + // re-using the drawable when the notification is updated. contentView.setBoolean(R.id.notification_header, "setExpanded", false); contentView.setTextViewText(R.id.app_name_text, null); contentView.setViewVisibility(R.id.chronometer, View.GONE); @@ -3457,6 +3482,8 @@ public class Notification implements Parcelable mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon); } contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon); + contentView.setDrawableParameters(R.id.icon, false /* targetBackground */, + -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel); processSmallIconColor(mN.mSmallIcon, contentView); } @@ -3502,6 +3529,8 @@ public class Notification implements Parcelable boolean validRemoteInput = false; int N = mActions.size(); + boolean emphazisedMode = mN.fullScreenIntent != null; + big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode); if (N > 0) { big.setViewVisibility(R.id.actions_container, View.VISIBLE); big.setViewVisibility(R.id.actions, View.VISIBLE); @@ -3512,7 +3541,8 @@ public class Notification implements Parcelable Action action = mActions.get(i); validRemoteInput |= hasValidRemoteInput(action); - final RemoteViews button = generateActionButton(action); + final RemoteViews button = generateActionButton(action, emphazisedMode, + i % 2 != 0); big.addView(R.id.actions, button); } } else { @@ -3677,13 +3707,13 @@ public class Notification implements Parcelable - private RemoteViews generateActionButton(Action action) { + private RemoteViews generateActionButton(Action action, boolean emphazisedMode, + boolean oddAction) { final boolean tombstone = (action.actionIntent == null); RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), - tombstone ? getActionTombstoneLayoutResource() - : getActionLayoutResource()); - final Icon ai = action.getIcon(); - button.setTextViewText(R.id.action0, processLegacyText(action.title)); + emphazisedMode ? getEmphasizedActionLayoutResource() + : tombstone ? getActionTombstoneLayoutResource() + : getActionLayoutResource()); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); } @@ -3691,12 +3721,143 @@ public class Notification implements Parcelable if (action.mRemoteInputs != null) { button.setRemoteInputs(R.id.action0, action.mRemoteInputs); } - if (mN.color != COLOR_DEFAULT) { - button.setTextColor(R.id.action0, resolveContrastColor()); + if (emphazisedMode) { + // change the background bgColor + int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list + : R.color.notification_action_list_dark); + button.setDrawableParameters(R.id.button_holder, true, -1, bgColor, + PorterDuff.Mode.SRC_ATOP, -1); + CharSequence title = action.title; + ColorStateList[] outResultColor = null; + if (isLegacy()) { + title = clearColorSpans(title); + } else { + outResultColor = new ColorStateList[1]; + title = ensureColorSpanContrast(title, bgColor, outResultColor); + } + button.setTextViewText(R.id.action0, title); + if (outResultColor != null && outResultColor[0] != null) { + // We need to set the text color as well since changing a text to uppercase + // clears its spans. + button.setTextColor(R.id.action0, outResultColor[0]); + } else if (mN.color != COLOR_DEFAULT) { + button.setTextColor(R.id.action0,resolveContrastColor()); + } + } else { + button.setTextViewText(R.id.action0, processLegacyText(action.title)); + if (mN.color != COLOR_DEFAULT) { + button.setTextColor(R.id.action0, resolveContrastColor()); + } } return button; } + /** + * Clears all color spans of a text + * @param charSequence the input text + * @return the same text but without color spans + */ + private CharSequence clearColorSpans(CharSequence charSequence) { + if (charSequence instanceof Spanned) { + Spanned ss = (Spanned) charSequence; + Object[] spans = ss.getSpans(0, ss.length(), Object.class); + SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString()); + for (Object span : spans) { + Object resultSpan = span; + if (resultSpan instanceof CharacterStyle) { + resultSpan = ((CharacterStyle) span).getUnderlying(); + } + if (resultSpan instanceof TextAppearanceSpan) { + TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan; + if (originalSpan.getTextColor() != null) { + resultSpan = new TextAppearanceSpan( + originalSpan.getFamily(), + originalSpan.getTextStyle(), + originalSpan.getTextSize(), + null, + originalSpan.getLinkTextColor()); + } + } else if (resultSpan instanceof ForegroundColorSpan + || (resultSpan instanceof BackgroundColorSpan)) { + continue; + } else { + resultSpan = span; + } + builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span), + ss.getSpanFlags(span)); + } + return builder; + } + return charSequence; + } + + /** + * Ensures contrast on color spans against a background color. also returns the color of the + * text if a span was found that spans over the whole text. + * + * @param charSequence the charSequence on which the spans are + * @param background the background color to ensure the contrast against + * @param outResultColor an array in which a color will be returned as the first element if + * there exists a full length color span. + * @return the contrasted charSequence + */ + private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background, + ColorStateList[] outResultColor) { + if (charSequence instanceof Spanned) { + Spanned ss = (Spanned) charSequence; + Object[] spans = ss.getSpans(0, ss.length(), Object.class); + SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString()); + for (Object span : spans) { + Object resultSpan = span; + int spanStart = ss.getSpanStart(span); + int spanEnd = ss.getSpanEnd(span); + boolean fullLength = (spanEnd - spanStart) == charSequence.length(); + if (resultSpan instanceof CharacterStyle) { + resultSpan = ((CharacterStyle) span).getUnderlying(); + } + if (resultSpan instanceof TextAppearanceSpan) { + TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan; + ColorStateList textColor = originalSpan.getTextColor(); + if (textColor != null) { + int[] colors = textColor.getColors(); + int[] newColors = new int[colors.length]; + for (int i = 0; i < newColors.length; i++) { + newColors[i] = NotificationColorUtil.ensureLargeTextContrast( + colors[i], background); + } + textColor = new ColorStateList(textColor.getStates().clone(), + newColors); + resultSpan = new TextAppearanceSpan( + originalSpan.getFamily(), + originalSpan.getTextStyle(), + originalSpan.getTextSize(), + textColor, + originalSpan.getLinkTextColor()); + if (fullLength) { + outResultColor[0] = new ColorStateList( + textColor.getStates().clone(), newColors); + } + } + } else if (resultSpan instanceof ForegroundColorSpan) { + ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan; + int foregroundColor = originalSpan.getForegroundColor(); + foregroundColor = NotificationColorUtil.ensureLargeTextContrast( + foregroundColor, background); + resultSpan = new ForegroundColorSpan(foregroundColor); + if (fullLength) { + outResultColor[0] = ColorStateList.valueOf(foregroundColor); + } + } else { + resultSpan = span; + } + + builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span)); + } + return builder; + } + return charSequence; + } + /** * @return Whether we are currently building a notification from a legacy (an app that * doesn't create material notifications by itself) app. @@ -3962,6 +4123,10 @@ public class Notification implements Parcelable return R.layout.notification_material_action; } + private int getEmphasizedActionLayoutResource() { + return R.layout.notification_material_action_emphasized; + } + private int getActionTombstoneLayoutResource() { return R.layout.notification_material_action_tombstone; } @@ -4267,9 +4432,15 @@ public class Notification implements Parcelable // mN.mLargeIcon // 2. !mBigLargeIconSet -> mN.mLargeIcon applies Icon oldLargeIcon = null; + Bitmap largeIconLegacy = null; if (mBigLargeIconSet) { oldLargeIcon = mBuilder.mN.mLargeIcon; mBuilder.mN.mLargeIcon = mBigLargeIcon; + // The legacy largeIcon might not allow us to clear the image, as it's taken in + // replacement if the other one is null. Because we're restoring these legacy icons + // for old listeners, this is in general non-null. + largeIconLegacy = mBuilder.mN.largeIcon; + mBuilder.mN.largeIcon = null; } RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource()); @@ -4281,6 +4452,7 @@ public class Notification implements Parcelable if (mBigLargeIconSet) { mBuilder.mN.mLargeIcon = oldLargeIcon; + mBuilder.mN.largeIcon = largeIconLegacy; } contentView.setImageViewBitmap(R.id.big_picture, mPicture); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 18b72e29e0e610253a00c6a73269674f5e222bcf..ff514bd7c81b7792a8b0e5aade2c8376f1a20666 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -312,8 +312,8 @@ public class NotificationManager try { service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, copy, idOut, user.getIdentifier()); - if (id != idOut[0]) { - Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); + if (localLOGV && id != idOut[0]) { + Log.v(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index c4673a3b0b4cd3c10e65af7286eaa25a19ee943f..d2e032721022b774766016b14901be03e9a93d14 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -242,7 +242,7 @@ public class ResourcesManager { * @return a new AssetManager. */ @VisibleForTesting - protected @NonNull AssetManager createAssetManager(@NonNull final ResourcesKey key) { + protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { AssetManager assets = new AssetManager(); // resDir can be null if the 'android' package is creating a new Resources object. @@ -250,15 +250,16 @@ public class ResourcesManager { // already. if (key.mResDir != null) { if (assets.addAssetPath(key.mResDir) == 0) { - throw new Resources.NotFoundException("failed to add asset path " + key.mResDir); + Log.e(TAG, "failed to add asset path " + key.mResDir); + return null; } } if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { if (assets.addAssetPath(splitResDir) == 0) { - throw new Resources.NotFoundException( - "failed to add split asset path " + splitResDir); + Log.e(TAG, "failed to add split asset path " + splitResDir); + return null; } } } @@ -303,11 +304,15 @@ public class ResourcesManager { return config; } - private @NonNull ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) { + private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) { final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration); daj.setCompatibilityInfo(key.mCompatInfo); final AssetManager assets = createAssetManager(key); + if (assets == null) { + return null; + } + final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj); final Configuration config = generateConfig(key, dm); final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj); @@ -323,7 +328,7 @@ public class ResourcesManager { * @param key The key to match. * @return a ResourcesImpl if the key matches a cache entry, null otherwise. */ - private ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) { + private @Nullable ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) { WeakReference weakImplRef = mResourceImpls.get(key); ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; if (impl != null && impl.getAssets().isUpToDate()) { @@ -338,12 +343,14 @@ public class ResourcesManager { * @param key The key to match. * @return a ResourcesImpl object matching the key. */ - private @NonNull ResourcesImpl findOrCreateResourcesImplForKeyLocked( + private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( @NonNull ResourcesKey key) { ResourcesImpl impl = findResourcesImplForKeyLocked(key); if (impl == null) { impl = createResourcesImpl(key); - mResourceImpls.put(key, new WeakReference<>(impl)); + if (impl != null) { + mResourceImpls.put(key, new WeakReference<>(impl)); + } } return impl; } @@ -352,7 +359,8 @@ public class ResourcesManager { * Find the ResourcesKey that this ResourcesImpl object is associated with. * @return the ResourcesKey or null if none was found. */ - private ResourcesKey findKeyForResourceImplLocked(@NonNull ResourcesImpl resourceImpl) { + private @Nullable ResourcesKey findKeyForResourceImplLocked( + @NonNull ResourcesImpl resourceImpl) { final int refCount = mResourceImpls.size(); for (int i = 0; i < refCount; i++) { WeakReference weakImplRef = mResourceImpls.valueAt(i); @@ -364,6 +372,26 @@ public class ResourcesManager { return null; } + /** + * Check if activity resources have same override config as the provided on. + * @param activityToken The Activity that resources should be associated with. + * @param overrideConfig The override configuration to be checked for equality with. + * @return true if activity resources override config matches the provided one or they are both + * null, false otherwise. + */ + boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken, + @Nullable Configuration overrideConfig) { + synchronized (this) { + final ActivityResources activityResources + = activityToken != null ? mActivityResourceReferences.get(activityToken) : null; + if (activityResources == null) { + return overrideConfig == null; + } else { + return Objects.equals(activityResources.overrideConfig, overrideConfig); + } + } + } + private ActivityResources getOrCreateActivityResourcesStructLocked( @NonNull IBinder activityToken) { ActivityResources activityResources = mActivityResourceReferences.get(activityToken); @@ -460,7 +488,7 @@ public class ResourcesManager { * {@link ClassLoader#getSystemClassLoader()} is used. * @return a Resources object from which to access resources. */ - public @NonNull Resources createBaseActivityResources(@NonNull IBinder activityToken, + public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -514,7 +542,7 @@ public class ResourcesManager { * {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)} * is called. */ - private @NonNull Resources getOrCreateResources(@Nullable IBinder activityToken, + private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken, @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) { synchronized (this) { if (DEBUG) { @@ -569,6 +597,9 @@ public class ResourcesManager { // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. ResourcesImpl resourcesImpl = createResourcesImpl(key); + if (resourcesImpl == null) { + return null; + } synchronized (this) { ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); @@ -622,7 +653,7 @@ public class ResourcesManager { * {@link ClassLoader#getSystemClassLoader()} is used. * @return a Resources object from which to access resources. */ - public @NonNull Resources getResources(@Nullable IBinder activityToken, + public @Nullable Resources getResources(@Nullable IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -745,10 +776,12 @@ public class ResourcesManager { ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey); if (resourcesImpl == null) { resourcesImpl = createResourcesImpl(newKey); - mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl)); + if (resourcesImpl != null) { + mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl)); + } } - if (resourcesImpl != resources.getImpl()) { + if (resourcesImpl != null && resourcesImpl != resources.getImpl()) { // Set the ResourcesImpl, updating it for all users of this Resources // object. resources.setImpl(resourcesImpl); @@ -890,7 +923,11 @@ public class ResourcesManager { if (r != null) { final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); if (key != null) { - r.setImpl(findOrCreateResourcesImplForKeyLocked(key)); + final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); + if (impl == null) { + throw new Resources.NotFoundException("failed to load " + libAsset); + } + r.setImpl(impl); } } } @@ -903,7 +940,11 @@ public class ResourcesManager { if (r != null) { final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); if (key != null) { - r.setImpl(findOrCreateResourcesImplForKeyLocked(key)); + final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); + if (impl == null) { + throw new Resources.NotFoundException("failed to load " + libAsset); + } + r.setImpl(impl); } } } diff --git a/core/java/android/app/RetailDemoModeServiceInternal.java b/core/java/android/app/RetailDemoModeServiceInternal.java new file mode 100644 index 0000000000000000000000000000000000000000..7ca214aae60265712129a7f5b17a84109f5bf603 --- /dev/null +++ b/core/java/android/app/RetailDemoModeServiceInternal.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app; + +/** + * Retail Demo Mode Service interface to be used locally inside system server + * + * @hide Only for use inside system server + */ +public interface RetailDemoModeServiceInternal { + /** + * Used to notify RetailDemoModeService of any user activity. + */ + public void onUserActivity(); +} \ No newline at end of file diff --git a/core/java/android/app/TabActivity.java b/core/java/android/app/TabActivity.java index 882e55a89a74b1e4fe029d5bc17301b63ecf4d93..637c8c19aef5eeb6ff5fb2586e6882709fe94777 100644 --- a/core/java/android/app/TabActivity.java +++ b/core/java/android/app/TabActivity.java @@ -28,23 +28,6 @@ import android.widget.TextView; * {@link ActionBar#newTab() ActionBar.newTab()} and * related APIs for placing tabs within their action bar area.

* - *

A replacement for TabActivity can also be implemented by directly using - * TabHost. You will need to define a layout that correctly uses a TabHost - * with a TabWidget as well as an area in which to display your tab content. - * A typical example would be:

- * - * {@sample development/samples/Support4Demos/res/layout/fragment_tabs.xml complete} - * - *

The implementation needs to take over responsibility for switching - * the shown content when the user switches between tabs. - * - * {@sample development/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java - * complete} - * - *

Also see the - * Fragment Tabs Pager sample for an example of using the support library's ViewPager to - * allow the user to swipe the content to switch between tabs.

- * * @deprecated New applications should use Fragments instead of this class; * to continue to run on older devices, you can use the v4 support library * which provides a version of the Fragment API that is compatible down to diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index 7db9fa8fed65993d2ea3cbf2d88f91db8b9ba430..9d40381fcefd2527c1db3c327dda1d2607380329 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.service.wallpaper.WallpaperService; @@ -72,6 +73,10 @@ public final class WallpaperInfo implements Parcelable { */ final int mDescriptionResource; + final int mContextUriResource; + final int mContextDescriptionResource; + final boolean mShowMetadataInPreview; + /** * Constructor. * @@ -89,7 +94,10 @@ public final class WallpaperInfo implements Parcelable { int thumbnailRes = -1; int authorRes = -1; int descriptionRes = -1; - + int contextUriRes = -1; + int contextDescriptionRes = -1; + boolean showMetadataInPreview = false; + XmlResourceParser parser = null; try { parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA); @@ -127,6 +135,15 @@ public final class WallpaperInfo implements Parcelable { descriptionRes = sa.getResourceId( com.android.internal.R.styleable.Wallpaper_description, -1); + contextUriRes = sa.getResourceId( + com.android.internal.R.styleable.Wallpaper_contextUri, + -1); + contextDescriptionRes = sa.getResourceId( + com.android.internal.R.styleable.Wallpaper_contextDescription, + -1); + showMetadataInPreview = sa.getBoolean( + com.android.internal.R.styleable.Wallpaper_showMetadataInPreview, + false); sa.recycle(); } catch (NameNotFoundException e) { @@ -140,6 +157,9 @@ public final class WallpaperInfo implements Parcelable { mThumbnailResource = thumbnailRes; mAuthorResource = authorRes; mDescriptionResource = descriptionRes; + mContextUriResource = contextUriRes; + mContextDescriptionResource = contextDescriptionRes; + mShowMetadataInPreview = showMetadataInPreview; } WallpaperInfo(Parcel source) { @@ -147,6 +167,9 @@ public final class WallpaperInfo implements Parcelable { mThumbnailResource = source.readInt(); mAuthorResource = source.readInt(); mDescriptionResource = source.readInt(); + mContextUriResource = source.readInt(); + mContextDescriptionResource = source.readInt(); + mShowMetadataInPreview = source.readInt() != 0; mService = ResolveInfo.CREATOR.createFromParcel(source); } @@ -248,7 +271,60 @@ public final class WallpaperInfo implements Parcelable { return pm.getText(packageName, mDescriptionResource, mService.serviceInfo.applicationInfo); } - + + /** + * Returns an URI that specifies a link for further context about this wallpaper. + * + * @param pm An instance of {@link PackageManager} to retrieve the URI. + * @return The URI. + */ + public Uri loadContextUri(PackageManager pm) throws NotFoundException { + if (mContextUriResource <= 0) throw new NotFoundException(); + String packageName = mService.resolvePackageName; + ApplicationInfo applicationInfo = null; + if (packageName == null) { + packageName = mService.serviceInfo.packageName; + applicationInfo = mService.serviceInfo.applicationInfo; + } + String contextUriString = pm.getText( + packageName, mContextUriResource, applicationInfo).toString(); + if (contextUriString == null) { + return null; + } + return Uri.parse(contextUriString); + } + + /** + * Retrieves a title of the URI that specifies a link for further context about this wallpaper. + * + * @param pm An instance of {@link PackageManager} to retrieve the title. + * @return The title. + */ + public CharSequence loadContextDescription(PackageManager pm) throws NotFoundException { + if (mContextDescriptionResource <= 0) throw new NotFoundException(); + String packageName = mService.resolvePackageName; + ApplicationInfo applicationInfo = null; + if (packageName == null) { + packageName = mService.serviceInfo.packageName; + applicationInfo = mService.serviceInfo.applicationInfo; + } + return pm.getText(packageName, mContextDescriptionResource, applicationInfo).toString(); + } + + /** + * Queries whether any metadata should be shown when previewing the wallpaper. If this value is + * set to true, any component that shows a preview of this live wallpaper should also show + * accompanying information like {@link #loadLabel}, + * {@link #loadDescription}, {@link #loadAuthor} and + * {@link #loadContextDescription(PackageManager)}, so the user gets to know further information + * about this wallpaper. + * + * @return Whether any metadata should be shown when previewing the wallpaper. + */ + public boolean getShowMetadataInPreview() { + return mShowMetadataInPreview; + } + /** * Return the class name of an activity that provides a settings UI for * the wallpaper. You can launch this activity be starting it with @@ -287,6 +363,9 @@ public final class WallpaperInfo implements Parcelable { dest.writeInt(mThumbnailResource); dest.writeInt(mAuthorResource); dest.writeInt(mDescriptionResource); + dest.writeInt(mContextUriResource); + dest.writeInt(mContextDescriptionResource); + dest.writeInt(mShowMetadataInPreview ? 1 : 0); mService.writeToParcel(dest, flags); } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7f467f0585c8faf8671ce84d5bc0246b928c8066..219afea02c48d6f30811e293df8b6a1b1eb5f07c 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -48,9 +48,11 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.view.WindowManagerGlobal; @@ -265,7 +267,7 @@ public class WallpaperManager { } static class Globals extends IWallpaperManagerCallback.Stub { - private IWallpaperManager mService; + private final IWallpaperManager mService; private Bitmap mCachedWallpaper; private int mCachedWallpaperUserId; private Bitmap mDefaultWallpaper; @@ -292,16 +294,16 @@ public class WallpaperManager { public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId) { - synchronized (this) { - if (mService != null) { - try { - if (!mService.isWallpaperSupported(context.getOpPackageName())) { - return null; - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (mService != null) { + try { + if (!mService.isWallpaperSupported(context.getOpPackageName())) { + return null; } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } + } + synchronized (this) { if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) { return mCachedWallpaper; } @@ -316,17 +318,21 @@ public class WallpaperManager { if (mCachedWallpaper != null) { return mCachedWallpaper; } - if (returnDefault) { - if (mDefaultWallpaper == null) { - mDefaultWallpaper = getDefaultWallpaperLocked(context, which); + } + if (returnDefault) { + Bitmap defaultWallpaper = mDefaultWallpaper; + if (defaultWallpaper == null) { + defaultWallpaper = getDefaultWallpaper(context, which); + synchronized (this) { + mDefaultWallpaper = defaultWallpaper; } - return mDefaultWallpaper; } - return null; + return defaultWallpaper; } + return null; } - public void forgetLoadedWallpaper() { + void forgetLoadedWallpaper() { synchronized (this) { mCachedWallpaper = null; mCachedWallpaperUserId = 0; @@ -361,7 +367,7 @@ public class WallpaperManager { return null; } - private Bitmap getDefaultWallpaperLocked(Context context, @SetWallpaperFlags int which) { + private Bitmap getDefaultWallpaper(Context context, @SetWallpaperFlags int which) { InputStream is = openDefaultWallpaper(context, which); if (is != null) { try { @@ -412,8 +418,14 @@ public class WallpaperManager { * This is returned as an * abstract Drawable that you can install in a View to display whatever * wallpaper the user has currently set. - * - * @return Returns a Drawable object that will draw the wallpaper. + *

+ * This method can return null if there is no system wallpaper available, if + * wallpapers are not supported in the current user, or if the calling app is not + * permitted to access the system wallpaper. + * + * @return Returns a Drawable object that will draw the system wallpaper, + * or {@code null} if no system wallpaper exists or if the calling application + * is not able to access the wallpaper. */ public Drawable getDrawable() { Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM); @@ -783,7 +795,7 @@ public class WallpaperManager { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } else { - return sGlobals.mService.getWallpaperInfo(); + return sGlobals.mService.getWallpaperInfo(UserHandle.myUserId()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -928,7 +940,8 @@ public class WallpaperManager { /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), - mContext.getOpPackageName(), null, false, result, which, completion); + mContext.getOpPackageName(), null, false, result, which, completion, + UserHandle.myUserId()); if (fd != null) { FileOutputStream fos = null; boolean ok = false; @@ -1029,6 +1042,19 @@ public class WallpaperManager { public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { + return setBitmap(fullImage, visibleCropHint, allowBackup, which, + UserHandle.myUserId()); + } + + /** + * Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user + * id. If the user id doesn't match the user id the process is running under, calling this + * requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * @hide + */ + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, + boolean allowBackup, @SetWallpaperFlags int which, int userId) + throws IOException { validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -1039,7 +1065,7 @@ public class WallpaperManager { try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, - result, which, completion); + result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; try { @@ -1165,7 +1191,7 @@ public class WallpaperManager { try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, - result, which, completion); + result, which, completion, UserHandle.myUserId()); if (fd != null) { FileOutputStream fos = null; try { @@ -1399,12 +1425,26 @@ public class WallpaperManager { */ @SystemApi public boolean setWallpaperComponent(ComponentName name) { + return setWallpaperComponent(name, UserHandle.myUserId()); + } + + /** + * Set the live wallpaper. + * + * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT + * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change + * another user's wallpaper. + * + * @hide + */ + public boolean setWallpaperComponent(ComponentName name, int userId) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } try { - sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName()); + sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(), + userId); return true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1655,13 +1695,13 @@ public class WallpaperManager { * Only the OS itself may use this method. * @hide */ - public boolean isWallpaperBackupEligible() { + public boolean isWallpaperBackupEligible(int which) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } try { - return sGlobals.mService.isWallpaperBackupEligible(mContext.getUserId()); + return sGlobals.mService.isWallpaperBackupEligible(which, mContext.getUserId()); } catch (RemoteException e) { Log.e(TAG, "Exception querying wallpaper backup eligibility: " + e.getMessage()); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 269037698408cf2be0568471d5ab5f4cb32d436d..46a5a8c73a19faa7dadf1c8834fa02253ee1cccf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1315,7 +1315,7 @@ public class DevicePolicyManager { /** * Retrieve the current minimum password quality for a particular admin or all admins that set - * retrictions on this user and its participating profiles. Restrictions on profiles that have + * restrictions on this user and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * *

This method can be called on the {@link DevicePolicyManager} instance @@ -1379,7 +1379,7 @@ public class DevicePolicyManager { /** * Retrieve the current minimum password length for a particular admin or all admins that set - * retrictions on this user and its participating profiles. Restrictions on profiles that have + * restrictions on this user and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * *

This method can be called on the {@link DevicePolicyManager} instance @@ -1442,7 +1442,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of upper case letters required in the password - * for a particular admin or all admins that set retrictions on this user and + * for a particular admin or all admins that set restrictions on this user and * its participating profiles. Restrictions on profiles that have a separate challenge * are not taken into account. * This is the same value as set by @@ -1511,7 +1511,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of lower case letters required in the password - * for a particular admin or all admins that set retrictions on this user + * for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * This is the same value as set by @@ -1580,7 +1580,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of letters required in the password - * for a particular admin or all admins that set retrictions on this user + * for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * This is the same value as set by @@ -1648,7 +1648,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of numerical digits required in the password - * for a particular admin or all admins that set retrictions on this user + * for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * This is the same value as set by @@ -1716,7 +1716,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of symbols required in the password - * for a particular admin or all admins that set retrictions on this user + * for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. This is the same value as * set by {@link #setPasswordMinimumSymbols(ComponentName, int)} @@ -1783,7 +1783,7 @@ public class DevicePolicyManager { /** * Retrieve the current number of non-letter characters required in the password - * for a particular admin or all admins that set retrictions on this user + * for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * This is the same value as set by @@ -1915,7 +1915,7 @@ public class DevicePolicyManager { /** * Get the current password expiration time for a particular admin or all admins that set - * retrictions on this user and its participating profiles. Restrictions on profiles that have + * restrictions on this user and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. If admin is {@code null}, then a composite * of all expiration times is returned - which will be the minimum of all of them. * @@ -1939,7 +1939,7 @@ public class DevicePolicyManager { /** * Retrieve the current password history length for a particular admin or all admins that - * set retrictions on this user and its participating profiles. Restrictions on profiles that + * set restrictions on this user and its participating profiles. Restrictions on profiles that * have a separate challenge are not taken into account. * *

This method can be called on the {@link DevicePolicyManager} instance @@ -2121,7 +2121,7 @@ public class DevicePolicyManager { /** * Retrieve the current maximum number of login attempts that are allowed before the device - * or profile is wiped, for a particular admin or all admins that set retrictions on this user + * or profile is wiped, for a particular admin or all admins that set restrictions on this user * and its participating profiles. Restrictions on profiles that have a separate challenge are * not taken into account. * @@ -2262,7 +2262,7 @@ public class DevicePolicyManager { /** * Retrieve the current maximum time to unlock for a particular admin or all admins that set - * retrictions on this user and its participating profiles. Restrictions on profiles that have + * restrictions on this user and its participating profiles. Restrictions on profiles that have * a separate challenge are not taken into account. * *

This method can be called on the {@link DevicePolicyManager} instance @@ -2510,6 +2510,8 @@ public class DevicePolicyManager { /** * Result code for {@link #setStorageEncryption} and {@link #getStorageEncryptionStatus}: * indicating that encryption is active. + *

+ * Also see {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}. */ public static final int ENCRYPTION_STATUS_ACTIVE = 3; @@ -2522,7 +2524,11 @@ public class DevicePolicyManager { /** * Result code for {@link #getStorageEncryptionStatus}: - * indicating that encryption is active and the encryption key is tied to the user. + * indicating that encryption is active and the encryption key is tied to the user or profile. + *

+ * This value is only returned to apps targeting API level 24 and above. For apps targeting + * earlier API levels, {@link #ENCRYPTION_STATUS_ACTIVE} is returned, even if the + * encryption key is specific to the user or profile. */ public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; @@ -2649,7 +2655,7 @@ public class DevicePolicyManager { /** * Called by an application that is administering the device to * determine the current encryption status of the device. - * + *

* Depending on the returned status code, the caller may proceed in different * ways. If the result is {@link #ENCRYPTION_STATUS_UNSUPPORTED}, the * storage system does not support encryption. If the @@ -2657,13 +2663,14 @@ public class DevicePolicyManager { * #ACTION_START_ENCRYPTION} to begin the process of encrypting or decrypting the * storage. If the result is {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, the * storage system has enabled encryption but no password is set so further action - * may be required. If the result is {@link #ENCRYPTION_STATUS_ACTIVATING} or - * {@link #ENCRYPTION_STATUS_ACTIVE}, no further action is required. + * may be required. If the result is {@link #ENCRYPTION_STATUS_ACTIVATING}, + * {@link #ENCRYPTION_STATUS_ACTIVE} or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}, + * no further action is required. * * @return current status of encryption. The value will be one of * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, * {@link #ENCRYPTION_STATUS_ACTIVATING}, {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, - * or {@link #ENCRYPTION_STATUS_ACTIVE}. + * {@link #ENCRYPTION_STATUS_ACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}. */ public int getStorageEncryptionStatus() { throwIfParentInstance("getStorageEncryptionStatus"); @@ -3341,7 +3348,7 @@ public class DevicePolicyManager { /** * Determine whether or not features have been disabled in keyguard either by the calling - * admin, if specified, or all admins that set retrictions on this user and its participating + * admin, if specified, or all admins that set restrictions on this user and its participating * profiles. Restrictions on profiles that have a separate challenge are not taken into account. * *

This method can be called on the {@link DevicePolicyManager} instance @@ -6416,6 +6423,43 @@ public class DevicePolicyManager { } } + /** + * @hide + * @return whether {@link android.provider.Settings.Global#DEVICE_PROVISIONED} has ever been set + * to 1. + */ + public boolean isDeviceProvisioned() { + try { + return mService.isDeviceProvisioned(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Writes that the provisioning configuration has been applied. + */ + public void setDeviceProvisioningConfigApplied() { + try { + mService.setDeviceProvisioningConfigApplied(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * @hide + * @return whether the provisioning configuration has been applied. + */ + public boolean isDeviceProvisioningConfigApplied() { + try { + return mService.isDeviceProvisioningConfigApplied(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + private void throwIfParentInstance(String functionName) { if (mParentInstance) { throw new SecurityException(functionName + " cannot be called on the parent instance"); diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index a1d7635f7e69e8f1e30730ea026bbf67841a0d79..8693041c1309804632f01db4c82949bebac2757f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -302,5 +302,9 @@ interface IDevicePolicyManager { boolean isUninstallInQueue(String packageName); void uninstallPackageWithActiveAdmins(String packageName); + boolean isDeviceProvisioned(); + boolean isDeviceProvisioningConfigApplied(); + void setDeviceProvisioningConfigApplied(); + boolean requireSecureKeyguard(int userHandle); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 478024d7d27cd5c5dab1e717e81f04dc01ca31cb..76828eeba78567bbb0c8aa9018a7b31e7aee964f 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -21,6 +21,8 @@ import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -223,8 +225,12 @@ public class FullBackup { final int mFullBackupContent; final PackageManager mPackageManager; + final StorageManager mStorageManager; final String mPackageName; + // lazy initialized, only when needed + private StorageVolume[] mVolumes = null; + /** * Parse out the semantic domains into the correct physical location. */ @@ -260,16 +266,41 @@ public class FullBackup { } else { return null; } + } else if (domainToken.startsWith(FullBackup.SHARED_PREFIX)) { + return sharedDomainToPath(domainToken); } // Not a supported location Log.i(TAG, "Unrecognized domain " + domainToken); return null; - } catch (IOException e) { + } catch (Exception e) { Log.i(TAG, "Error reading directory for domain: " + domainToken); return null; } } + + private String sharedDomainToPath(String domain) throws IOException { + // already known to start with SHARED_PREFIX, so we just look after that + final String volume = domain.substring(FullBackup.SHARED_PREFIX.length()); + final StorageVolume[] volumes = getVolumeList(); + final int volNum = Integer.parseInt(volume); + if (volNum < mVolumes.length) { + return volumes[volNum].getPathFile().getCanonicalPath(); + } + return null; + } + + private StorageVolume[] getVolumeList() { + if (mStorageManager != null) { + if (mVolumes == null) { + mVolumes = mStorageManager.getVolumeList(); + } + } else { + Log.e(TAG, "Unable to access Storage Manager"); + } + return mVolumes; + } + /** * A map of domain -> list of canonical file names in that domain that are to be included. * We keep track of the domain so that we can go through the file system in order later on. @@ -283,6 +314,7 @@ public class FullBackup { BackupScheme(Context context) { mFullBackupContent = context.getApplicationInfo().fullBackupContent; + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); mPackageManager = context.getPackageManager(); mPackageName = context.getPackageName(); diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java index f256a9536061b1e3426dcdd1bcb87e3f19d3f06c..f9874685167860be38fb0e5aa117dff18e0179b4 100644 --- a/core/java/android/app/backup/WallpaperBackupHelper.java +++ b/core/java/android/app/backup/WallpaperBackupHelper.java @@ -42,7 +42,7 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu // If 'true', then apply an acceptable-size heuristic at restore time, dropping back // to the factory default wallpaper if the restored one differs "too much" from the // device's preferred wallpaper image dimensions. - private static final boolean REJECT_OUTSIZED_RESTORE = true; + private static final boolean REJECT_OUTSIZED_RESTORE = false; // When outsized restore rejection is enabled, this is the maximum ratio between the // source and target image heights that will be permitted. The ratio is checked both @@ -60,6 +60,9 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu public static final String WALLPAPER_IMAGE = new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), "wallpaper").getAbsolutePath(); + public static final String WALLPAPER_ORIG_IMAGE = + new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), + "wallpaper_orig").getAbsolutePath(); public static final String WALLPAPER_INFO = new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM), "wallpaper_info.xml").getAbsolutePath(); @@ -199,7 +202,7 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu // since it does not exist anywhere other than the private wallpaper // file. Slog.d(TAG, "Applying restored wallpaper image."); - f.renameTo(new File(WALLPAPER_IMAGE)); + f.renameTo(new File(WALLPAPER_ORIG_IMAGE)); } } } diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 5823abf9d78fd8495538dde277b401641a205d95..734bf6917eca4d92b27086fa048c8c6194966395 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -165,6 +165,9 @@ public class JobInfo implements Parcelable { * network restrictions for the requesting app. Note that this flag alone * doesn't actually place your {@link JobService} in the foreground; you * still need to post the notification yourself. + *

+ * To use this flag, the caller must hold the + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission. * * @hide */ diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 24647f388fc128a3fd1a867a10654cc1d3fa772a..a0da258bdf1cd7f8aa5ffeb72f432e0936f67977 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -78,6 +78,13 @@ public final class UsageEvents implements Parcelable { */ public static final int USER_INTERACTION = 7; + /** + * An event type denoting that an action equivalent to a ShortcutInfo is taken by the user. + * + * @see android.content.pm.ShortcutManager#reportShortcutUsed(String) + */ + public static final int SHORTCUT_INVOCATION = 8; + /** * {@hide} */ @@ -104,6 +111,13 @@ public final class UsageEvents implements Parcelable { */ public Configuration mConfiguration; + /** + * ID of the shortcut. + * Only present for {@link #SHORTCUT_INVOCATION} event types. + * {@hide} + */ + public String mShortcutId; + /** * The package name of the source of this event. */ @@ -145,6 +159,16 @@ public final class UsageEvents implements Parcelable { public Configuration getConfiguration() { return mConfiguration; } + + /** + * Returns the ID of a {@link android.content.pm.ShortcutInfo} for this event + * if the event is of type {@link #SHORTCUT_INVOCATION}, otherwise it returns null. + * + * @see android.content.pm.ShortcutManager#reportShortcutUsed(String) + */ + public String getShortcutId() { + return mShortcutId; + } } // Only used when creating the resulting events. Not used for reading/unparceling. @@ -276,8 +300,13 @@ public final class UsageEvents implements Parcelable { p.writeInt(event.mEventType); p.writeLong(event.mTimeStamp); - if (event.mEventType == Event.CONFIGURATION_CHANGE) { - event.mConfiguration.writeToParcel(p, flags); + switch (event.mEventType) { + case Event.CONFIGURATION_CHANGE: + event.mConfiguration.writeToParcel(p, flags); + break; + case Event.SHORTCUT_INVOCATION: + p.writeString(event.mShortcutId); + break; } } @@ -301,11 +330,18 @@ public final class UsageEvents implements Parcelable { eventOut.mEventType = p.readInt(); eventOut.mTimeStamp = p.readLong(); - // Extract the configuration for configuration change events. - if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) { - eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p); - } else { - eventOut.mConfiguration = null; + // Fill out the event-dependant fields. + eventOut.mConfiguration = null; + eventOut.mShortcutId = null; + + switch (eventOut.mEventType) { + case Event.CONFIGURATION_CHANGE: + // Extract the configuration for configuration change events. + eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p); + break; + case Event.SHORTCUT_INVOCATION: + eventOut.mShortcutId = p.readString(); + break; } } diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index b6f1567568c29048ad6417cd4bedceb6d758fc67..a6f91fe1722758d7b620d42bd68a03ab91254265 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -55,6 +55,17 @@ public abstract class UsageStatsManagerInternal { */ public abstract void reportConfigurationChange(Configuration config, int userId); + /** + * Reports that an action equivalent to a ShortcutInfo is taken by the user. + * + * @param packageName The package name of the shortcut publisher + * @param shortcutId The ID of the shortcut in question + * @param userId The user in which the content provider was accessed. + * + * @see android.content.pm.ShortcutManager#reportShortcutUsed(String) + */ + public abstract void reportShortcutUsage(String packageName, String shortcutId, int userId); + /** * Reports that a content provider has been accessed by a foreground app. * @param name The authority of the content provider diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 2d9f4a71b0054845ad07b7aa38f933a80a47bae2..cd144695a989dd744faf1fc99f9db073aa5c886a 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -34,7 +34,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.SparseArray; import android.util.TypedValue; @@ -187,19 +186,28 @@ public class AppWidgetHost { idsToUpdate[i] = mViews.keyAt(i); } } - List updatedViews; - int[] updatedIds = new int[idsToUpdate.length]; + List updates; try { - updatedViews = sService.startListening( - mCallbacks, mContextOpPackageName, mHostId, idsToUpdate, updatedIds).getList(); + updates = sService.startListening( + mCallbacks, mContextOpPackageName, mHostId, idsToUpdate).getList(); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } - int N = updatedViews.size(); + int N = updates.size(); for (int i = 0; i < N; i++) { - updateAppWidgetView(updatedIds[i], updatedViews.get(i)); + PendingHostUpdate update = updates.get(i); + switch (update.type) { + case PendingHostUpdate.TYPE_VIEWS_UPDATE: + updateAppWidgetView(update.appWidgetId, update.views); + break; + case PendingHostUpdate.TYPE_PROVIDER_CHANGED: + onProviderChanged(update.appWidgetId, update.widgetInfo); + break; + case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED: + viewDataChanged(update.appWidgetId, update.viewId); + } } } diff --git a/core/java/android/appwidget/PendingHostUpdate.java b/core/java/android/appwidget/PendingHostUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..578031908f52e3ff935870b469d4da1619457f94 --- /dev/null +++ b/core/java/android/appwidget/PendingHostUpdate.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.appwidget; + +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.RemoteViews; + +/** + * @hide + */ +public class PendingHostUpdate implements Parcelable { + + static final int TYPE_VIEWS_UPDATE = 0; + static final int TYPE_PROVIDER_CHANGED = 1; + static final int TYPE_VIEW_DATA_CHANGED = 2; + + final int appWidgetId; + final int type; + RemoteViews views; + AppWidgetProviderInfo widgetInfo; + int viewId; + + public static PendingHostUpdate updateAppWidget(int appWidgetId, RemoteViews views) { + PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEWS_UPDATE); + update.views = views; + return update; + } + + public static PendingHostUpdate providerChanged(int appWidgetId, AppWidgetProviderInfo info) { + PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_PROVIDER_CHANGED); + update.widgetInfo = info; + return update; + } + + public static PendingHostUpdate viewDataChanged(int appWidgetId, int viewId) { + PendingHostUpdate update = new PendingHostUpdate(appWidgetId, TYPE_VIEW_DATA_CHANGED); + update.viewId = viewId; + return update; + } + + private PendingHostUpdate(int appWidgetId, int type) { + this.appWidgetId = appWidgetId; + this.type = type; + } + + private PendingHostUpdate(Parcel in) { + appWidgetId = in.readInt(); + type = in.readInt(); + + switch (type) { + case TYPE_VIEWS_UPDATE: + if (0 != in.readInt()) { + views = new RemoteViews(in); + } + break; + case TYPE_PROVIDER_CHANGED: + if (0 != in.readInt()) { + widgetInfo = new AppWidgetProviderInfo(in); + } + break; + case TYPE_VIEW_DATA_CHANGED: + viewId = in.readInt(); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(appWidgetId); + dest.writeInt(type); + switch (type) { + case TYPE_VIEWS_UPDATE: + writeNullParcelable(views, dest, flags); + break; + case TYPE_PROVIDER_CHANGED: + writeNullParcelable(widgetInfo, dest, flags); + break; + case TYPE_VIEW_DATA_CHANGED: + dest.writeInt(viewId); + break; + } + } + + private void writeNullParcelable(Parcelable p, Parcel dest, int flags) { + if (p != null) { + dest.writeInt(1); + p.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + } + + /** + * Parcelable.Creator that instantiates PendingHostUpdate objects + */ + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public PendingHostUpdate createFromParcel(Parcel parcel) { + return new PendingHostUpdate(parcel); + } + + public PendingHostUpdate[] newArray(int size) { + return new PendingHostUpdate[size]; + } + }; +} diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index f66b5ff466c3f790d84aabe3b330edc4b60ae8ad..353c6400ffd74d8ee1446df58660b7eb7979b290 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -30,8 +30,11 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -114,7 +117,8 @@ public final class BluetoothA2dp implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothA2dp mService; + private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("mServiceLock") private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -122,25 +126,27 @@ public final class BluetoothA2dp implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } + if (VDBG) Log.d(TAG, "Unbinding service..."); + try { + mServiceLock.writeLock().lock(); + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); } } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG,"",re); + try { + mServiceLock.readLock().lock(); + if (mService == null) { + if (VDBG) Log.d(TAG,"Binding service..."); + doBind(); } + } catch (Exception re) { + Log.e(TAG,"",re); + } finally { + mServiceLock.readLock().unlock(); } } } @@ -189,15 +195,16 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - synchronized (mConnection) { + try { + mServiceLock.writeLock().lock(); if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } + mService = null; + mContext.unbindService(mConnection); } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); } } @@ -229,17 +236,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && + isValidDevice(device)) { return mService.connect(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -270,17 +280,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && + isValidDevice(device)) { return mService.disconnect(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -288,16 +301,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.getConnectedDevices(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); } /** @@ -305,16 +321,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); } /** @@ -322,17 +341,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.getConnectionState(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.STATE_DISCONNECTED; } /** @@ -352,21 +374,24 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON){ - return false; - } - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } return mService.setPriority(device, priority); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -385,17 +410,20 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.getPriority(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; } /** @@ -406,16 +434,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.isAvrcpAbsoluteVolumeSupported(); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -433,16 +464,17 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public void adjustAvrcpAbsoluteVolume(int direction) { if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { mService.adjustAvrcpAbsoluteVolume(direction); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); - return; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); } /** @@ -453,16 +485,17 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { mService.setAvrcpAbsoluteVolume(volume); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - return; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); } /** @@ -473,17 +506,20 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.isA2dpPlaying(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -534,7 +570,12 @@ public final class BluetoothA2dp implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dp.Stub.asInterface(service); + try { + mServiceLock.writeLock().lock(); + mService = IBluetoothA2dp.Stub.asInterface(service); + } finally { + mServiceLock.writeLock().unlock(); + } if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); @@ -542,7 +583,12 @@ public final class BluetoothA2dp implements BluetoothProfile { } public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + try { + mServiceLock.writeLock().lock(); + mService = null; + } finally { + mServiceLock.writeLock().unlock(); + } if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ad628a42017b0204c1ccccdcb3258c9f981cd129..6f2c207a59576c6291984bfa4908ca60b54917af 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -92,8 +92,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * *

*

Developer Guides

- *

For more information about using Bluetooth, read the - * Bluetooth developer guide. + *

+ * For more information about using Bluetooth, read the Bluetooth developer + * guide. + *

*
* * {@see BluetoothDevice} diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 87be4a8d7a3d4911871ec472f111e610ebb6250a..9439aa4ab3c7c65807dd2191233ca01826daa5c8 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -59,8 +59,11 @@ import java.util.UUID; * *
*

Developer Guides

- *

For more information about using Bluetooth, read the - * Bluetooth developer guide.

+ *

+ * For more information about using Bluetooth, read the Bluetooth developer + * guide. + *

*
* * {@see BluetoothAdapter} diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index da810329aa60ed45940da50ab672221a992a94cf..c7c9c3f2a43855b95d1361d5fe7f5dca2e2fc122 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -241,6 +241,46 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ + + /** + * Intent used to broadcast the headset's indicator status + * + *

This intent will have 3 extras: + *

    + *
  • {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by + the headset ( as indicated by AT+BIND + command in the SLC sequence).or whose value + is changed (indicated by AT+BIEV command)
  • + *
  • {@link #EXTRA_IND_VALUE}- The updated value of headset indicator.
  • + *
  • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
  • + *
+ *

{@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are + * given an assigned number. Below shows the assigned number of Indicator added so far + * - Enhanced Safety - 1 + *

Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * @hide + */ + public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = + "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; + + /** + * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG) + * that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_ID = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; + + /** + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the value of the Headset indicator that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_VALUE = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; + public static final int STATE_AUDIO_CONNECTED = 12; @@ -994,7 +1034,32 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { + /** + * Send Headset the BIND response from AG to report change in the status of the + * HF indicators to the headset + * + * @param ind_id Assigned Number of the indicator (defined by SIG) + * @param ind_status + * 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 ind_id, boolean ind_status) { + if (mService != null && isEnabled()) { + try { + mService.bindResponse(ind_id, ind_status); + } 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 public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 35437a1fd7078a99ed39f6eb65fb1e123ec86939..00058a979094f3a13dda2773e434fe54e49bdc83 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -38,8 +38,11 @@ import java.util.List; * *

*

Developer Guides

- *

For more information about using BLUETOOTH, read the - * Bluetooth developer guide.

+ *

+ * For more information about using BLUETOOTH, read the Bluetooth developer + * guide. + *

*
* * @see Context#getSystemService diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index 014cb22c57b389698ef670b1a04407c790cef177..e70c936e253e1b8bcf2ea91ab751290bd335e39b 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -138,7 +138,7 @@ public final class BluetoothSap implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothMap.class.getName()); + Intent intent = new Intent(IBluetoothSap.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index eae6e8db0a650f83492414c06048a727cf684a57..63009688a09c6fa8cc84411960bc49912faba6c1 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -588,22 +588,19 @@ public final class BluetoothSocket implements Closeable { if(length <= mMaxTxPacketSize) { mSocketOS.write(b, offset, length); } else { - int tmpOffset = offset; - int tmpLength = mMaxTxPacketSize; - int endIndex = offset + length; - boolean done = false; if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" + "Packet will be divided into SDU packets of size " + mMaxTxPacketSize); - do{ + int tmpOffset = offset; + int bytesToWrite = length; + while (bytesToWrite > 0) { + int tmpLength = (bytesToWrite > mMaxTxPacketSize) + ? mMaxTxPacketSize + : bytesToWrite; mSocketOS.write(b, tmpOffset, tmpLength); - tmpOffset += mMaxTxPacketSize; - if((tmpOffset + mMaxTxPacketSize) > endIndex) { - tmpLength = endIndex - tmpOffset; - done = true; - } - } while(!done); - + tmpOffset += tmpLength; + bytesToWrite -= tmpLength; + } } } else { mSocketOS.write(b, offset, length); diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 0bb4088f62c9359cbc454b1edc08b13cc29be986..6ad442b6138d8cae33b2b206517b2efaf1bff852 100755 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -59,4 +59,5 @@ interface IBluetoothHeadset { String number, int type); boolean enableWBS(); boolean disableWBS(); + void bindResponse(int ind_id, boolean ind_status); } diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index bd8c6c9687f1aea6f2cc2e609f59d29e620e51a9..592e6b305e21ee02f61578363d8b11e5de1b2546 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -36,6 +36,7 @@ interface IBluetoothManager boolean enable(String callingPackage); boolean enableNoAutoConnect(); boolean disable(boolean persist); + int getState(); IBluetoothGatt getBluetoothGatt(); String getAddress(); diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index c365e9ee9549417a27e5d53a85a32137fed67c55..d9816a64db3a7a5f0719ded6aea5fe8369d1bf22 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -191,6 +191,14 @@ public class ClipData implements Parcelable { final Intent mIntent; Uri mUri; + /** @hide */ + public Item(Item other) { + mText = other.mText; + mHtmlText = other.mHtmlText; + mIntent = other.mIntent; + mUri = other.mUri; + } + /** * Create an Item consisting of a single block of (possibly styled) text. */ @@ -816,6 +824,11 @@ public class ClipData implements Parcelable { return mItems.get(index); } + /** @hide */ + public void setItemAt(int index, Item item) { + mItems.set(index, item); + } + /** * Prepare this {@link ClipData} to leave an app process. * diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index f1f71950e3326ca5aa88592892cbae8fbf1bf2f9..1c8c69ff61ad8b3dd1019f269b5e64d2bf5b066a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -34,6 +34,7 @@ import android.database.CrossProcessCursorWrapper; import android.database.Cursor; import android.database.IContentObserver; import android.graphics.Point; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; @@ -51,6 +52,7 @@ import android.util.EventLog; import android.util.Log; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.MimeIconUtils; import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; @@ -2696,4 +2698,9 @@ public abstract class ContentResolver { public int resolveUserId(Uri uri) { return ContentProvider.getUserIdFromUri(uri, mContext.getUserId()); } + + /** @hide */ + public Drawable getTypeDrawable(String mimeType) { + return MimeIconUtils.loadMimeIcon(mContext, mimeType); + } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bdf888f59715119ba48c865ffcd97b3a624945e7..3f18ea91c99a3e9482f289fbd36742b651be2f8d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3615,12 +3615,11 @@ public abstract class Context { public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; /** - * TODO Javadoc + * Use with {@link #getSystemService} to retrieve a + * {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service. * * @see #getSystemService * @see android.content.pm.ShortcutManager - * - * @hide */ public static final String SHORTCUT_SERVICE = "shortcut"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7a721083fe4b693f6278a445d49e89537f388230..659cd689417c71322c09c68b7bfee96ab2bda960 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2227,6 +2227,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"; + /** + * Broadcast Action: preferred activities have changed *explicitly*. + * + *

Note there are cases where a preferred activity is invalidated *implicitly*, e.g. + * when an app is installed or uninstalled, but in such cases this broadcast will *not* + * be sent. + * + * {@link #EXTRA_USER_HANDLE} contains the user ID in question. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PREFERRED_ACTIVITY_CHANGED = + "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED"; + + /** * Broadcast Action: The current system wallpaper has changed. See * {@link android.app.WallpaperManager} for retrieving the new wallpaper. @@ -3189,6 +3205,14 @@ public class Intent implements Parcelable, Cloneable { /** {@hide} */ public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"; + /** + * Boolean intent extra to be used with {@link ACTION_MASTER_CLEAR} in order to force a factory + * reset even if {@link android.os.UserManager.DISALLOW_FACTORY_RESET} is set. + * @hide + */ + public static final String EXTRA_FORCE_MASTER_CLEAR = + "android.intent.extra.FORCE_MASTER_CLEAR"; + /** * Broadcast action: report that a settings element is being restored from backup. The intent * contains three extras: EXTRA_SETTING_NAME is a string naming the restored setting, diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index c5e0ea727869217d971e7ff641c596762e526577..3faf13b601ad3b3be2d15f4944aad1c973e53370 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -44,7 +44,7 @@ import java.io.PrintWriter; *

*

Developer Guides

*

For more information about using loaders, read the - * Loaders developer guide.

+ * Loaders developer guide.

*
* * @param The result returned when the load is complete diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index 7f9e17641a19fbd4dc64613924e8a6c7cc8a3863..4b09feda21736bb090bebdc0f07e85b9f5200137 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -72,7 +72,9 @@ public interface SharedPreferences { * {@link #commit} or {@link #apply} are called. * * @param key The name of the preference to modify. - * @param value The new value for the preference. + * @param value The new value for the preference. Passing {@code null} + * for this argument is equivalent to calling {@link #remove(String)} with + * this key. * * @return Returns a reference to the same Editor object, so you can * chain put calls together. diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java index afb4c30d6a996369c9e695cf2f17e8b1990a47d1..fc3b95850d21e055e4d11508cad309c98c5202d3 100644 --- a/core/java/android/content/pm/EphemeralResolveInfo.java +++ b/core/java/android/content/pm/EphemeralResolveInfo.java @@ -27,6 +27,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Information about an ephemeral application. @@ -37,10 +38,7 @@ public final class EphemeralResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ public static final String SHA_ALGORITHM = "SHA-256"; - /** Full digest of the domain hash */ - private final byte[] mDigestBytes; - /** The first 4 bytes of the domain hash */ - private final int mDigestPrefix; + private final EphemeralDigest mDigest; private final String mPackageName; /** The filters used to match domain */ private final List mFilters = new ArrayList(); @@ -55,29 +53,23 @@ public final class EphemeralResolveInfo implements Parcelable { throw new IllegalArgumentException(); } - mDigestBytes = generateDigest(uri); - mDigestPrefix = - mDigestBytes[0] << 24 - | mDigestBytes[1] << 16 - | mDigestBytes[2] << 8 - | mDigestBytes[3] << 0; + mDigest = new EphemeralDigest(uri, 0xFFFFFFFF, -1); mFilters.addAll(filters); mPackageName = packageName; } EphemeralResolveInfo(Parcel in) { - mDigestBytes = in.createByteArray(); - mDigestPrefix = in.readInt(); + mDigest = in.readParcelable(null /*loader*/); mPackageName = in.readString(); in.readList(mFilters, null /*loader*/); } public byte[] getDigestBytes() { - return mDigestBytes; + return mDigest.getDigestBytes()[0]; } public int getDigestPrefix() { - return mDigestPrefix; + return mDigest.getDigestPrefix()[0]; } public String getPackageName() { @@ -88,16 +80,6 @@ public final class EphemeralResolveInfo implements Parcelable { return mFilters; } - private static byte[] generateDigest(Uri uri) { - try { - final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); - final byte[] hostBytes = uri.getHost().getBytes(); - return digest.digest(hostBytes); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("could not find digest algorithm"); - } - } - @Override public int describeContents() { return 0; @@ -105,8 +87,7 @@ public final class EphemeralResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeByteArray(mDigestBytes); - out.writeInt(mDigestPrefix); + out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); } @@ -136,4 +117,121 @@ public final class EphemeralResolveInfo implements Parcelable { return mResolveInfo; } } + + /** + * Helper class to generate and store each of the digests and prefixes + * sent to the Ephemeral Resolver. + *

+ * Since intent filters may want to handle multiple hosts within a + * domain [eg “*.google.com”], the resolver is presented with multiple + * hash prefixes. For example, "a.b.c.d.e" generates digests for + * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e". + * + * @hide + */ + public static final class EphemeralDigest implements Parcelable { + /** Full digest of the domain hashes */ + private final byte[][] mDigestBytes; + /** The first 4 bytes of the domain hashes */ + private final int[] mDigestPrefix; + + public EphemeralDigest(@NonNull Uri uri, int digestMask, int maxDigests) { + if (uri == null) { + throw new IllegalArgumentException(); + } + mDigestBytes = generateDigest(uri, maxDigests); + mDigestPrefix = new int[mDigestBytes.length]; + for (int i = 0; i < mDigestBytes.length; i++) { + mDigestPrefix[i] = + ((mDigestBytes[i][0] & 0xFF) << 24 + | (mDigestBytes[i][1] & 0xFF) << 16 + | (mDigestBytes[i][2] & 0xFF) << 8 + | (mDigestBytes[i][3] & 0xFF) << 0) + & digestMask; + } + } + + private static byte[][] generateDigest(Uri uri, int maxDigests) { + ArrayList digests = new ArrayList<>(); + try { + final String host = uri.getHost().toLowerCase(Locale.ENGLISH); + final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); + if (maxDigests <= 0) { + final byte[] hostBytes = host.getBytes(); + digests.add(digest.digest(hostBytes)); + } else { + int prevDot = host.lastIndexOf('.'); + prevDot = host.lastIndexOf('.', prevDot - 1); + // shortcut for short URLs + if (prevDot < 0) { + digests.add(digest.digest(host.getBytes())); + } else { + byte[] hostBytes = host.substring(prevDot + 1, host.length()).getBytes(); + digests.add(digest.digest(hostBytes)); + int digestCount = 1; + while (prevDot >= 0 && digestCount < maxDigests) { + prevDot = host.lastIndexOf('.', prevDot - 1); + hostBytes = host.substring(prevDot + 1, host.length()).getBytes(); + digests.add(digest.digest(hostBytes)); + digestCount++; + } + } + } + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("could not find digest algorithm"); + } + return digests.toArray(new byte[digests.size()][]); + } + + EphemeralDigest(Parcel in) { + final int digestCount = in.readInt(); + if (digestCount == -1) { + mDigestBytes = null; + } else { + mDigestBytes = new byte[digestCount][]; + for (int i = 0; i < digestCount; i++) { + mDigestBytes[i] = in.createByteArray(); + } + } + mDigestPrefix = in.createIntArray(); + } + + public byte[][] getDigestBytes() { + return mDigestBytes; + } + + public int[] getDigestPrefix() { + return mDigestPrefix; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + if (mDigestBytes == null) { + out.writeInt(-1); + } else { + out.writeInt(mDigestBytes.length); + for (int i = 0; i < mDigestBytes.length; i++) { + out.writeByteArray(mDigestBytes[i]); + } + } + out.writeIntArray(mDigestPrefix); + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public EphemeralDigest createFromParcel(Parcel in) { + return new EphemeralDigest(in); + } + + public EphemeralDigest[] newArray(int size) { + return new EphemeralDigest[size]; + } + }; + } } diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl index 8f38d6f90a7dee961cd0b91530732cce276fef77..467bd5f5e82bddfe34a76a8d7abeed9f41b3e1fc 100644 --- a/core/java/android/content/pm/IOtaDexopt.aidl +++ b/core/java/android/content/pm/IOtaDexopt.aidl @@ -41,9 +41,22 @@ interface IOtaDexopt { */ boolean isDone(); + /** + * Return the progress (0..1) made in this session. When {@link #isDone() isDone} returns + * true, the progress value will be 1. + */ + float getProgress(); + /** * Optimize the next package. Note: this command is synchronous, that is, only returns after * the package has been dexopted (or dexopting failed). + * + * Note: this will be removed after a transition period. Use nextDexoptCommand instead. */ void dexoptNextPackage(); + + /** + * Get the optimization parameters for the next package. + */ + String nextDexoptCommand(); } diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 2ba24f6eecdbaa4e2e2633bbcf12b89ea3340e70..1bf2ab0abbadafe0aad65f7232201ea413305642 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -28,6 +28,8 @@ interface IShortcutService { ParceledListSlice getDynamicShortcuts(String packageName, int userId); + ParceledListSlice getManifestShortcuts(String packageName, int userId); + boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, int userId); @@ -39,7 +41,12 @@ interface IShortcutService { boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId); - int getMaxDynamicShortcutCount(String packageName, int userId); + void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage, + int disabledMessageResId, int userId); + + void enableShortcuts(String packageName, in List shortcutIds, int userId); + + int getMaxShortcutCountPerActivity(String packageName, int userId); int getRemainingCallCount(String packageName, int userId); @@ -47,6 +54,8 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); + void reportShortcutUsed(String packageName, String shortcutId, int userId); + void resetThrottling(); // system only API for developer opsions void onApplicationActive(String packageName, int userId); // system only API for sysUI diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 8ca27c5e0087905763530df33f53acad750c3b7f..6b23da93bb8623a3e866a702557f97e7433900b3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -20,11 +20,18 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.ApplicationInfoFlags; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -34,8 +41,10 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; +import android.util.DisplayMetrics; import android.util.Log; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -156,18 +165,19 @@ public class LauncherApps { } /** - * Indicates that one or more shortcuts (which may be dynamic and/or pinned) + * Indicates that one or more shortcuts of any kind (dynamic, pinned, or manifest) * have been added, updated or removed. * *

Only the applications that are allowed to access the shortcut information, * as defined in {@link #hasShortcutHostPermission()}, will receive it. * * @param packageName The name of the package that has the shortcuts. - * @param shortcuts all shortcuts from the package (dynamic and/or pinned). Only "key" - * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. + * @param shortcuts All shortcuts from the package (dynamic, manifest and/or pinned). + * Only "key" information will be provided, as defined in + * {@link ShortcutInfo#hasKeyFieldsOnly()}. * @param user The UserHandle of the profile that generated the change. * - * @hide + * @see ShortcutManager */ public void onShortcutsChanged(@NonNull String packageName, @NonNull List shortcuts, @NonNull UserHandle user) { @@ -176,31 +186,68 @@ public class LauncherApps { /** * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}. - * - * @hide */ public static class ShortcutQuery { /** * Include dynamic shortcuts in the result. */ - public static final int FLAG_GET_DYNAMIC = 1 << 0; + public static final int FLAG_MATCH_DYNAMIC = 1 << 0; + + /** @hide kept for unit tests */ + @Deprecated + public static final int FLAG_GET_DYNAMIC = FLAG_MATCH_DYNAMIC; /** * Include pinned shortcuts in the result. */ - public static final int FLAG_GET_PINNED = 1 << 1; + public static final int FLAG_MATCH_PINNED = 1 << 1; + + /** @hide kept for unit tests */ + @Deprecated + public static final int FLAG_GET_PINNED = FLAG_MATCH_PINNED; /** - * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which - * fields are available. + * Include manifest shortcuts in the result. + */ + public static final int FLAG_MATCH_MANIFEST = 1 << 3; + + /** @hide kept for unit tests */ + @Deprecated + public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; + + /** @hide */ + public static final int FLAG_MATCH_ALL_KINDS = + FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST; + + /** @hide kept for unit tests */ + @Deprecated + public static final int FLAG_GET_ALL_KINDS = FLAG_MATCH_ALL_KINDS; + + /** + * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()}'s javadoc to + * see which fields fields "key". + * This allows quicker access to shortcut information in order to + * determine whether the caller's in-memory cache needs to be updated. + * + *

Typically, launcher applications cache all or most shortcut information + * in memory in order to show shortcuts without a delay. + * + * When a given launcher application wants to update its cache, such as when its process + * restarts, it can fetch shortcut information with this flag. + * The application can then check {@link ShortcutInfo#getLastChangedTimestamp()} for each + * shortcut, fetching a shortcut's non-key information only if that shortcut has been + * updated. + * + * @see ShortcutManager */ public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2; /** @hide */ @IntDef(flag = true, value = { - FLAG_GET_DYNAMIC, - FLAG_GET_PINNED, + FLAG_MATCH_DYNAMIC, + FLAG_MATCH_PINNED, + FLAG_MATCH_MANIFEST, FLAG_GET_KEY_FIELDS_ONLY, }) @Retention(RetentionPolicy.SOURCE) @@ -224,40 +271,56 @@ public class LauncherApps { } /** - * If non-zero, returns only shortcuts that have been added or updated since the timestamp, - * which is a milliseconds since the Epoch. + * If non-zero, returns only shortcuts that have been added or updated + * since the given timestamp, expressed in milliseconds since the Epoch—see + * {@link System#currentTimeMillis()}. */ - public void setChangedSince(long changedSince) { + public ShortcutQuery setChangedSince(long changedSince) { mChangedSince = changedSince; + return this; } /** * If non-null, returns only shortcuts from the package. */ - public void setPackage(@Nullable String packageName) { + public ShortcutQuery setPackage(@Nullable String packageName) { mPackage = packageName; + return this; } /** * If non-null, return only the specified shortcuts by ID. When setting this field, - * a packange name must also be set with {@link #setPackage}. + * a package name must also be set with {@link #setPackage}. */ - public void setShortcutIds(@Nullable List shortcutIds) { + public ShortcutQuery setShortcutIds(@Nullable List shortcutIds) { mShortcutIds = shortcutIds; + return this; } /** - * If non-null, returns only shortcuts associated with the activity. + * If non-null, returns only shortcuts associated with the activity; i.e. + * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal + * to {@code activity}. */ - public void setActivity(@Nullable ComponentName activity) { + public ShortcutQuery setActivity(@Nullable ComponentName activity) { mActivity = activity; + return this; } /** - * Set query options. + * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, + * no shortcuts will be returned. + * + *

    + *
  • {@link #FLAG_MATCH_DYNAMIC} + *
  • {@link #FLAG_MATCH_PINNED} + *
  • {@link #FLAG_MATCH_MANIFEST} + *
  • {@link #FLAG_GET_KEY_FIELDS_ONLY} + *
*/ - public void setQueryFlags(@QueryFlags int queryFlags) { + public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) { mQueryFlags = queryFlags; + return this; } } @@ -422,12 +485,16 @@ public class LauncherApps { * *

Only the default launcher can access the shortcut information. * - *

Note when this method returns {@code false}, that may be a temporary situation because + *

Note when this method returns {@code false}, it may be a temporary situation because * the user is trying a new launcher application. The user may decide to change the default - * launcher to the calling application again, so even if a launcher application loses + * launcher back to the calling application again, so even if a launcher application loses * this permission, it does not have to purge pinned shortcut information. + * If the calling launcher application contains pinned shortcuts, they will still work, + * even though the caller no longer has the shortcut host permission. * - * @hide + * @throws IllegalStateException when the user is locked. + * + * @see ShortcutManager */ public boolean hasShortcutHostPermission() { try { @@ -438,7 +505,7 @@ public class LauncherApps { } /** - * Returns the IDs of {@link ShortcutInfo}s that match {@code query}. + * Returns {@link ShortcutInfo}s that match {@code query}. * *

Callers must be allowed to access the shortcut information, as defined in {@link * #hasShortcutHostPermission()}. @@ -447,8 +514,10 @@ public class LauncherApps { * @param user The UserHandle of the profile. * * @return the IDs of {@link ShortcutInfo}s that match the query. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. * - * @hide + * @see ShortcutManager */ @Nullable public List getShortcuts(@NonNull ShortcutQuery query, @@ -467,12 +536,13 @@ public class LauncherApps { * @hide // No longer used. Use getShortcuts() instead. Kept for unit tests. */ @Nullable + @Deprecated public List getShortcutInfo(@NonNull String packageName, @NonNull List ids, @NonNull UserHandle user) { final ShortcutQuery q = new ShortcutQuery(); q.setPackage(packageName); q.setShortcutIds(ids); - q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED); + q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); return getShortcuts(q, user); } @@ -482,14 +552,16 @@ public class LauncherApps { *

This API is NOT cumulative; this will replace all pinned shortcuts for the package. * However, different launchers may have different set of pinned shortcuts. * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. + *

The calling launcher application must be allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}. * * @param packageName The target package name. * @param shortcutIds The IDs of the shortcut to be pinned. * @param user The UserHandle of the profile. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. * - * @hide + * @see ShortcutManager */ public void pinShortcuts(@NonNull String packageName, @NonNull List shortcutIds, @NonNull UserHandle user) { @@ -503,6 +575,7 @@ public class LauncherApps { /** * @hide kept for testing. */ + @Deprecated public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) { return shortcut.getIconResourceId(); } @@ -510,46 +583,29 @@ public class LauncherApps { /** * @hide kept for testing. */ + @Deprecated public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { final ShortcutQuery q = new ShortcutQuery(); q.setPackage(packageName); q.setShortcutIds(Arrays.asList(shortcutId)); - q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED); + q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS); final List shortcuts = getShortcuts(q, user); return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0; } /** - * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file - * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}). - * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. - * - * @param shortcut The target shortcut. - * - * @hide + * @hide internal/unit tests only */ public ParcelFileDescriptor getShortcutIconFd( @NonNull ShortcutInfo shortcut) { - return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(), + return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId()); } /** - * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file - * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}). - * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. - * - * @param packageName The target package name. - * @param shortcutId The ID of the shortcut to lad rom. - * @param user The UserHandle of the profile. - * - * @hide + * @hide internal/unit tests only */ public ParcelFileDescriptor getShortcutIconFd( @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) { @@ -567,55 +623,133 @@ public class LauncherApps { } /** - * Launches a shortcut. + * Returns the icon for this shortcut, without any badging for the profile. * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. + *

The calling launcher application must be allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}. + * + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * + * @return The drawable associated with the shortcut. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. + * + * @see ShortcutManager + * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int) + * @see DisplayMetrics + */ + public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) { + if (shortcut.hasIconFile()) { + final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut); + if (pfd == null) { + return null; + } + try { + final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor()); + return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp); + } finally { + try { + pfd.close(); + } catch (IOException ignore) { + } + } + } else if (shortcut.hasIconResource()) { + try { + final int resId = shortcut.getIconResourceId(); + if (resId == 0) { + return null; // Shouldn't happen but just in case. + } + final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(), + /* flags =*/ 0, shortcut.getUserHandle()); + final Resources res = mContext.getPackageManager().getResourcesForApplication(ai); + return res.getDrawableForDensity(resId, density); + } catch (NameNotFoundException | Resources.NotFoundException e) { + return null; + } + } else { + return null; // Has no icon. + } + } + + /** + * Returns the shortcut icon with badging appropriate for the profile. + * + *

The calling launcher application must be allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}. + * + * @param density Optional density for the icon, or 0 to use the default density. Use + * @return A badged icon for the shortcut. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. + * + * @see ShortcutManager + * @see #getShortcutIconDrawable(ShortcutInfo, int) + * @see DisplayMetrics + */ + public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) { + final Drawable originalIcon = getShortcutIconDrawable(shortcut, density); + + return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon( + originalIcon, shortcut.getUserHandle()); + } + + /** + * Starts a shortcut. + * + *

The calling launcher application must be allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}. * * @param packageName The target shortcut package name. * @param shortcutId The target shortcut ID. * @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param startActivityOptions Options to pass to startActivity. * @param user The UserHandle of the profile. - * @return {@code false} when the shortcut is no longer valid (e.g. the creator application - * has been uninstalled). {@code true} when the shortcut is still valid. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. * - * @hide + * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. + * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) */ - public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId, + public void startShortcut(@NonNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, @NonNull UserHandle user) { - return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, + startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions, user.getIdentifier()); } /** * Launches a shortcut. * - *

Callers must be allowed to access the shortcut information, as defined in {@link - * #hasShortcutHostPermission()}. + *

The calling launcher application must be allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}. * * @param shortcut The target shortcut. * @param sourceBounds The Rect containing the source bounds of the clicked icon. * @param startActivityOptions Options to pass to startActivity. - * @return {@code false} when the shortcut is no longer valid (e.g. the creator application - * has been uninstalled). {@code true} when the shortcut is still valid. + * @throws IllegalStateException when the user is locked, or when the {@code user} user + * is locked or not running. * - * @hide + * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g. + * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc) */ - public boolean startShortcut(@NonNull ShortcutInfo shortcut, + public void startShortcut(@NonNull ShortcutInfo shortcut, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) { - return startShortcut(shortcut.getPackageName(), shortcut.getId(), + startShortcut(shortcut.getPackage(), shortcut.getId(), sourceBounds, startActivityOptions, shortcut.getUserId()); } - private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId, + private void startShortcut(@NonNull String packageName, @NonNull String shortcutId, @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions, int userId) { try { - return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, + final boolean success = + mService.startShortcut(mContext.getPackageName(), packageName, shortcutId, sourceBounds, startActivityOptions, userId); + if (!success) { + throw new ActivityNotFoundException("Shortcut could not be started"); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index dc349bf9b8d903dc17e60a6b705bbbf3c9c6e7e7..3e84a8179e633c4c4592a93dbc7a50238023ead6 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -132,6 +132,9 @@ public abstract class PackageManager { MATCH_SYSTEM_ONLY, MATCH_FACTORY_ONLY, MATCH_DEBUG_TRIAGED_MISSING, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface PackageInfoFlags {} @@ -143,6 +146,9 @@ public abstract class PackageManager { MATCH_UNINSTALLED_PACKAGES, MATCH_SYSTEM_ONLY, MATCH_DEBUG_TRIAGED_MISSING, + MATCH_DISABLED_UNTIL_USED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoFlags {} @@ -160,6 +166,9 @@ public abstract class PackageManager { MATCH_DIRECT_BOOT_UNAWARE, MATCH_SYSTEM_ONLY, MATCH_UNINSTALLED_PACKAGES, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ComponentInfoFlags {} @@ -178,6 +187,9 @@ public abstract class PackageManager { MATCH_DIRECT_BOOT_UNAWARE, MATCH_SYSTEM_ONLY, MATCH_UNINSTALLED_PACKAGES, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ResolveInfoFlags {} @@ -2883,6 +2895,7 @@ public abstract class PackageManager { * * @see #GET_META_DATA * @see #GET_SHARED_LIBRARY_FILES + * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS * @see #MATCH_SYSTEM_ONLY * @see #MATCH_UNINSTALLED_PACKAGES */ @@ -3511,6 +3524,7 @@ public abstract class PackageManager { * * @see #GET_META_DATA * @see #GET_SHARED_LIBRARY_FILES + * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS * @see #MATCH_SYSTEM_ONLY * @see #MATCH_UNINSTALLED_PACKAGES */ diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 14f7727e61a02daaeb5ac9bbfd08789b35098812..d208fe7c10ca1c5f8bac3b1daba4d68313866ad1 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -157,7 +157,7 @@ public abstract class PackageManagerInternal { int deviceOwnerUserId, String deviceOwner, SparseArray profileOwners); /** - * Whether a package's data be cleared. + * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}. */ - public abstract boolean canPackageBeWiped(int userId, String packageName); + public abstract boolean isPackageDataProtected(int userId, String packageName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index a4601d2ccf3324449d72aee362b75e755be5c0d5..f2e3333b67da98d79de3b0f8473425f53c4d0e9c 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -274,6 +274,7 @@ public class PackageParser { final int nameRes; final int labelRes; final int iconRes; + final int roundIconRes; final int logoRes; final int bannerRes; @@ -281,7 +282,8 @@ public class PackageParser { TypedArray sa; ParsePackageItemArgs(Package _owner, String[] _outError, - int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes) { + int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes, + int _bannerRes) { owner = _owner; outError = _outError; nameRes = _nameRes; @@ -289,6 +291,7 @@ public class PackageParser { iconRes = _iconRes; logoRes = _logoRes; bannerRes = _bannerRes; + roundIconRes = _roundIconRes; } } @@ -300,10 +303,12 @@ public class PackageParser { int flags; ParseComponentArgs(Package _owner, String[] _outError, - int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes, + int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes, + int _bannerRes, String[] _sepProcesses, int _processRes, int _descriptionRes, int _enabledRes) { - super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes, _bannerRes); + super(_owner, _outError, _nameRes, _labelRes, _iconRes, _roundIconRes, _logoRes, + _bannerRes); sepProcesses = _sepProcesses; processRes = _processRes; descriptionRes = _descriptionRes; @@ -2338,11 +2343,7 @@ public class PackageParser { b.append(cls); return b.toString().intern(); } - if (c >= 'a' && c <= 'z') { - return cls.intern(); - } - outError[0] = "Bad class name " + cls + " in package " + pkg; - return null; + return cls.intern(); } private static String buildCompoundName(String pkg, @@ -2556,12 +2557,12 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPermissionGroup); - if (!parsePackageItemInfo(owner, perm.info, outError, - "", sa, + "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermissionGroup_name, com.android.internal.R.styleable.AndroidManifestPermissionGroup_label, com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon, + com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon, com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo, com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) { sa.recycle(); @@ -2602,10 +2603,11 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermission); if (!parsePackageItemInfo(owner, perm.info, outError, - "", sa, + "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermission_name, com.android.internal.R.styleable.AndroidManifestPermission_label, com.android.internal.R.styleable.AndroidManifestPermission_icon, + com.android.internal.R.styleable.AndroidManifestPermission_roundIcon, com.android.internal.R.styleable.AndroidManifestPermission_logo, com.android.internal.R.styleable.AndroidManifestPermission_banner)) { sa.recycle(); @@ -2675,10 +2677,11 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionTree); if (!parsePackageItemInfo(owner, perm.info, outError, - "", sa, + "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermissionTree_name, com.android.internal.R.styleable.AndroidManifestPermissionTree_label, com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, + com.android.internal.R.styleable.AndroidManifestPermissionTree_roundIcon, com.android.internal.R.styleable.AndroidManifestPermissionTree_logo, com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) { sa.recycle(); @@ -2725,6 +2728,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestInstrumentation_name, com.android.internal.R.styleable.AndroidManifestInstrumentation_label, com.android.internal.R.styleable.AndroidManifestInstrumentation_icon, + com.android.internal.R.styleable.AndroidManifestInstrumentation_roundIcon, com.android.internal.R.styleable.AndroidManifestInstrumentation_logo, com.android.internal.R.styleable.AndroidManifestInstrumentation_banner); mParseInstrumentationArgs.tag = ""; @@ -2790,15 +2794,21 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestApplication); - String name = sa.getNonConfigurationString( - com.android.internal.R.styleable.AndroidManifestApplication_name, 0); - if (name != null) { - ai.className = buildClassName(pkgName, name, outError); - if (ai.className == null) { - sa.recycle(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return false; - } + if (!parsePackageItemInfo(owner, ai, outError, + "", sa, false /*nameRequired*/, + com.android.internal.R.styleable.AndroidManifestApplication_name, + com.android.internal.R.styleable.AndroidManifestApplication_label, + com.android.internal.R.styleable.AndroidManifestApplication_icon, + com.android.internal.R.styleable.AndroidManifestApplication_roundIcon, + com.android.internal.R.styleable.AndroidManifestApplication_logo, + com.android.internal.R.styleable.AndroidManifestApplication_banner)) { + sa.recycle(); + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return false; + } + + if (ai.name != null) { + ai.className = ai.name; } String manageSpaceActivity = sa.getNonConfigurationString( @@ -2864,18 +2874,6 @@ public class PackageParser { } } - TypedValue v = sa.peekValue( - com.android.internal.R.styleable.AndroidManifestApplication_label); - if (v != null && (ai.labelRes=v.resourceId) == 0) { - ai.nonLocalizedLabel = v.coerceToString(); - } - - ai.icon = sa.getResourceId( - com.android.internal.R.styleable.AndroidManifestApplication_icon, 0); - ai.logo = sa.getResourceId( - com.android.internal.R.styleable.AndroidManifestApplication_logo, 0); - ai.banner = sa.getResourceId( - com.android.internal.R.styleable.AndroidManifestApplication_banner, 0); ai.theme = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_theme, 0); ai.descriptionRes = sa.getResourceId( @@ -3389,25 +3387,35 @@ public class PackageParser { return true; } - private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, - String[] outError, String tag, TypedArray sa, - int nameRes, int labelRes, int iconRes, int logoRes, int bannerRes) { + private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, + String[] outError, String tag, TypedArray sa, boolean nameRequired, + int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) { String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { - outError[0] = tag + " does not specify android:name"; - return false; - } - - outInfo.name - = buildClassName(owner.applicationInfo.packageName, name, outError); - if (outInfo.name == null) { - return false; + if (nameRequired) { + outError[0] = tag + " does not specify android:name"; + return false; + } + } else { + outInfo.name + = buildClassName(owner.applicationInfo.packageName, name, outError); + if (outInfo.name == null) { + return false; + } } - int iconVal = sa.getResourceId(iconRes, 0); - if (iconVal != 0) { - outInfo.icon = iconVal; + final boolean useRoundIcon = + Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon); + int roundIconVal = useRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0; + if (roundIconVal != 0) { + outInfo.icon = roundIconVal; outInfo.nonLocalizedLabel = null; + } else { + int iconVal = sa.getResourceId(iconRes, 0); + if (iconVal != 0) { + outInfo.icon = iconVal; + outInfo.nonLocalizedLabel = null; + } } int logoVal = sa.getResourceId(logoRes, 0); @@ -3441,6 +3449,7 @@ public class PackageParser { R.styleable.AndroidManifestActivity_name, R.styleable.AndroidManifestActivity_label, R.styleable.AndroidManifestActivity_icon, + R.styleable.AndroidManifestActivity_roundIcon, R.styleable.AndroidManifestActivity_logo, R.styleable.AndroidManifestActivity_banner, mSeparateProcesses, @@ -3815,6 +3824,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivityAlias_name, com.android.internal.R.styleable.AndroidManifestActivityAlias_label, com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, + com.android.internal.R.styleable.AndroidManifestActivityAlias_roundIcon, com.android.internal.R.styleable.AndroidManifestActivityAlias_logo, com.android.internal.R.styleable.AndroidManifestActivityAlias_banner, mSeparateProcesses, @@ -3969,6 +3979,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_name, com.android.internal.R.styleable.AndroidManifestProvider_label, com.android.internal.R.styleable.AndroidManifestProvider_icon, + com.android.internal.R.styleable.AndroidManifestProvider_roundIcon, com.android.internal.R.styleable.AndroidManifestProvider_logo, com.android.internal.R.styleable.AndroidManifestProvider_banner, mSeparateProcesses, @@ -4288,6 +4299,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_name, com.android.internal.R.styleable.AndroidManifestService_label, com.android.internal.R.styleable.AndroidManifestService_icon, + com.android.internal.R.styleable.AndroidManifestService_roundIcon, com.android.internal.R.styleable.AndroidManifestService_logo, com.android.internal.R.styleable.AndroidManifestService_banner, mSeparateProcesses, @@ -4604,8 +4616,16 @@ public class PackageParser { outInfo.nonLocalizedLabel = v.coerceToString(); } - outInfo.icon = sa.getResourceId( - com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0); + final boolean useRoundIcon = + Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon); + int roundIconVal = useRoundIcon ? sa.getResourceId( + com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0; + if (roundIconVal != 0) { + outInfo.icon = roundIconVal; + } else { + outInfo.icon = sa.getResourceId( + com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0); + } outInfo.logo = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0); @@ -5237,45 +5257,13 @@ public class PackageParser { public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { owner = args.owner; intents = new ArrayList(0); - String name = args.sa.getNonConfigurationString(args.nameRes, 0); - if (name == null) { - className = null; - args.outError[0] = args.tag + " does not specify android:name"; - return; - } - - outInfo.name - = buildClassName(owner.applicationInfo.packageName, name, args.outError); - if (outInfo.name == null) { + if (parsePackageItemInfo(args.owner, outInfo, args.outError, args.tag, args.sa, + true /*nameRequired*/, args.nameRes, args.labelRes, args.iconRes, + args.roundIconRes, args.logoRes, args.bannerRes)) { + className = outInfo.name; + } else { className = null; - args.outError[0] = args.tag + " does not have valid android:name"; - return; } - - className = outInfo.name; - - int iconVal = args.sa.getResourceId(args.iconRes, 0); - if (iconVal != 0) { - outInfo.icon = iconVal; - outInfo.nonLocalizedLabel = null; - } - - int logoVal = args.sa.getResourceId(args.logoRes, 0); - if (logoVal != 0) { - outInfo.logo = logoVal; - } - - int bannerVal = args.sa.getResourceId(args.bannerRes, 0); - if (bannerVal != 0) { - outInfo.banner = bannerVal; - } - - TypedValue v = args.sa.peekValue(args.labelRes); - if (v != null && (outInfo.labelRes=v.resourceId) == 0) { - outInfo.nonLocalizedLabel = v.coerceToString(); - } - - outInfo.packageName = owner.packageName; } public Component(final ParseComponentArgs args, final ComponentInfo outInfo) { diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 6162d1aeeceb7676ab00d9432d3e27cec3d232a1..a1103838280c0826f14c978be2c1ffa117867ab8 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.AtomicFile; import android.util.AttributeSet; +import android.util.IntArray; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -54,6 +55,7 @@ import java.io.InputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -344,6 +346,47 @@ public abstract class RegisteredServicesCache { } } + public void updateServices(int userId) { + if (DEBUG) { + Slog.d(TAG, "updateServices u" + userId); + } + List> allServices; + synchronized (mServicesLock) { + final UserServices user = findOrCreateUserLocked(userId); + // If services haven't been initialized yet - no updates required + if (user.services == null) { + return; + } + allServices = new ArrayList<>(user.services.values()); + } + IntArray updatedUids = null; + for (ServiceInfo service : allServices) { + int versionCode = service.componentInfo.applicationInfo.versionCode; + String pkg = service.componentInfo.packageName; + ApplicationInfo newAppInfo = null; + try { + newAppInfo = mContext.getPackageManager().getApplicationInfoAsUser(pkg, 0, userId); + } catch (NameNotFoundException e) { + // Package uninstalled - treat as null app info + } + // If package updated or removed + if ((newAppInfo == null) || (newAppInfo.versionCode != versionCode)) { + if (DEBUG) { + Slog.d(TAG, "Package " + pkg + " uid=" + service.uid + + " updated. New appInfo: " + newAppInfo); + } + if (updatedUids == null) { + updatedUids = new IntArray(); + } + updatedUids.add(service.uid); + } + } + if (updatedUids != null && updatedUids.size() > 0) { + int[] updatedUidsArray = updatedUids.toArray(); + generateServicesMap(updatedUidsArray, userId); + } + } + @VisibleForTesting protected boolean inSystemImage(int callerUid) { String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid); @@ -379,10 +422,11 @@ public abstract class RegisteredServicesCache { */ private void generateServicesMap(int[] changedUids, int userId) { if (DEBUG) { - Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = " + changedUids); + Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = " + + Arrays.toString(changedUids)); } - final ArrayList> serviceInfos = new ArrayList>(); + final ArrayList> serviceInfos = new ArrayList<>(); final List resolveInfos = queryIntentServices(userId); for (ResolveInfo resolveInfo : resolveInfos) { try { diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 4340d04cee69fab8ae3e34ec397a32aced99eb48..ed0ac53861764dc4f2d6b0814a8f60f19f34bf9b 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -19,56 +19,78 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.TaskStackBuilder; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherApps.ShortcutQuery; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArraySet; +import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.Set; -// TODO Enhance javadoc /** + * Represents a "launcher shortcut" that can be published via {@link ShortcutManager}. * - * Represents a shortcut from an application. - * - *

Notes about icons: - *

    - *
  • If an {@link Icon} is a resource, the system keeps the package name and the resource ID. - * Otherwise, the bitmap is fetched when it's registered to ShortcutManager, - * then shrunk if necessary, and persisted. - *
  • The system disallows byte[] icons, because they can easily go over the binder size limit. - *
- * - * @see {@link ShortcutManager}. - * - * @hide + * @see ShortcutManager */ public final class ShortcutInfo implements Parcelable { - /* @hide */ + static final String TAG = "Shortcut"; + + private static final String RES_TYPE_STRING = "string"; + + private static final String ANDROID_PACKAGE_NAME = "android"; + + private static final int IMPLICIT_RANK_MASK = 0x7fffffff; + + private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK; + + /** @hide */ + public static final int RANK_NOT_SET = Integer.MAX_VALUE; + + /** @hide */ public static final int FLAG_DYNAMIC = 1 << 0; - /* @hide */ + /** @hide */ public static final int FLAG_PINNED = 1 << 1; - /* @hide */ + /** @hide */ public static final int FLAG_HAS_ICON_RES = 1 << 2; - /* @hide */ + /** @hide */ public static final int FLAG_HAS_ICON_FILE = 1 << 3; - /* @hide */ + /** @hide */ public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4; + /** @hide */ + public static final int FLAG_MANIFEST = 1 << 5; + + /** @hide */ + public static final int FLAG_DISABLED = 1 << 6; + + /** @hide */ + public static final int FLAG_STRINGS_RESOLVED = 1 << 7; + + /** @hide */ + public static final int FLAG_IMMUTABLE = 1 << 8; + /** @hide */ @IntDef(flag = true, value = { @@ -77,26 +99,34 @@ public final class ShortcutInfo implements Parcelable { FLAG_HAS_ICON_RES, FLAG_HAS_ICON_FILE, FLAG_KEY_FIELDS_ONLY, + FLAG_MANIFEST, + FLAG_DISABLED, + FLAG_STRINGS_RESOLVED, + FLAG_IMMUTABLE, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} // Cloning options. - /* @hide */ + /** @hide */ private static final int CLONE_REMOVE_ICON = 1 << 0; - /* @hide */ + /** @hide */ private static final int CLONE_REMOVE_INTENT = 1 << 1; - /* @hide */ + /** @hide */ public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2; - /* @hide */ - public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON; + /** @hide */ + public static final int CLONE_REMOVE_RES_NAMES = 1 << 3; + + /** @hide */ + public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES; - /* @hide */ - public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT; + /** @hide */ + public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT + | CLONE_REMOVE_RES_NAMES; /** @hide */ @IntDef(flag = true, @@ -104,6 +134,7 @@ public final class ShortcutInfo implements Parcelable { CLONE_REMOVE_ICON, CLONE_REMOVE_INTENT, CLONE_REMOVE_NON_KEY_INFO, + CLONE_REMOVE_RES_NAMES, CLONE_REMOVE_FOR_CREATOR, CLONE_REMOVE_FOR_LAUNCHER }) @@ -111,7 +142,7 @@ public final class ShortcutInfo implements Parcelable { public @interface CloneFlags {} /** - * Shortcut category for + * Shortcut category for messaging related actions, such as chat. */ public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation"; @@ -121,33 +152,57 @@ public final class ShortcutInfo implements Parcelable { private final String mPackageName; @Nullable - private ComponentName mActivityComponent; + private ComponentName mActivity; @Nullable private Icon mIcon; - @NonNull - private String mTitle; + private int mTitleResId; + + private String mTitleResName; @Nullable - private String mText; + private CharSequence mTitle; - @NonNull + private int mTextResId; + + private String mTextResName; + + @Nullable + private CharSequence mText; + + private int mDisabledMessageResId; + + private String mDisabledMessageResName; + + @Nullable + private CharSequence mDisabledMessage; + + @Nullable private ArraySet mCategories; /** - * Intent *with extras removed*. + * Intents *with extras removed*. */ - @NonNull - private Intent mIntent; + @Nullable + private Intent[] mIntents; /** - * Extras for the intent. + * Extras for the intents. */ - @NonNull - private PersistableBundle mIntentPersistableExtras; + @Nullable + private PersistableBundle[] mIntentPersistableExtrases; - private int mWeight; + private int mRank; + + /** + * Internally used for auto-rank-adjustment. + * + * RANK_CHANGED_BIT is used to denote that the rank of a shortcut is changing. + * The rest of the bits are used to denote the order in which shortcuts are passed to + * APIs, which is used to preserve the argument order when ranks are tie. + */ + private int mImplicitRank; @Nullable private PersistableBundle mExtras; @@ -159,7 +214,9 @@ public final class ShortcutInfo implements Parcelable { private int mFlags; // Internal use only. - private int mIconResourceId; + private int mIconResId; + + private String mIconResName; // Internal use only. @Nullable @@ -175,26 +232,81 @@ public final class ShortcutInfo implements Parcelable { // Note we can't do other null checks here because SM.updateShortcuts() takes partial // information. mPackageName = b.mContext.getPackageName(); - mActivityComponent = b.mActivityComponent; + mActivity = b.mActivity; mIcon = b.mIcon; mTitle = b.mTitle; + mTitleResId = b.mTitleResId; mText = b.mText; - mCategories = clone(b.mCategories); - mIntent = b.mIntent; - if (mIntent != null) { - final Bundle intentExtras = mIntent.getExtras(); - if (intentExtras != null) { - mIntent.replaceExtras((Bundle) null); - mIntentPersistableExtras = new PersistableBundle(intentExtras); - } - } - mWeight = b.mWeight; + mTextResId = b.mTextResId; + mDisabledMessage = b.mDisabledMessage; + mDisabledMessageResId = b.mDisabledMessageResId; + mCategories = cloneCategories(b.mCategories); + mIntents = cloneIntents(b.mIntents); + fixUpIntentExtras(); + mRank = b.mRank; mExtras = b.mExtras; updateTimestamp(); } - private ArraySet clone(Set source) { - return (source == null) ? null : new ArraySet<>(source); + /** + * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases} + * as {@link PersistableBundle}, and remove extras from the original intents. + */ + private void fixUpIntentExtras() { + if (mIntents == null) { + mIntentPersistableExtrases = null; + return; + } + mIntentPersistableExtrases = new PersistableBundle[mIntents.length]; + for (int i = 0; i < mIntents.length; i++) { + final Intent intent = mIntents[i]; + final Bundle extras = intent.getExtras(); + if (extras == null) { + mIntentPersistableExtrases[i] = null; + } else { + mIntentPersistableExtrases[i] = new PersistableBundle(extras); + intent.replaceExtras((Bundle) null); + } + } + } + + private static ArraySet cloneCategories(Set source) { + if (source == null) { + return null; + } + final ArraySet ret = new ArraySet<>(source.size()); + for (CharSequence s : source) { + if (!TextUtils.isEmpty(s)) { + ret.add(s.toString().intern()); + } + } + return ret; + } + + private static Intent[] cloneIntents(Intent[] intents) { + if (intents == null) { + return null; + } + final Intent[] ret = new Intent[intents.length]; + for (int i = 0; i < ret.length; i++) { + if (intents[i] != null) { + ret[i] = new Intent(intents[i]); + } + } + return ret; + } + + private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) { + if (bundle == null) { + return null; + } + final PersistableBundle[] ret = new PersistableBundle[bundle.length]; + for (int i = 0; i < ret.length; i++) { + if (bundle[i] != null) { + ret[i] = new PersistableBundle(bundle[i]); + } + } + return ret; } /** @@ -204,8 +316,12 @@ public final class ShortcutInfo implements Parcelable { */ public void enforceMandatoryFields() { Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided"); - Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided"); - Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided"); + Preconditions.checkNotNull(mActivity, "Activity must be provided"); + if (mTitle == null && mTitleResId == 0) { + throw new IllegalArgumentException("Short label must be provided"); + } + Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided"); + Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided"); } /** @@ -215,14 +331,14 @@ public final class ShortcutInfo implements Parcelable { mUserId = source.mUserId; mId = source.mId; mPackageName = source.mPackageName; + mActivity = source.mActivity; mFlags = source.mFlags; mLastChangedTimestamp = source.mLastChangedTimestamp; // Just always keep it since it's cheep. - mIconResourceId = source.mIconResourceId; + mIconResId = source.mIconResId; if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) { - mActivityComponent = source.mActivityComponent; if ((cloneFlags & CLONE_REMOVE_ICON) == 0) { mIcon = source.mIcon; @@ -230,20 +346,247 @@ public final class ShortcutInfo implements Parcelable { } mTitle = source.mTitle; + mTitleResId = source.mTitleResId; mText = source.mText; - mCategories = clone(source.mCategories); + mTextResId = source.mTextResId; + mDisabledMessage = source.mDisabledMessage; + mDisabledMessageResId = source.mDisabledMessageResId; + mCategories = cloneCategories(source.mCategories); if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) { - mIntent = source.mIntent; - mIntentPersistableExtras = source.mIntentPersistableExtras; + mIntents = cloneIntents(source.mIntents); + mIntentPersistableExtrases = + clonePersistableBundle(source.mIntentPersistableExtrases); } - mWeight = source.mWeight; + mRank = source.mRank; mExtras = source.mExtras; + + if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) { + mTitleResName = source.mTitleResName; + mTextResName = source.mTextResName; + mDisabledMessageResName = source.mDisabledMessageResName; + mIconResName = source.mIconResName; + } } else { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; } } + /** + * Load a string resource from the publisher app. + * + * @param resId resource ID + * @param defValue default value to be returned when the specified resource isn't found. + */ + private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) { + try { + return res.getString(resId); + } catch (NotFoundException e) { + Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName); + return defValue; + } + } + + /** + * Load the string resources for the text fields and set them to the actual value fields. + * This will set {@link #FLAG_STRINGS_RESOLVED}. + * + * @param res {@link Resources} for the publisher. Must have been loaded with + * {@link PackageManager#getResourcesForApplicationAsUser}. + * + * @hide + */ + public void resolveResourceStrings(@NonNull Resources res) { + mFlags |= FLAG_STRINGS_RESOLVED; + + if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) { + return; // Bail early. + } + + if (mTitleResId != 0) { + mTitle = getResourceString(res, mTitleResId, mTitle); + } + if (mTextResId != 0) { + mText = getResourceString(res, mTextResId, mText); + } + if (mDisabledMessageResId != 0) { + mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage); + } + } + + /** + * Look up resource name for a given resource ID. + * + * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the + * type (e.g. "string/text_1"). + * + * @hide + */ + @VisibleForTesting + public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType, + @NonNull String packageName) { + if (resId == 0) { + return null; + } + try { + final String fullName = res.getResourceName(resId); + + if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) { + // If it's a framework resource, the value won't change, so just return the ID + // value as a string. + return String.valueOf(resId); + } + return withType ? getResourceTypeAndEntryName(fullName) + : getResourceEntryName(fullName); + } catch (NotFoundException e) { + Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName + + ". Resource IDs may change when the application is upgraded, and the system" + + " may not be able to find the correct resource."); + return null; + } + } + + /** + * Extract the package name from a fully-donated resource name. + * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1" + * @hide + */ + @VisibleForTesting + public static String getResourcePackageName(@NonNull String fullResourceName) { + final int p1 = fullResourceName.indexOf(':'); + if (p1 < 0) { + return null; + } + return fullResourceName.substring(0, p1); + } + + /** + * Extract the type name from a fully-donated resource name. + * e.g. "com.android.app1:drawable/icon1" -> "drawable" + * @hide + */ + @VisibleForTesting + public static String getResourceTypeName(@NonNull String fullResourceName) { + final int p1 = fullResourceName.indexOf(':'); + if (p1 < 0) { + return null; + } + final int p2 = fullResourceName.indexOf('/', p1 + 1); + if (p2 < 0) { + return null; + } + return fullResourceName.substring(p1 + 1, p2); + } + + /** + * Extract the type name + the entry name from a fully-donated resource name. + * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1" + * @hide + */ + @VisibleForTesting + public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) { + final int p1 = fullResourceName.indexOf(':'); + if (p1 < 0) { + return null; + } + return fullResourceName.substring(p1 + 1); + } + + /** + * Extract the entry name from a fully-donated resource name. + * e.g. "com.android.app1:drawable/icon1" -> "icon1" + * @hide + */ + @VisibleForTesting + public static String getResourceEntryName(@NonNull String fullResourceName) { + final int p1 = fullResourceName.indexOf('/'); + if (p1 < 0) { + return null; + } + return fullResourceName.substring(p1 + 1); + } + + /** + * Return the resource ID for a given resource ID. + * + * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except + * if {@code resourceName} is an integer then it'll just return its value. (Which also the + * aforementioned method would do internally, but not documented, so doing here explicitly.) + * + * @param res {@link Resources} for the publisher. Must have been loaded with + * {@link PackageManager#getResourcesForApplicationAsUser}. + * + * @hide + */ + @VisibleForTesting + public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName, + @Nullable String resourceType, String packageName) { + if (resourceName == null) { + return 0; + } + try { + try { + // It the name can be parsed as an integer, just use it. + return Integer.parseInt(resourceName); + } catch (NumberFormatException ignore) { + } + + return res.getIdentifier(resourceName, resourceType, packageName); + } catch (NotFoundException e) { + Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package " + + packageName); + return 0; + } + } + + /** + * Look up resource names from the resource IDs for the icon res and the text fields, and fill + * in the resource name fields. + * + * @param res {@link Resources} for the publisher. Must have been loaded with + * {@link PackageManager#getResourcesForApplicationAsUser}. + * + * @hide + */ + public void lookupAndFillInResourceNames(@NonNull Resources res) { + if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0) + && (mIconResId == 0)) { + return; // Bail early. + } + + // We don't need types for strings because their types are always "string". + mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName); + mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName); + mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId, + /*withType=*/ false, mPackageName); + + // But icons have multiple possible types, so include the type. + mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName); + } + + /** + * Look up resource IDs from the resource names for the icon res and the text fields, and fill + * in the resource ID fields. + * + * This is called when an app is updated. + * + * @hide + */ + public void lookupAndFillInResourceIds(@NonNull Resources res) { + if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null) + && (mIconResName == null)) { + return; // Bail early. + } + + mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName); + mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName); + mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING, + mPackageName); + + // mIconResName already contains the type, so the third argument is not needed. + mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName); + } + /** * Copy a {@link ShortcutInfo}, optionally removing fields. * @hide @@ -252,50 +595,86 @@ public final class ShortcutInfo implements Parcelable { return new ShortcutInfo(this, cloneFlags); } + /** + * @hide + */ + public void ensureUpdatableWith(ShortcutInfo source) { + Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match"); + Preconditions.checkState(mId.equals(source.mId), "ID must match"); + Preconditions.checkState(mPackageName.equals(source.mPackageName), + "Package name must match"); + Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable"); + } + /** * Copy non-null/zero fields from another {@link ShortcutInfo}. Only "public" information - * will be overwritten. The timestamp will be updated. + * will be overwritten. The timestamp will *not* be updated to be consistent with other + * setters (and also the clock is not injectable in this file). * * - Flags will not change * - mBitmapPath will not change * - Current time will be set to timestamp * + * @throws IllegalStateException if source is not compatible. + * * @hide */ public void copyNonNullFieldsFrom(ShortcutInfo source) { - Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match"); - Preconditions.checkState(mId.equals(source.mId), "ID must match"); - Preconditions.checkState(mPackageName.equals(source.mPackageName), - "Package name must match"); + ensureUpdatableWith(source); - if (source.mActivityComponent != null) { - mActivityComponent = source.mActivityComponent; + if (source.mActivity != null) { + mActivity = source.mActivity; } if (source.mIcon != null) { mIcon = source.mIcon; + + mIconResId = 0; + mIconResName = null; + mBitmapPath = null; } if (source.mTitle != null) { mTitle = source.mTitle; + mTitleResId = 0; + mTitleResName = null; + } else if (source.mTitleResId != 0) { + mTitle = null; + mTitleResId = source.mTitleResId; + mTitleResName = null; } + if (source.mText != null) { mText = source.mText; + mTextResId = 0; + mTextResName = null; + } else if (source.mTextResId != 0) { + mText = null; + mTextResId = source.mTextResId; + mTextResName = null; + } + if (source.mDisabledMessage != null) { + mDisabledMessage = source.mDisabledMessage; + mDisabledMessageResId = 0; + mDisabledMessageResName = null; + } else if (source.mDisabledMessageResId != 0) { + mDisabledMessage = null; + mDisabledMessageResId = source.mDisabledMessageResId; + mDisabledMessageResName = null; } if (source.mCategories != null) { - mCategories = clone(source.mCategories); + mCategories = cloneCategories(source.mCategories); } - if (source.mIntent != null) { - mIntent = source.mIntent; - mIntentPersistableExtras = source.mIntentPersistableExtras; + if (source.mIntents != null) { + mIntents = cloneIntents(source.mIntents); + mIntentPersistableExtrases = + clonePersistableBundle(source.mIntentPersistableExtrases); } - if (source.mWeight != 0) { - mWeight = source.mWeight; + if (source.mRank != RANK_NOT_SET) { + mRank = source.mRank; } if (source.mExtras != null) { mExtras = source.mExtras; } - - updateTimestamp(); } /** @@ -310,7 +689,6 @@ public final class ShortcutInfo implements Parcelable { throw getInvalidIconException(); } if (icon.hasTint()) { - // TODO support it throw new IllegalArgumentException("Icons with tints are not supported"); } @@ -320,77 +698,123 @@ public final class ShortcutInfo implements Parcelable { /** @hide */ public static IllegalArgumentException getInvalidIconException() { return new IllegalArgumentException("Unsupported icon type:" - +" only bitmap, resource and content URI are supported"); + +" only the bitmap and resource types are supported"); } /** * Builder class for {@link ShortcutInfo} objects. + * + * @see ShortcutManager */ public static class Builder { private final Context mContext; private String mId; - private ComponentName mActivityComponent; + private ComponentName mActivity; private Icon mIcon; - private String mTitle; + private int mTitleResId; + + private CharSequence mTitle; + + private int mTextResId; + + private CharSequence mText; - private String mText; + private int mDisabledMessageResId; + + private CharSequence mDisabledMessage; private Set mCategories; - private Intent mIntent; + private Intent[] mIntents; - private int mWeight; + private int mRank = RANK_NOT_SET; private PersistableBundle mExtras; - /** Constructor. */ + /** + * Old style constructor. + * @hide + */ + @Deprecated public Builder(Context context) { mContext = context; } /** - * Sets the ID of the shortcut. This is a mandatory field. + * Used with the old style constructor, kept for unit tests. + * @hide */ @NonNull + @Deprecated public Builder setId(@NonNull String id) { - mId = Preconditions.checkStringNotEmpty(id, "id"); + mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty"); return this; } /** - * Optionally sets the target activity. If it's not set, and if the caller application - * has multiple launcher icons, this shortcut will be shown on all those icons. - * If it's set, this shortcut will be only shown on this activity. + * Constructor. + * + * @param context Client context. + * @param id ID of the shortcut. + */ + public Builder(Context context, String id) { + mContext = context; + mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty"); + } + + /** + * Sets the target activity. A shortcut will be shown along with this activity's icon + * on the launcher. + * + * When selecting a target activity, keep the following in mind: + *
    + *
  • All dynamic shortcuts must have a target activity. When a shortcut with no target + * activity is published using + * {@link ShortcutManager#addDynamicShortcuts(List)} or + * {@link ShortcutManager#setDynamicShortcuts(List)}, + * the first main activity defined in the application's AndroidManifest.xml + * file is used. * - *

    The package name of the target activity must match the package name of the shortcut - * publisher. + *

  • Only "main" activities—ones that define the {@link Intent#ACTION_MAIN} + * and {@link Intent#CATEGORY_LAUNCHER} intent filters—can be target + * activities. * - *

    This has nothing to do with the activity that this shortcut will launch. This is - * a hint to the launcher app about which launcher icon to associate this shortcut with. + *

  • By default, the first main activity defined in the application manifest is + * the target activity. + * + *
  • A target activity must belong to the publisher application. + *
+ * + * @see ShortcutInfo#getActivity() */ @NonNull - public Builder setActivityComponent(@NonNull ComponentName activityComponent) { - mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent"); + public Builder setActivity(@NonNull ComponentName activity) { + mActivity = Preconditions.checkNotNull(activity, "activity cannot be null"); return this; } /** - * Optionally sets an icon. + * Sets an icon of a shortcut. * - *
    - *
  • Tints set by {@link Icon#setTint} or {@link Icon#setTintList} are not supported. - *
  • Bitmaps and resources are supported, but "content:" URIs are not supported. - *
+ *

Icons are not available on {@link ShortcutInfo} instances + * returned by {@link ShortcutManager} or {@link LauncherApps}. The default launcher + * application can use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)} + * or {@link LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int)} to fetch + * shortcut icons. + * + *

Tints set with {@link Icon#setTint} or {@link Icon#setTintList} are not supported + * and will be ignored. * - *

For performance reasons, icons will NOT be available on instances - * returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications - * can use {@link ShortcutInfo#getIconResourceId()} if {@link #hasIconResource()} is true. - * Otherwise, if {@link #hasIconFile()} is true, use - * {@link LauncherApps#getShortcutIconFd} to load the image. + *

Only icons created with {@link Icon#createWithBitmap(Bitmap)} and + * {@link Icon#createWithResource} are supported. + * Other types, such as URI-based icons, are not supported. + * + * @see LauncherApps#getShortcutIconDrawable(ShortcutInfo, int) + * @see LauncherApps#getShortcutBadgedIconDrawable(ShortcutInfo, int) */ @NonNull public Builder setIcon(Icon icon) { @@ -399,26 +823,112 @@ public final class ShortcutInfo implements Parcelable { } /** - * Sets the title of a shortcut. This is a mandatory field. + * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests + * use it.) + */ + @Deprecated + public Builder setShortLabelResId(int shortLabelResId) { + Preconditions.checkState(mTitle == null, "shortLabel already set"); + mTitleResId = shortLabelResId; + return this; + } + + /** + * Sets the short title of a shortcut. + * + *

This is a mandatory field when publishing a new shortcut with + * {@link ShortcutManager#addDynamicShortcuts(List)} or + * {@link ShortcutManager#setDynamicShortcuts(List)}. + * + *

This field is intended to be a concise description of a shortcut. + * + *

The recommended maximum length is 10 characters. * - *

This field is intended for a concise description of a shortcut displayed under - * an icon. The recommend max length is 10 characters. + * @see ShortcutInfo#getShortLabel() */ @NonNull - public Builder setTitle(@NonNull String title) { - mTitle = Preconditions.checkStringNotEmpty(title, "title"); + public Builder setShortLabel(@NonNull CharSequence shortLabel) { + Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set"); + mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty"); + return this; + } + + /** + * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests + * use it.) + */ + @Deprecated + public Builder setLongLabelResId(int longLabelResId) { + Preconditions.checkState(mText == null, "longLabel already set"); + mTextResId = longLabelResId; return this; } /** - * Sets the text of a shortcut. This is an optional field. + * Sets the text of a shortcut. + * + *

This field is intended to be more descriptive than the shortcut title. The launcher + * shows this instead of the short title when it has enough space. + * + *

The recommend maximum length is 25 characters. * - *

This field is intended to be more descriptive than the shortcut title. - * The recommend max length is 25 characters. + * @see ShortcutInfo#getLongLabel() */ @NonNull - public Builder setText(@NonNull String text) { - mText = Preconditions.checkStringNotEmpty(text, "text"); + public Builder setLongLabel(@NonNull CharSequence longLabel) { + Preconditions.checkState(mTextResId == 0, "longLabelResId already set"); + mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty"); + return this; + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public Builder setTitle(@NonNull CharSequence value) { + return setShortLabel(value); + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public Builder setTitleResId(int value) { + return setShortLabelResId(value); + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public Builder setText(@NonNull CharSequence value) { + return setLongLabel(value); + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public Builder setTextResId(int value) { + return setLongLabelResId(value); + } + + /** + * @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests + * use it.) + */ + @Deprecated + public Builder setDisabledMessageResId(int disabledMessageResId) { + Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set"); + mDisabledMessageResId = disabledMessageResId; + return this; + } + + /** + * Sets the message that should be shown when the user attempts to start a shortcut that + * is disabled. + * + * @see ShortcutInfo#getDisabledMessage() + */ + @NonNull + public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) { + Preconditions.checkState( + mDisabledMessageResId == 0, "disabledMessageResId already set"); + mDisabledMessage = + Preconditions.checkStringNotEmpty(disabledMessage, + "disabledMessage cannot be empty"); return this; } @@ -427,6 +937,7 @@ public final class ShortcutInfo implements Parcelable { * categorise shortcuts. * * @see #SHORTCUT_CATEGORY_CONVERSATION + * @see ShortcutInfo#getCategories() */ @NonNull public Builder setCategories(Set categories) { @@ -435,28 +946,70 @@ public final class ShortcutInfo implements Parcelable { } /** - * Sets the intent of a shortcut. This is a mandatory field. The extras must only contain - * persistable information. (See {@link PersistableBundle}). + * Sets the intent of a shortcut. Alternatively, {@link #setIntents(Intent[])} can be used + * to launch an activity with other activities in the back stack. + * + *

This is a mandatory field when publishing a new shortcut with + * {@link ShortcutManager#addDynamicShortcuts(List)} or + * {@link ShortcutManager#setDynamicShortcuts(List)}. + * + *

A shortcut can launch any intent that the publisher application has permission to + * launch. For example, a shortcut can launch an unexported activity within the publisher + * application. A shortcut intent doesn't have to point at the target activity. + * + *

The given {@code intent} can contain extras, but these extras must contain values + * of primitive types in order for the system to persist these values. + * + * @see ShortcutInfo#getIntent() + * @see #setIntents(Intent[]) */ @NonNull public Builder setIntent(@NonNull Intent intent) { - mIntent = Preconditions.checkNotNull(intent, "intent"); + return setIntents(new Intent[]{intent}); + } + + /** + * Sets multiple intents instead of a single intent, in order to launch an activity with + * other activities in back stack. Use {@link TaskStackBuilder} to build intents. + * See the {@link ShortcutManager} javadoc for details. + * + * @see Builder#setIntent(Intent) + * @see ShortcutInfo#getIntents() + * @see Context#startActivities(Intent[]) + * @see TaskStackBuilder + */ + @NonNull + public Builder setIntents(@NonNull Intent[] intents) { + Preconditions.checkNotNull(intents, "intents cannot be null"); + Preconditions.checkNotNull(intents.length, "intents cannot be empty"); + for (Intent intent : intents) { + Preconditions.checkNotNull(intent, "intents cannot contain null"); + Preconditions.checkNotNull(intent.getAction(), "intent's action must be set"); + } + // Make sure always clone incoming intents. + mIntents = cloneIntents(intents); return this; } /** - * Optionally sets the weight of a shortcut, which will be used by the launcher for sorting. - * The larger the weight, the more "important" a shortcut is. + * "Rank" of a shortcut, which is a non-negative value that's used by the launcher app + * to sort shortcuts. + * + * See {@link ShortcutInfo#getRank()} for details. */ @NonNull - public Builder setWeight(int weight) { - mWeight = weight; + public Builder setRank(int rank) { + Preconditions.checkArgument((0 <= rank), + "Rank cannot be negative or bigger than MAX_RANK"); + mRank = rank; return this; } /** - * Optional values that applications can set. Applications can store any meta-data of - * shortcuts in this, and retrieve later from {@link ShortcutInfo#getExtras()}. + * Extras that application can set for any purpose. + * + *

Applications can store arbitrary shortcut metadata in extras and retrieve the + * metadata later using {@link ShortcutInfo#getExtras()}. */ @NonNull public Builder setExtras(@NonNull PersistableBundle extras) { @@ -474,7 +1027,11 @@ public final class ShortcutInfo implements Parcelable { } /** - * Return the ID of the shortcut. + * Returns the ID of a shortcut. + * + *

Shortcut IDs are unique within each publisher application and must be stable across + * devices so that shortcuts will still be valid when restored on a different device. + * See {@link ShortcutManager} for details. */ @NonNull public String getId() { @@ -482,33 +1039,34 @@ public final class ShortcutInfo implements Parcelable { } /** - * Return the package name of the creator application. + * Return the package name of the publisher application. */ @NonNull - public String getPackageName() { + public String getPackage() { return mPackageName; } /** - * Return the target activity, which may be null, in which case the shortcut is not associated - * with a specific activity. + * Return the target activity. * - *

This has nothing to do with the activity that this shortcut will launch. This is - * a hint to the launcher app that on which launcher icon this shortcut should be shown. + *

This has nothing to do with the activity that this shortcut will launch. + * Launcher applications should show the launcher icon for the returned activity alongside + * this shortcut. * - * @see Builder#setActivityComponent + * @see Builder#setActivity */ @Nullable - public ComponentName getActivityComponent() { - return mActivityComponent; + public ComponentName getActivity() { + return mActivity; + } + + /** @hide */ + public void setActivity(ComponentName activity) { + mActivity = activity; } /** - * Icon. - * - * For performance reasons, this will NOT be available when an instance is returned - * by {@link ShortcutManager} or {@link LauncherApps}. A launcher application needs to use - * other APIs in LauncherApps to fetch the bitmap. + * Returns the shortcut icon. * * @hide */ @@ -517,27 +1075,82 @@ public final class ShortcutInfo implements Parcelable { return mIcon; } + /** @hide -- old signature, the internal code still uses it. */ + @Nullable + @Deprecated + public CharSequence getTitle() { + return mTitle; + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public int getTitleResId() { + return mTitleResId; + } + + /** @hide -- old signature, the internal code still uses it. */ + @Nullable + @Deprecated + public CharSequence getText() { + return mText; + } + + /** @hide -- old signature, the internal code still uses it. */ + @Deprecated + public int getTextResId() { + return mTextResId; + } + /** - * Return the shortcut title. + * Return the shorter description of a shortcut. * - *

All shortcuts must have a non-empty title, but this method will return null when - * {@link #hasKeyFieldsOnly()} is true. + * @see Builder#setShortLabel(CharSequence) */ @Nullable - public String getTitle() { + public CharSequence getShortLabel() { return mTitle; } + /** @hide */ + public int getShortLabelResourceId() { + return mTitleResId; + } + /** - * Return the shortcut text. + * Return the longer description of a shortcut. + * + * @see Builder#setLongLabel(CharSequence) */ @Nullable - public String getText() { + public CharSequence getLongLabel() { return mText; } + /** @hide */ + public int getLongLabelResourceId() { + return mTextResId; + } + + /** + * Return the message that should be shown when the user attempts to start a shortcut + * that is disabled. + * + * @see Builder#setDisabledMessage(CharSequence) + */ + @Nullable + public CharSequence getDisabledMessage() { + return mDisabledMessage; + } + + /** @hide */ + public int getDisabledMessageResourceId() { + return mDisabledMessageResId; + } + /** - * Return the categories. + * Return the shortcut's categories. + * + * @see Builder#setCategories(Set) */ @Nullable public Set getCategories() { @@ -545,55 +1158,125 @@ public final class ShortcutInfo implements Parcelable { } /** - * Return the intent. + * Returns the intent that is executed when the user selects this shortcut. + * If setIntents() was used, then return the last intent in the array. * - *

All shortcuts must have an intent, but this method will return null when - * {@link #hasKeyFieldsOnly()} is true. + *

Launcher applications cannot see the intent. If a {@link ShortcutInfo} is + * obtained via {@link LauncherApps}, then this method will always return null. + * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}. * - *

Launcher apps cannot see the intent. If a {@link ShortcutInfo} is obtained via - * {@link LauncherApps}, then this method will always return null. Launcher apps can only - * start a shortcut intent with {@link LauncherApps#startShortcut}. + * @see Builder#setIntent(Intent) */ @Nullable public Intent getIntent() { - if (mIntent == null) { + if (mIntents == null || mIntents.length == 0) { return null; } - final Intent intent = new Intent(mIntent); - intent.replaceExtras( - mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null); - return intent; + final int last = mIntents.length - 1; + final Intent intent = new Intent(mIntents[last]); + return setIntentExtras(intent, mIntentPersistableExtrases[last]); } /** - * Return "raw" intent, which is the original intent without the extras. + * Return the intent set with {@link Builder#setIntents(Intent[])}. + * + *

Launcher applications cannot see the intents. If a {@link ShortcutInfo} is + * obtained via {@link LauncherApps}, then this method will always return null. + * Launchers can only start a shortcut intent with {@link LauncherApps#startShortcut}. + * + * @see Builder#setIntents(Intent[]) + */ + @Nullable + public Intent[] getIntents() { + final Intent[] ret = new Intent[mIntents.length]; + + for (int i = 0; i < ret.length; i++) { + ret[i] = new Intent(mIntents[i]); + setIntentExtras(ret[i], mIntentPersistableExtrases[i]); + } + + return ret; + } + + /** + * Return "raw" intents, which is the original intents without the extras. * @hide */ @Nullable - public Intent getIntentNoExtras() { - return mIntent; + public Intent[] getIntentsNoExtras() { + return mIntents; } /** - * The extras in the intent. We convert extras into {@link PersistableBundle} so we can + * The extras in the intents. We convert extras into {@link PersistableBundle} so we can * persist them. * @hide */ @Nullable - public PersistableBundle getIntentPersistableExtras() { - return mIntentPersistableExtras; + public PersistableBundle[] getIntentPersistableExtrases() { + return mIntentPersistableExtrases; } /** - * Return the weight of a shortcut, which will be used by Launcher for sorting. - * The larger the weight, the more "important" a shortcut is. + * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each + * {@link #getActivity} for each of the two kinds, dynamic shortcuts and manifest shortcuts. + * + *

Because manifest shortcuts and dynamic shortcuts have overlapping ranks, + * when a launcher application shows shortcuts for an activity, it should first show + * the manifest shortcuts followed by the dynamic shortcuts. Within each of those categories, + * shortcuts should be sorted by rank in ascending order. + * + *

"Floating" shortcuts (i.e. shortcuts that are neither dynamic nor manifest) will all + * have rank 0, because there's no sorting for them. + * + * See the {@link ShortcutManager}'s class javadoc for details. + * + * @see Builder#setRank(int) */ - public int getWeight() { - return mWeight; + public int getRank() { + return mRank; + } + + /** @hide */ + public boolean hasRank() { + return mRank != RANK_NOT_SET; + } + + /** @hide */ + public void setRank(int rank) { + mRank = rank; + } + + /** @hide */ + public void clearImplicitRankAndRankChangedFlag() { + mImplicitRank = 0; + } + + /** @hide */ + public void setImplicitRank(int rank) { + // Make sure to keep RANK_CHANGED_BIT. + mImplicitRank = (mImplicitRank & RANK_CHANGED_BIT) | (rank & IMPLICIT_RANK_MASK); + } + + /** @hide */ + public int getImplicitRank() { + return mImplicitRank & IMPLICIT_RANK_MASK; + } + + /** @hide */ + public void setRankChanged() { + mImplicitRank |= RANK_CHANGED_BIT; + } + + /** @hide */ + public boolean isRankChanged() { + return (mImplicitRank & RANK_CHANGED_BIT) != 0; } /** - * Optional values that application can set. + * Extras that application can set to any purposes. + * + * @see Builder#setExtras(PersistableBundle) */ @Nullable public PersistableBundle getExtras() { @@ -606,7 +1289,7 @@ public final class ShortcutInfo implements Parcelable { } /** - * {@link UserHandle} on which the publisher created shortcuts. + * {@link UserHandle} on which the publisher created this shortcut. */ public UserHandle getUserHandle() { return UserHandle.of(mUserId); @@ -655,19 +1338,94 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_PINNED); } + /** + * Return whether a shortcut is published from AndroidManifest.xml or not. If {@code true}, + * it's also {@link #isImmutable()}. + * + *

When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml, + * this will be set to {@code false}. If the shortcut is not pinned, then it'll just disappear. + * However, if it's pinned, it will still be alive, and {@link #isEnabled()} will be + * {@code false} and {@link #isImmutable()} will be {@code true}. + */ + public boolean isDeclaredInManifest() { + return hasFlags(FLAG_MANIFEST); + } + + /** @hide kept for unit tests */ + @Deprecated + public boolean isManifestShortcut() { + return isDeclaredInManifest(); + } + + /** + * @return true if pinned but neither dynamic nor manifest. + * @hide + */ + public boolean isFloating() { + return isPinned() && !(isDynamic() || isManifestShortcut()); + } + + /** @hide */ + public boolean isOriginallyFromManifest() { + return hasFlags(FLAG_IMMUTABLE); + } + + /** + * Return if a shortcut is immutable, in which case it cannot be modified with any of + * {@link ShortcutManager} APIs. + * + *

All manifest shortcuts are immutable. When a manifest shortcut is pinned and then + * disabled because the app is upgraded and its AndroidManifest.xml no longer publishes it, + * {@link #isDeclaredInManifest()} returns {@code false}, but it is still immutable. + * + *

All shortcuts originally published via the {@link ShortcutManager} APIs + * are all mutable. + */ + public boolean isImmutable() { + return hasFlags(FLAG_IMMUTABLE); + } + + /** + * Returns {@code false} if a shortcut is disabled with + * {@link ShortcutManager#disableShortcuts}. + */ + public boolean isEnabled() { + return !hasFlags(FLAG_DISABLED); + } + + /** @hide */ + public boolean isAlive() { + return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST); + } + + /** @hide */ + public boolean usesQuota() { + return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST); + } + /** * Return whether a shortcut's icon is a resource in the owning package. * - * @see LauncherApps#getShortcutIconResId(ShortcutInfo) + * @hide internal/unit tests only */ public boolean hasIconResource() { return hasFlags(FLAG_HAS_ICON_RES); } + /** @hide */ + public boolean hasStringResources() { + return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0); + } + + /** @hide */ + public boolean hasAnyResources() { + return hasIconResource() || hasStringResources(); + } + /** * Return whether a shortcut's icon is stored as a file. * - * @see LauncherApps#getShortcutIconFd(ShortcutInfo) + * @hide internal/unit tests only */ public boolean hasIconFile() { return hasFlags(FLAG_HAS_ICON_FILE); @@ -678,18 +1436,32 @@ public final class ShortcutInfo implements Parcelable { * following fields are available. *

    *
  • {@link #getId()} - *
  • {@link #getPackageName()} + *
  • {@link #getPackage()} + *
  • {@link #getActivity()} *
  • {@link #getLastChangedTimestamp()} *
  • {@link #isDynamic()} *
  • {@link #isPinned()} - *
  • {@link #hasIconResource()} - *
  • {@link #hasIconFile()} + *
  • {@link #isDeclaredInManifest()} + *
  • {@link #isImmutable()} + *
  • {@link #isEnabled()} + *
  • {@link #getUserHandle()} *
+ * + *

For performance reasons, shortcuts passed to + * {@link LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle)} as well as those + * returned from {@link LauncherApps#getShortcuts(ShortcutQuery, UserHandle)} + * while using the {@link ShortcutQuery#FLAG_GET_KEY_FIELDS_ONLY} option contain only key + * information. */ public boolean hasKeyFieldsOnly() { return hasFlags(FLAG_KEY_FIELDS_ONLY); } + /** @hide */ + public boolean hasStringResourcesResolved() { + return hasFlags(FLAG_STRINGS_RESOLVED); + } + /** @hide */ public void updateTimestamp() { mLastChangedTimestamp = System.currentTimeMillis(); @@ -708,14 +1480,18 @@ public final class ShortcutInfo implements Parcelable { /** @hide */ public void setIconResourceId(int iconResourceId) { - mIconResourceId = iconResourceId; + if (mIconResId != iconResourceId) { + mIconResName = null; + } + mIconResId = iconResourceId; } /** * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true. + * @hide internal / tests only. */ public int getIconResourceId() { - return mIconResourceId; + return mIconResId; } /** @hide */ @@ -728,25 +1504,129 @@ public final class ShortcutInfo implements Parcelable { mBitmapPath = bitmapPath; } + /** @hide */ + public void setDisabledMessageResId(int disabledMessageResId) { + if (mDisabledMessageResId != disabledMessageResId) { + mDisabledMessageResName = null; + } + mDisabledMessageResId = disabledMessageResId; + mDisabledMessage = null; + } + + /** @hide */ + public void setDisabledMessage(String disabledMessage) { + mDisabledMessage = disabledMessage; + mDisabledMessageResId = 0; + mDisabledMessageResName = null; + } + + /** @hide */ + public String getTitleResName() { + return mTitleResName; + } + + /** @hide */ + public void setTitleResName(String titleResName) { + mTitleResName = titleResName; + } + + /** @hide */ + public String getTextResName() { + return mTextResName; + } + + /** @hide */ + public void setTextResName(String textResName) { + mTextResName = textResName; + } + + /** @hide */ + public String getDisabledMessageResName() { + return mDisabledMessageResName; + } + + /** @hide */ + public void setDisabledMessageResName(String disabledMessageResName) { + mDisabledMessageResName = disabledMessageResName; + } + + /** @hide */ + public String getIconResName() { + return mIconResName; + } + + /** @hide */ + public void setIconResName(String iconResName) { + mIconResName = iconResName; + } + + /** + * Replaces the intent + * + * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}. + * + * @hide + */ + public void setIntents(Intent[] intents) throws IllegalArgumentException { + Preconditions.checkNotNull(intents); + Preconditions.checkArgument(intents.length > 0); + + mIntents = cloneIntents(intents); + fixUpIntentExtras(); + } + + /** @hide */ + public static Intent setIntentExtras(Intent intent, PersistableBundle extras) { + if (extras == null) { + intent.replaceExtras((Bundle) null); + } else { + intent.replaceExtras(new Bundle(extras)); + } + return intent; + } + + /** + * Replaces the categories. + * + * @hide + */ + public void setCategories(Set categories) { + mCategories = cloneCategories(categories); + } + private ShortcutInfo(Parcel source) { final ClassLoader cl = getClass().getClassLoader(); mUserId = source.readInt(); mId = source.readString(); mPackageName = source.readString(); - mActivityComponent = source.readParcelable(cl); + mActivity = source.readParcelable(cl); + mFlags = source.readInt(); + mIconResId = source.readInt(); + mLastChangedTimestamp = source.readLong(); + + if (source.readInt() == 0) { + return; // key information only. + } + mIcon = source.readParcelable(cl); - mTitle = source.readString(); - mText = source.readString(); - mIntent = source.readParcelable(cl); - mIntentPersistableExtras = source.readParcelable(cl); - mWeight = source.readInt(); + mTitle = source.readCharSequence(); + mTitleResId = source.readInt(); + mText = source.readCharSequence(); + mTextResId = source.readInt(); + mDisabledMessage = source.readCharSequence(); + mDisabledMessageResId = source.readInt(); + mIntents = source.readParcelableArray(cl, Intent.class); + mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class); + mRank = source.readInt(); mExtras = source.readParcelable(cl); - mLastChangedTimestamp = source.readLong(); - mFlags = source.readInt(); - mIconResourceId = source.readInt(); mBitmapPath = source.readString(); + mIconResName = source.readString(); + mTitleResName = source.readString(); + mTextResName = source.readString(); + mDisabledMessageResName = source.readString(); + int N = source.readInt(); if (N == 0) { mCategories = null; @@ -763,20 +1643,36 @@ public final class ShortcutInfo implements Parcelable { dest.writeInt(mUserId); dest.writeString(mId); dest.writeString(mPackageName); - dest.writeParcelable(mActivityComponent, flags); - dest.writeParcelable(mIcon, flags); - dest.writeString(mTitle); - dest.writeString(mText); + dest.writeParcelable(mActivity, flags); + dest.writeInt(mFlags); + dest.writeInt(mIconResId); + dest.writeLong(mLastChangedTimestamp); - dest.writeParcelable(mIntent, flags); - dest.writeParcelable(mIntentPersistableExtras, flags); - dest.writeInt(mWeight); + if (hasKeyFieldsOnly()) { + dest.writeInt(0); + return; + } + dest.writeInt(1); + + dest.writeParcelable(mIcon, flags); + dest.writeCharSequence(mTitle); + dest.writeInt(mTitleResId); + dest.writeCharSequence(mText); + dest.writeInt(mTextResId); + dest.writeCharSequence(mDisabledMessage); + dest.writeInt(mDisabledMessageResId); + + dest.writeParcelableArray(mIntents, flags); + dest.writeParcelableArray(mIntentPersistableExtrases, flags); + dest.writeInt(mRank); dest.writeParcelable(mExtras, flags); - dest.writeLong(mLastChangedTimestamp); - dest.writeInt(mFlags); - dest.writeInt(mIconResourceId); dest.writeString(mBitmapPath); + dest.writeString(mIconResName); + dest.writeString(mTitleResName); + dest.writeString(mTextResName); + dest.writeString(mDisabledMessageResName); + if (mCategories != null) { final int N = mCategories.size(); dest.writeInt(N); @@ -823,24 +1719,67 @@ public final class ShortcutInfo implements Parcelable { sb.append("id="); sb.append(secure ? "***" : mId); - sb.append(", packageName="); - sb.append(mPackageName); - + sb.append(", flags=0x"); + sb.append(Integer.toHexString(mFlags)); + sb.append(" ["); + if (!isEnabled()) { + sb.append("X"); + } + if (isImmutable()) { + sb.append("Im"); + } + if (isManifestShortcut()) { + sb.append("M"); + } if (isDynamic()) { - sb.append(", dynamic"); + sb.append("D"); } if (isPinned()) { - sb.append(", pinned"); + sb.append("P"); + } + if (hasIconFile()) { + sb.append("If"); + } + if (hasIconResource()) { + sb.append("Ir"); + } + if (hasKeyFieldsOnly()) { + sb.append("K"); } + if (hasStringResourcesResolved()) { + sb.append("Sr"); + } + sb.append("]"); + + sb.append(", packageName="); + sb.append(mPackageName); sb.append(", activity="); - sb.append(mActivityComponent); + sb.append(mActivity); - sb.append(", title="); + sb.append(", shortLabel="); sb.append(secure ? "***" : mTitle); + sb.append(", resId="); + sb.append(mTitleResId); + sb.append("["); + sb.append(mTitleResName); + sb.append("]"); - sb.append(", text="); + sb.append(", longLabel="); sb.append(secure ? "***" : mText); + sb.append(", resId="); + sb.append(mTextResId); + sb.append("["); + sb.append(mTextResName); + sb.append("]"); + + sb.append(", disabledMessage="); + sb.append(secure ? "***" : mDisabledMessage); + sb.append(", resId="); + sb.append(mDisabledMessageResId); + sb.append("["); + sb.append(mDisabledMessageResName); + sb.append("]"); sb.append(", categories="); sb.append(mCategories); @@ -848,28 +1787,44 @@ public final class ShortcutInfo implements Parcelable { sb.append(", icon="); sb.append(mIcon); - sb.append(", weight="); - sb.append(mWeight); + sb.append(", rank="); + sb.append(mRank); sb.append(", timestamp="); sb.append(mLastChangedTimestamp); - sb.append(", intent="); - sb.append(mIntent); - - sb.append(", intentExtras="); - sb.append(secure ? "***" : mIntentPersistableExtras); + sb.append(", intents="); + if (mIntents == null) { + sb.append("null"); + } else { + if (secure) { + sb.append("size:"); + sb.append(mIntents.length); + } else { + final int size = mIntents.length; + sb.append("["); + String sep = ""; + for (int i = 0; i < size; i++) { + sb.append(sep); + sep = ", "; + sb.append(mIntents[i]); + sb.append("/"); + sb.append(mIntentPersistableExtrases[i]); + } + sb.append("]"); + } + } sb.append(", extras="); sb.append(mExtras); - sb.append(", flags="); - sb.append(mFlags); - if (includeInternalData) { sb.append(", iconRes="); - sb.append(mIconResourceId); + sb.append(mIconResId); + sb.append("["); + sb.append(mIconResName); + sb.append("]"); sb.append(", bitmapPath="); sb.append(mBitmapPath); @@ -881,26 +1836,36 @@ public final class ShortcutInfo implements Parcelable { /** @hide */ public ShortcutInfo( - @UserIdInt int userId, String id, String packageName, ComponentName activityComponent, - Icon icon, String title, String text, Set categories, Intent intent, - PersistableBundle intentPersistableExtras, - int weight, PersistableBundle extras, long lastChangedTimestamp, - int flags, int iconResId, String bitmapPath) { + @UserIdInt int userId, String id, String packageName, ComponentName activity, + Icon icon, CharSequence title, int titleResId, String titleResName, + CharSequence text, int textResId, String textResName, + CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName, + Set categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, + long lastChangedTimestamp, + int flags, int iconResId, String iconResName, String bitmapPath) { mUserId = userId; mId = id; mPackageName = packageName; - mActivityComponent = activityComponent; + mActivity = activity; mIcon = icon; mTitle = title; + mTitleResId = titleResId; + mTitleResName = titleResName; mText = text; - mCategories = clone(categories); - mIntent = intent; - mIntentPersistableExtras = intentPersistableExtras; - mWeight = weight; + mTextResId = textResId; + mTextResName = textResName; + mDisabledMessage = disabledMessage; + mDisabledMessageResId = disabledMessageResId; + mDisabledMessageResName = disabledMessageResName; + mCategories = cloneCategories(categories); + mIntents = cloneIntents(intentsWithExtras); + fixUpIntentExtras(); + mRank = rank; mExtras = extras; mLastChangedTimestamp = lastChangedTimestamp; mFlags = flags; - mIconResourceId = iconResId; + mIconResId = iconResId; + mIconResName = iconResName; mBitmapPath = bitmapPath; } } diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 16486e13e5279450819f8f9c02744fecaa80b1cc..cd248ea4e981ac2653b6f63bd62fc2073b07ced5 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -17,8 +17,11 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.TestApi; +import android.annotation.UserIdInt; +import android.app.Activity; +import android.app.usage.UsageStatsManager; import android.content.Context; -import android.os.IBinder; +import android.content.Intent; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -27,69 +30,416 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.List; -// TODO Enhance javadoc /** - * {@link ShortcutManager} manages shortcuts created by applications. + * The ShortcutManager manages "launcher shortcuts" (or simply "shortcuts"). Shortcuts provide + * users + * with quick access to activities other than an application's main activity in the currently-active + * launcher. For example, + * an email application may publish the "compose new email" action, which will directly open the + * compose activity. The {@link ShortcutInfo} class contains information about each of the + * shortcuts themselves. * - *

Dynamic shortcuts and pinned shortcuts

+ *

Dynamic Shortcuts and Manifest Shortcuts

* - * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and - * {@link #addDynamicShortcuts(List)}. There can be at most - * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same + * There are two ways to publish shortcuts: manifest shortcuts and dynamic shortcuts. + * + *
    + *
  • Manifest shortcuts are declared in a resource + * XML, which is referenced in the publisher application's AndroidManifest.xml file. + * Manifest shortcuts are published when an application is installed, + * and the details of these shortcuts change when an application is upgraded with an updated XML + * file. + * Manifest shortcuts are immutable, and their + * definitions, such as icons and labels, cannot be changed dynamically without upgrading the + * publisher application. + * + *
  • Dynamic shortcuts are published at runtime using the {@link ShortcutManager} APIs. + * Applications can publish, update, and remove dynamic shortcuts at runtime. + *
+ * + *

Only "main" activities—activities that handle the {@code MAIN} action and the + * {@code LAUNCHER} category—can have shortcuts. + * If an application has multiple main activities, these activities will have different sets + * of shortcuts. + * + *

Dynamic shortcuts and manifest shortcuts are shown in the currently active launcher when + * the user long-presses on an application launcher icon. The actual gesture may be different + * depending on the launcher application. + * + *

Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of + * dynamic and manifest shortcuts combined. + * + * + *

Pinning Shortcuts

+ * + * Launcher applications allow users to "pin" shortcuts so they're easier to access. Both manifest + * and dynamic shortcuts can be pinned. + * Pinned shortcuts cannot be removed by publisher + * applications; they're removed only when the user removes them, + * when the publisher application is uninstalled, or when the + * user performs the "clear data" action on the publisher application from the device's Settings * application. - * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps - * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts. * - *

The shortcuts that are currently published by the above APIs are called "dynamic", because - * they can be removed by the creator application at any time. The user may "pin" dynamic shortcuts - * on Launcher to make "pinned" shortcuts. Pinned shortcuts cannot be removed by the creator - * app. An application can obtain all pinned shortcuts from itself with - * {@link #getPinnedShortcuts()}. Applications should keep the pinned shortcut information - * up-to-date using {@link #updateShortcuts(List)}. + *

However, the publisher application can disable pinned shortcuts so they cannot be + * started. See the following sections for details. + * + * + *

Updating and Disabling Shortcuts

+ * + *

When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut, + * the pinned shortcut will still be visible and launchable. This allows an application to have + * more than {@link #getMaxShortcutCountPerActivity()} number of shortcuts. + * + *

For example, suppose {@link #getMaxShortcutCountPerActivity()} is 5: + *

    + *
  • A chat application publishes 5 dynamic shortcuts for the 5 most recent + * conversations, "c1" - "c5". + * + *
  • The user pins all 5 of the shortcuts. + * + *
  • Later, the user has started 3 additional conversations ("c6", "c7", and "c8"), + * so the publisher application + * re-publishes its dynamic shortcuts. The new dynamic shortcut list is: + * "c4", "c5", "c6", "c7", and "c8". + * The publisher application has to remove "c1", "c2", and "c3" because it can't have more than + * 5 dynamic shortcuts. + * + *
  • However, even though "c1", "c2" and "c3" are no longer dynamic shortcuts, the pinned + * shortcuts for these conversations are still available and launchable. + * + *
  • At this point, the user can access a total of 8 shortcuts that link to activities in + * the publisher application, including the 3 pinned + * shortcuts, even though it's allowed to have at most 5 dynamic shortcuts. + * + *
  • The application can use {@link #updateShortcuts(List)} to update any of the existing + * 8 shortcuts, when, for example, the chat peers' icons have changed. + *
+ * The {@link #addDynamicShortcuts(List)} and {@link #setDynamicShortcuts(List)} methods + * can also be used + * to update existing shortcuts with the same IDs, but they cannot be used + * for updating non-dynamic, pinned shortcuts because these two methods try to convert the given + * lists of shortcuts to dynamic shortcuts. + * + * + *

Disabling Manifest Shortcuts

+ * When an application is upgraded and the new version + * no longer uses a manifest shortcut that appeared in the previous version, this deprecated + * shortcut will no longer be published as a manifest shortcut. + * + *

If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher, + * but it will be disabled automatically. + * Note that, in this case, the pinned shortcut is no longer a manifest shortcut, but it's + * still immutable and cannot be updated using the {@link ShortcutManager} APIs. + * + * + *

Disabling Dynamic Shortcuts

+ * Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut + * to a group chat will be unusable when the associated group chat is deleted. In cases like this, + * applications should use {@link #disableShortcuts(List)}, which will remove the specified dynamic + * shortcuts and also make any specified pinned shortcuts un-launchable. + * The {@link #disableShortcuts(List, CharSequence)} method can also be used to disabled shortcuts + * and show users a custom error message when they attempt to launch the disabled shortcuts. + * + * + *

Publishing Manifest Shortcuts

+ * + * In order to add manifest shortcuts to your application, first add + * {@code } to your main activity in + * AndroidManifest.xml: + *
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ *   package="com.example.myapplication">
+ *   <application . . .>
+ *     <activity android:name="Main">
+ *       <intent-filter>
+ *         <action android:name="android.intent.action.MAIN" />
+ *         <category android:name="android.intent.category.LAUNCHER" />
+ *       </intent-filter>
+ *       <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+ *     </activity>
+ *   </application>
+ * </manifest>
+ * 
+ * + * Then, define your application's manifest shortcuts in the res/xml/shortcuts.xml + * file: + *
+ * <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ *   <shortcut
+ *     android:shortcutId="compose"
+ *     android:enabled="true"
+ *     android:icon="@drawable/compose_icon"
+ *     android:shortcutShortLabel="@string/compose_shortcut_short_label1"
+ *     android:shortcutLongLabel="@string/compose_shortcut_long_label1"
+ *     android:shortcutDisabledMessage="@string/compose_disabled_message1"
+ *     >
+ *     <intent
+ *       android:action="android.intent.action.VIEW"
+ *       android:targetPackage="com.example.myapplication"
+ *       android:targetClass="com.example.myapplication.ComposeActivity" />
+ *     <!-- more intents can go here; see below -->
+ *     <categories android:name="android.shortcut.conversation" />
+ *   </shortcut>
+ *   <!-- more shortcuts can go here -->
+ * </shortcuts>
+ * 
+ * + * The following list includes descriptions for the different attributes within a manifest shortcut: + *
+ *
android:shortcutId
+ *
Mandatory shortcut ID
+ * + *
android:enabled
+ *
Default is {@code true}. Can be set to {@code false} in order + * to disable a manifest shortcut that was published in a previous version and and set a custom + * disabled message. If a custom disabled message is not needed, then a manifest shortcut can + * be simply removed from the XML file rather than keeping it with {@code enabled="false"}.
+ * + *
android:icon
+ *
Shortcut icon.
+ * + *
android:shortcutShortLabel
+ *
Mandatory shortcut short label. + * See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.
+ * + *
android:shortcutLongLabel
+ *
Shortcut long label. + * See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.
+ * + *
android:shortcutDisabledMessage
+ *
When {@code android:enabled} is set to + * {@code false}, this attribute is used to display a custom disabled message.
+ * + *
intent
+ *
Intent to launch when the user selects the shortcut. + * {@code android:action} is mandatory. + * See Using intents for the + * other supported tags. + * You can provide multiple intents for a single shortcut so that an activity is launched + * with other activities in the back stack. See {@link android.app.TaskStackBuilder} for details. + *
+ *
categories
+ *
Specify shortcut categories. Currently only + * {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework. + *
+ *
+ * + *

Publishing Dynamic Shortcuts

+ * + * Applications can publish dynamic shortcuts with {@link #setDynamicShortcuts(List)} + * or {@link #addDynamicShortcuts(List)}. The {@link #updateShortcuts(List)} method can also be + * used to update existing, mutable shortcuts. + * Use {@link #removeDynamicShortcuts(List)} or {@link #removeAllDynamicShortcuts()} to remove + * dynamic shortcuts. + * + *

Example: + *

+ * ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
+ *
+ * ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
+ *     .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mysite.com/")))
+ *     .setShortLabel("Web site")
+ *     .setLongLabel("Open the web site")
+ *     .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
+ *     .build();
+ *
+ * shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
+ * 
+ * + * + *

Shortcut Intents

+ * Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags. + * Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other + * flags; otherwise, if the application is already running, the application is simply brought to + * the foreground, and the target activity may not appear. + * + *

The {@link ShortcutInfo.Builder#setIntents(Intent[])} method can be used instead of + * {@link ShortcutInfo.Builder#setIntent(Intent)} with {@link android.app.TaskStackBuilder} + * in order to launch an activity with other activities in the back stack. + * When the user selects a shortcut to load an activity with a back stack, + * then presses the back key, a "parent" activity will be shown instead of the user being + * navigated back to the launcher. + * + *

Manifest shortcuts can also have multiple intents to achieve the same effect. + * In order to associate multiple {@link Intent} objects with a shortcut, simply list multiple + * <intent> elements within a single <shortcut> element. + * The last intent specifies what the user will see when they launch a shortcut. + * + *

Manifest shortcuts cannot have custom intent flags. + * The first intent of a manifest shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK} + * and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. + * This means, when the application is already running, all the existing activities will be + * destroyed when a manifest shortcut is launched. + * If this behavior is not desirable, you can use a trampoline activity, + * or an invisible activity that starts another activity in {@link Activity#onCreate}, + * then calls {@link Activity#finish()}. + * The first activity should include an attribute setting + * of {@code android:taskAffinity=""} in the application's AndroidManifest.xml + * file, and the intent within the manifest shortcut should point at this first activity. + * + * + *

Showing New Information in a Shortcut

+ * In order to avoid confusion, you should not use {@link #updateShortcuts(List)} to update + * a shortcut so that it contains conceptually different information. + * + *

For example, a phone application may publish the most frequently called contact as a dynamic + * shortcut. Over time, this contact may change; when it does, the application should + * represent the changed contact with a new shortcut that contains a different ID, using either + * {@link #setDynamicShortcuts(List)} or {@link #addDynamicShortcuts(List)}, rather than updating + * the existing shortcut with {@link #updateShortcuts(List)}. + * This is because when the shortcut is pinned, changing + * it to reference a different contact will likely confuse the user. + * + *

On the other hand, when the + * contact's information has changed, such as the name or picture, the application should + * use {@link #updateShortcuts(List)} so that the pinned shortcut is updated too. * - *

The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be - * published by an application at a time. - * No matter how many pinned shortcuts that Launcher has for an application, the - * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic - * shortcuts. * - *

Shortcut IDs

+ *

Shortcut Display Order

+ * When the launcher displays the shortcuts that are associated with a particular launcher icon, + * the shortcuts should appear in the following order: + *
    + *
  • First show manifest shortcuts + * (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}), + * and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}). + *
  • Within each category of shortcuts (manifest and dynamic), sort the shortcuts in order + * of increasing rank according to {@link ShortcutInfo#getRank()}. + *
+ *

Shortcut ranks are non-negative sequential integers + * that determine the order in which shortcuts appear, assuming that the shortcuts are all in + * the same category. + * Ranks of existing shortcuts can be updated with + * {@link #updateShortcuts(List)}; you can use {@link #addDynamicShortcuts(List)} and + * {@link #setDynamicShortcuts(List)}, too. * - * Each shortcut must have an ID, which must be unique within each application. When a shortcut is - * published, existing shortcuts with the same ID will be updated. Note this may include a - * pinned shortcut. + *

Ranks are auto-adjusted so that they're unique for each target activity in each category + * (dynamic or manifest). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2, + * adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at + * the second position. + * In response, the third and fourth shortcuts move closer to the bottom of the shortcut list, + * with their ranks changing to 2 and 3, respectively. * - *

Rate limiting

+ *

Rate Limiting

* - * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, - * and {@link #updateShortcuts(List)} from background applications will be - * rate-limited. An application can call these methods at most - * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset, - * which happens at a certain time every day. + * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)}, and + * {@link #updateShortcuts(List)} may be rate-limited when called by background applications, or + * applications with no foreground activity or service. When you attempt to call these methods + * from a background application after exceeding the rate limit, these APIs return {@code false}. * - *

An application can use {@link #getRateLimitResetTime()} to get the next reset time. + *

Applications with a foreground activity or service are not rate-limited. * - *

Foreground applications (i.e. ones with a foreground activity or a foreground services) - * will not be throttled. Also, when an application comes to foreground, - * {@link #getRemainingCallCount()} will be reset to the initial value. + *

Rate-limiting will be reset upon certain events, so that even background applications + * can call these APIs again until they are rate limit is reached again. + * These events include the following: + *

    + *
  • When an application comes to the foreground. + *
  • When the system locale changes. + *
  • When the user performs an "inline reply" action on a notification. + *
+ * + *

When rate-limiting is active, {@link #isRateLimitingActive()} returns {@code true}. + * + *

Resetting rate-limiting for testing

+ * + * If your application is rate-limited during development or testing, you can use the + * "Reset ShortcutManager rate-limiting" development option or the following adb command to reset + * it: + *
+ * adb shell cmd shortcut reset-throttling [ --user USER-ID ]
+ * 
+ * + *

Handling System Locale Changes

+ * + * Applications should update dynamic and pinned shortcuts when the system locale changes + * using the {@link Intent#ACTION_LOCALE_CHANGED} broadcast. + * + *

When the system locale changes, rate-limiting is reset, so even background applications + * can set dynamic shortcuts, add dynamic shortcuts, and update shortcuts until the rate limit + * is reached again. * - *

For testing purposes, use "Developer Options" (found in the Settings menu) to reset the - * internal rate-limiting counter. Automated tests can use the following ADB shell command to - * achieve the same effect:

- *
adb shell cmd shortcut reset-throttling
* *

Backup and Restore

* - * Shortcuts will be backed up and restored across devices. This means all information, including - * IDs, must be meaningful on a different device. + * When an application has the {@code android:allowBackup="true"} attribute assignment included + * in its AndroidManifest.xml file, pinned shortcuts are + * backed up automatically and are restored when the user sets up a new device. + * + *

Categories of Shortcuts that are Backed Up

+ * + *
    + *
  • Pinned shortcuts are backed up. Bitmap icons are not backed up by the system, + * but launcher applications should back them up and restore them so that the user still sees icons + * for pinned shortcuts on the launcher. Applications can always use + * {@link #updateShortcuts(List)} to re-publish icons. + * + *
  • Manifest shortcuts are not backed up, but when an application is re-installed on a new + * device, they are re-published from the AndroidManifest.xml file, anyway. + * + *
  • Dynamic shortcuts are not backed up. + *
+ * + *

Because dynamic shortcuts are not restored, it is recommended that applications check + * currently-published dynamic shortcuts using {@link #getDynamicShortcuts()} + * each time they are launched, and they should re-publish + * dynamic shortcuts when necessary. + * + *

+ * public class MainActivity extends Activity {
+ *     public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *
+ *         ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
+ *
+ *         if (shortcutManager.getDynamicShortcuts().size() == 0) {
+ *             // Application restored; re-publish dynamic shortcuts.
+ *
+ *             if (shortcutManager.getPinnedShortcuts().size() > 0) {
+ *                 // Pinned shortcuts have been restored.  Use updateShortcuts() to make sure
+ *                 // they have up-to-date information.
+ *             }
+ *         }
+ *     }
+ *     :
+ *
+ * }
+ * 
+ * + * + *

Backup/restore and shortcut IDs

+ * + * Because pinned shortcuts are backed up and restored on new devices, shortcut IDs should be + * meaningful across devices; that is, IDs should contain either stable, constant strings + * or server-side identifiers, + * rather than identifiers generated locally that might not make sense on other devices. + * + * + *

Report Shortcut Usage and Prediction

+ * + * Launcher applications may be capable of predicting which shortcuts will most likely be + * used at a given time by examining the shortcut usage history data. + * + *

In order to provide launchers with such data, publisher applications should + * report the shortcuts that are used with {@link #reportShortcutUsed(String)} + * when a shortcut is selected, + * or when an action equivalent to a shortcut is taken by the user even if it wasn't started + * with the shortcut. * - *

APIs for launcher

+ *

For example, suppose a GPS navigation application supports "navigate to work" as a shortcut. + * It should then report when the user selects this shortcut and when the user chooses + * to navigate to work within the application itself. + * This helps the launcher application + * learn that the user wants to navigate to work at a certain time every + * weekday, and it can then show this shortcut in a suggestion list at the right time. * - * Launcher applications should use {@link LauncherApps} to get shortcuts that are published from - * applications. Launcher applications can also pin shortcuts with - * {@link LauncherApps#pinShortcuts(String, List, UserHandle)}. + *

Launcher API

* - * @hide + * The {@link LauncherApps} class provides APIs for launcher applications to access shortcuts. + * + * + *

Direct Boot and Shortcuts

+ * + * All shortcut information is stored in credential encrypted storage, so no shortcuts can be + * accessed when the user is locked. */ public class ShortcutManager { private static final String TAG = "ShortcutManager"; @@ -115,15 +465,18 @@ public class ShortcutManager { } /** - * Publish a list of shortcuts. All existing dynamic shortcuts from the caller application - * will be replaced. + * Publish the list of shortcuts. All existing dynamic shortcuts from the caller application + * will be replaced. If there are already pinned shortcuts with the same IDs, + * the mutable pinned shortcuts are updated. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * - * @throws IllegalArgumentException if {@code shortcutInfoList} contains more than - * {@link #getMaxDynamicShortcutCount()} shortcuts. + * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, + * or when trying to update immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. */ public boolean setDynamicShortcuts(@NonNull List shortcutInfoList) { try { @@ -135,8 +488,9 @@ public class ShortcutManager { } /** - * Return all dynamic shortcuts from the caller application. The number of result items - * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}. + * Return all dynamic shortcuts from the caller application. + * + * @throws IllegalStateException when the user is locked. */ @NonNull public List getDynamicShortcuts() { @@ -149,15 +503,32 @@ public class ShortcutManager { } /** - * Publish a single dynamic shortcut. If there's already dynamic or pinned shortcuts with - * the same ID, they will all be updated. + * Return all manifest shortcuts from the caller application. + * + * @throws IllegalStateException when the user is locked. + */ + @NonNull + public List getManifestShortcuts() { + try { + return mService.getManifestShortcuts(mContext.getPackageName(), injectMyUserId()) + .getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Publish the list of dynamic shortcuts. If there are already dynamic or pinned shortcuts with + * the same IDs, each mutable shortcut is updated. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. * - * @throws IllegalArgumentException if the caller application has already published the - * max number of dynamic shortcuts. + * @throws IllegalArgumentException if {@link #getMaxShortcutCountPerActivity()} is exceeded, + * or when trying to update immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. */ public boolean addDynamicShortcuts(@NonNull List shortcutInfoList) { try { @@ -169,7 +540,9 @@ public class ShortcutManager { } /** - * Delete a single dynamic shortcut by ID. + * Delete dynamic shortcuts by ID. + * + * @throws IllegalStateException when the user is locked. */ public void removeDynamicShortcuts(@NonNull List shortcutIds) { try { @@ -182,6 +555,8 @@ public class ShortcutManager { /** * Delete all dynamic shortcuts from the caller application. + * + * @throws IllegalStateException when the user is locked. */ public void removeAllDynamicShortcuts() { try { @@ -193,6 +568,8 @@ public class ShortcutManager { /** * Return all pinned shortcuts from the caller application. + * + * @throws IllegalStateException when the user is locked. */ @NonNull public List getPinnedShortcuts() { @@ -205,11 +582,16 @@ public class ShortcutManager { } /** - * Update all existing shortcuts with the same IDs. Shortcuts may be pinned and/or dynamic. + * Update all existing shortcuts with the same IDs. Target shortcuts may be pinned and/or + * dynamic, but they must not be immutable. * *

This API will be rate-limited. * * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited. + * + * @throws IllegalArgumentException If trying to update immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. */ public boolean updateShortcuts(List shortcutInfoList) { try { @@ -221,11 +603,94 @@ public class ShortcutManager { } /** - * Return the max number of dynamic shortcuts that each application can have at a time. + * Disable pinned shortcuts. For more details, see the Javadoc for the {@link ShortcutManager} + * class. + * + * @throws IllegalArgumentException If trying to disable immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. + */ + public void disableShortcuts(@NonNull List shortcutIds) { + try { + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + /* disabledMessage =*/ null, /* disabledMessageResId =*/ 0, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide old signature, kept for unit testing. */ - public int getMaxDynamicShortcutCount() { + public void disableShortcuts(@NonNull List shortcutIds, int disabledMessageResId) { try { - return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId()); + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + /* disabledMessage =*/ null, disabledMessageResId, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide old signature, kept for unit testing. + */ + public void disableShortcuts(@NonNull List shortcutIds, String disabledMessage) { + disableShortcuts(shortcutIds, (CharSequence) disabledMessage); + } + + /** + * Disable pinned shortcuts, showing the user a custom error message when they try to select + * the disabled shortcuts. + * For more details, see the Javadoc for the {@link ShortcutManager} class. + * + * @throws IllegalArgumentException If trying to disable immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. + */ + public void disableShortcuts(@NonNull List shortcutIds, CharSequence disabledMessage) { + try { + mService.disableShortcuts(mContext.getPackageName(), shortcutIds, + disabledMessage, /* disabledMessageResId =*/ 0, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Re-enable pinned shortcuts that were previously disabled. If the target shortcuts + * already enabled, this method does nothing. + * + * @throws IllegalArgumentException If trying to enable immutable shortcuts. + * + * @throws IllegalStateException when the user is locked. + */ + public void enableShortcuts(@NonNull List shortcutIds) { + try { + mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** + * @hide old signature, kept for unit testing. + */ + public int getMaxShortcutCountForActivity() { + return getMaxShortcutCountPerActivity(); + } + + /** + * Return the maximum number of dynamic and manifest shortcuts that each launcher icon + * can have at a time. + */ + public int getMaxShortcutCountPerActivity() { + try { + return mService.getMaxShortcutCountPerActivity( + mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -236,6 +701,8 @@ public class ShortcutManager { * before the rate limit counter is reset. * * @see #getRateLimitResetTime() + * + * @hide */ public int getRemainingCallCount() { try { @@ -250,6 +717,8 @@ public class ShortcutManager { * * @see #getRemainingCallCount() * @see System#currentTimeMillis() + * + * @hide */ public long getRateLimitResetTime() { try { @@ -260,16 +729,82 @@ public class ShortcutManager { } /** - * Return the max width and height for icons, in pixels. + * Return {@code true} when rate-limiting is active for the caller application. + * + *

See the class level javadoc for details. + * + * @throws IllegalStateException when the user is locked. + */ + public boolean isRateLimitingActive() { + try { + return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId()) + == 0; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the max width for icons, in pixels. + */ + public int getIconMaxWidth() { + try { + // TODO Implement it properly using xdpi. + return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the max height for icons, in pixels. */ - public int getIconMaxDimensions() { + public int getIconMaxHeight() { try { + // TODO Implement it properly using ydpi. return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** + * Applications that publish shortcuts should call this method + * whenever the user selects the shortcut containing the given ID or when the user completes + * an action in the application that is equivalent to selecting the shortcut. + * For more details, see the Javadoc for the {@link ShortcutManager} class + * + *

The information is accessible via {@link UsageStatsManager#queryEvents} + * Typically, launcher applications use this information to build a prediction model + * so that they can promote the shortcuts that are likely to be used at the moment. + * + * @throws IllegalStateException when the user is locked. + */ + public void reportShortcutUsed(String shortcutId) { + try { + mService.reportShortcutUsed(mContext.getPackageName(), shortcutId, + injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Called internally when an application is considered to have come to foreground + * even when technically it's not. This method resets the throttling for this package. + * For example, when the user sends an "inline reply" on an notification, the system UI will + * call it. + * + * @hide + */ + public void onApplicationActive(@NonNull String packageName, @UserIdInt int userId) { + try { + mService.onApplicationActive(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide injection point */ @VisibleForTesting protected int injectMyUserId() { diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 3f8bad15035bfe08834c675f18a2ee704db96d23..af5610570f70ed260bfed84e4a7f35f73cdf55ee 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -53,7 +53,8 @@ public abstract class ShortcutServiceInternal { @NonNull String callingPackage, @NonNull String packageName, @NonNull List shortcutIds, int userId); - public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage, + public abstract Intent[] createShortcutIntents( + int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId, int userId); public abstract void addListener(@NonNull ShortcutChangeListener listener); @@ -67,10 +68,4 @@ public abstract class ShortcutServiceInternal { public abstract boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage); - - /** - * Called by AM when the system locale changes *within the AM lock*. ABSOLUTELY do not take - * any locks in this method. - */ - public abstract void onSystemLocaleChangedNoLock(); } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index dd3a36c7996c5e08670b452e1c743e158ca58025..6cd84033da8c137955e138e3c8b323ce3d24882f 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -28,8 +28,8 @@ import android.os.UserManager; */ public class UserInfo implements Parcelable { - /** 8 bits for user type */ - public static final int FLAG_MASK_USER_TYPE = 0x000000FF; + /** 16 bits for user type */ + public static final int FLAG_MASK_USER_TYPE = 0x0000FFFF; /** * *************************** NOTE *************************** @@ -87,6 +87,11 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_EPHEMERAL = 0x00000100; + /** + * User is for demo purposes only and can be removed at any time. + */ + public static final int FLAG_DEMO = 0x00000200; + public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL; public int id; @@ -153,6 +158,10 @@ public class UserInfo implements Parcelable { return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED; } + public boolean isDemo() { + return (flags & FLAG_DEMO) == FLAG_DEMO; + } + /** * Returns true if the user is a split system user. *

If {@link UserManager#isSplitSystemUser split system user mode} is not enabled, diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 6f43d99ecb9583e4f1f192624d44d27ef3b027ad..b2d518c56ca0a4fc68746673f82effd305666d79 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1543,27 +1543,41 @@ public final class Configuration implements Parcelable, Comparable - * When it returns, the array values is filled with the result: + * When it returns, the array values are as follows: *

    - *
  • values[0]: azimuth, rotation around the -Z axis, - * i.e. the opposite direction of Z axis.
  • - *
  • values[1]: pitch, rotation around the -X axis, - * i.e the opposite direction of X axis.
  • - *
  • values[2]: roll, rotation around the Y axis.
  • + *
  • values[0]: Azimuth, angle of rotation about the -z axis. + * This value represents the angle between the device's y + * axis and the magnetic north pole. When facing north, this + * angle is 0, when facing south, this angle is π. + * Likewise, when facing east, this angle is π/2, and + * when facing west, this angle is -π/2. The range of + * values is -π to π.
  • + *
  • values[1]: Pitch, angle of rotation about the x axis. + * This value represents the angle between a plane parallel + * to the device's screen and a plane parallel to the ground. + * Assuming that the bottom edge of the device faces the + * user and that the screen is face-up, tilting the top edge + * of the device toward the ground creates a positive pitch + * angle. The range of values is -π to π.
  • + *
  • values[2]: Roll, angle of rotation about the y axis. This + * value represents the angle between a plane perpendicular + * to the device's screen and a plane perpendicular to the + * ground. Assuming that the bottom edge of the device faces + * the user and that the screen is face-up, tilting the left + * edge of the device toward the ground creates a positive + * roll angle. The range of values is -π/2 to π/2.
  • *
*

- * Applying these three intrinsic rotations in azimuth, pitch and roll order transforms - * identity matrix to the rotation matrix given in input R. - * All three angles above are in radians and positive in the - * counter-clockwise direction. Range of output is: azimuth from -π to π, - * pitch from -π/2 to π/2 and roll from -π to π. + * Applying these three rotations in the azimuth, pitch, roll order + * transforms an identity matrix to the rotation matrix passed into this + * method. Also, note that all three orientation angles are expressed in + * radians. * * @param R * rotation matrix see {@link #getRotationMatrix}. diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 38279a47f2a9b7040b702c38c5f74311be0e3c5c..4b211873598365768517aff397ac055a3130cf87 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -18,6 +18,7 @@ package android.hardware.camera2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.camera2.params.OutputConfiguration; import android.os.Handler; import android.view.Surface; @@ -219,6 +220,53 @@ public abstract class CameraCaptureSession implements AutoCloseable { */ public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException; + /** + *

+ * Finish the deferred output configurations where the output Surface was not configured before. + *

+ *

+ * For camera use cases where a preview and other output configurations need to be configured, + * it can take some time for the preview Surface to be ready (e.g., if the preview Surface is + * obtained from {@link android.view.SurfaceView}, the SurfaceView is ready after the UI layout + * is done, then it takes some time to get the preview Surface). + *

+ *

+ * To speed up camera startup time, the application can configure the + * {@link CameraCaptureSession} with the desired preview size, and defer the preview output + * configuration until the Surface is ready. After the {@link CameraCaptureSession} is created + * successfully with this deferred configuration and other normal configurations, the + * application can submit requests that don't include deferred output Surfaces. Once the + * deferred Surface is ready, the application can set the Surface to the same deferred output + * configuration with the {@link OutputConfiguration#setDeferredSurface} method, and then finish + * the deferred output configuration via this method, before it can submit requests with this + * output target. + *

+ *

+ * The output Surfaces included by this list of deferred {@link OutputConfiguration + * OutputConfigurations} can be used as {@link CaptureRequest} targets as soon as this call + * returns; + *

+ *

+ * This method is not supported by Legacy devices. + *

+ * + * @param deferredOutputConfigs a list of {@link OutputConfiguration OutputConfigurations} that + * have had {@link OutputConfiguration#setDeferredSurface setDeferredSurface} invoked + * with a valid output Surface. + * @throws CameraAccessException if the camera device is no longer connected or has encountered + * a fatal error. + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created or the camera device has + * been closed. Or if this output configuration was already finished with the + * included surface before. + * @throws IllegalArgumentException for invalid output configurations, including ones where the + * source of the Surface is no longer valid or the Surface is from a unsupported + * source. + * @hide + */ + public abstract void finishDeferredConfiguration( + List deferredOutputConfigs) throws CameraAccessException; + /** *

Submit a request for an image to be captured by the camera device.

* diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 3917bfa6beea58657b2faa204b01761970008900..145b1d076e80789309f8fbae58ee953518cad181 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -267,6 +267,10 @@ public final class CameraManager { * @param cameraId The unique identifier of the camera device to open * @param callback The callback for the camera. Must not be null. * @param handler The handler to invoke the callback on. Must not be null. + * @param uid The UID of the application actually opening the camera. + * Must be USE_CALLING_UID unless the caller is a service + * that is trusted to open the device on behalf of an + * application and to forward the real UID. * * @throws CameraAccessException if the camera is disabled by device policy, * too many camera devices are already open, or the cameraId does not match @@ -281,7 +285,7 @@ public final class CameraManager { * @see android.app.admin.DevicePolicyManager#setCameraDisabled */ private CameraDevice openCameraDeviceUserAsync(String cameraId, - CameraDevice.StateCallback callback, Handler handler) + CameraDevice.StateCallback callback, Handler handler, final int uid) throws CameraAccessException { CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); CameraDevice device = null; @@ -317,7 +321,7 @@ public final class CameraManager { "Camera service is currently unavailable"); } cameraUser = cameraService.connectDevice(callbacks, id, - mContext.getOpPackageName(), USE_CALLING_UID); + mContext.getOpPackageName(), uid); } else { // Use legacy camera implementation for HAL1 devices Log.i(TAG, "Using legacy camera HAL."); @@ -434,6 +438,29 @@ public final class CameraManager { @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) throws CameraAccessException { + openCameraForUid(cameraId, callback, handler, USE_CALLING_UID); + } + + /** + * Open a connection to a camera with the given ID, on behalf of another application + * specified by clientUid. + * + *

The behavior of this method matches that of {@link #openCamera}, except that it allows + * the caller to specify the UID to use for permission/etc verification. This can only be + * done by services trusted by the camera subsystem to act on behalf of applications and + * to forward the real UID.

+ * + * @param clientUid + * The UID of the application on whose behalf the camera is being opened. + * Must be USE_CALLING_UID unless the caller is a trusted service. + * + * @hide + */ + public void openCameraForUid(@NonNull String cameraId, + @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler, + int clientUid) + throws CameraAccessException { + if (cameraId == null) { throw new IllegalArgumentException("cameraId was null"); } else if (callback == null) { @@ -447,7 +474,7 @@ public final class CameraManager { } } - openCameraDeviceUserAsync(cameraId, callback, handler); + openCameraDeviceUserAsync(cameraId, callback, handler, clientUid); } /** diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 6736d3466e2cd80e13e174f5a9a8972a99e79f52..b10c341eb5ec7e4117f09bd1dbc8951380d0b283 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -24,6 +24,7 @@ import android.hardware.camera2.dispatch.BroadcastDispatcher; import android.hardware.camera2.dispatch.DuckTypingDispatcher; import android.hardware.camera2.dispatch.HandlerDispatcher; import android.hardware.camera2.dispatch.InvokeDispatcher; +import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.utils.TaskDrainer; import android.hardware.camera2.utils.TaskSingleDrainer; import android.os.Handler; @@ -155,6 +156,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession mDeviceImpl.tearDown(surface); } + @Override + public void finishDeferredConfiguration( + List deferredOutputConfigs) throws CameraAccessException { + mDeviceImpl.finishDeferredConfig(deferredOutputConfigs); + } + @Override public synchronized int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { @@ -279,23 +286,29 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } @Override - public synchronized void abortCaptures() throws CameraAccessException { - checkNotClosed(); + public void abortCaptures() throws CameraAccessException { + synchronized (this) { + checkNotClosed(); - if (DEBUG) { - Log.v(TAG, mIdString + "abortCaptures"); - } + if (DEBUG) { + Log.v(TAG, mIdString + "abortCaptures"); + } - if (mAborting) { - Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing"); - return; - } + if (mAborting) { + Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing"); + return; + } - mAborting = true; - mAbortDrainer.taskStarted(); + mAborting = true; + mAbortDrainer.taskStarted(); + } - mDeviceImpl.flush(); - // The next BUSY -> IDLE set of transitions will mark the end of the abort. + synchronized (mDeviceImpl.mInterfaceLock) { + synchronized (this) { + mDeviceImpl.flush(); + // The next BUSY -> IDLE set of transitions will mark the end of the abort. + } + } } @Override @@ -323,78 +336,86 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * @see CameraCaptureSession#close */ @Override - public synchronized void replaceSessionClose() { - /* - * In order for creating new sessions to be fast, the new session should be created - * before the old session is closed. - * - * Otherwise the old session will always unconfigure if there is no new session to - * replace it. - * - * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt - * to skip unconfigure if a new session is created before the captures are all drained, - * but this would introduce nondeterministic behavior. - */ - - if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose"); - - // Set up fast shutdown. Possible alternative paths: - // - This session is active, so close() below starts the shutdown drain - // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener. - // - This session is already closed and has executed the idle drain listener, and - // configureOutputsChecked(null) has already been called. - // - // Do not call configureOutputsChecked(null) going forward, since it would race with the - // configuration for the new session. If it was already called, then we don't care, since it - // won't get called again. - mSkipUnconfigure = true; + public void replaceSessionClose() { + synchronized (this) { + /* + * In order for creating new sessions to be fast, the new session should be created + * before the old session is closed. + * + * Otherwise the old session will always unconfigure if there is no new session to + * replace it. + * + * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt + * to skip unconfigure if a new session is created before the captures are all drained, + * but this would introduce nondeterministic behavior. + */ + if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose"); + + // Set up fast shutdown. Possible alternative paths: + // - This session is active, so close() below starts the shutdown drain + // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener. + // - This session is already closed and has executed the idle drain listener, and + // configureOutputsChecked(null) has already been called. + // + // Do not call configureOutputsChecked(null) going forward, since it would race with the + // configuration for the new session. If it was already called, then we don't care, + // since it won't get called again. + mSkipUnconfigure = true; + } close(); } @Override - public synchronized void close() { - - if (mClosed) { - if (DEBUG) Log.v(TAG, mIdString + "close - reentering"); - return; - } + public void close() { + synchronized (this) { + if (mClosed) { + if (DEBUG) Log.v(TAG, mIdString + "close - reentering"); + return; + } - if (DEBUG) Log.v(TAG, mIdString + "close - first time"); + if (DEBUG) Log.v(TAG, mIdString + "close - first time"); - mClosed = true; + mClosed = true; + } - /* - * Flush out any repeating request. Since camera is closed, no new requests - * can be queued, and eventually the entire request queue will be drained. - * - * If the camera device was already closed, short circuit and do nothing; since - * no more internal device callbacks will fire anyway. - * - * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the - * camera. Once that's done, fire #onClosed. - */ - try { - mDeviceImpl.stopRepeating(); - } catch (IllegalStateException e) { - // OK: Camera device may already be closed, nothing else to do + synchronized (mDeviceImpl.mInterfaceLock) { + synchronized (this) { + /* + * Flush out any repeating request. Since camera is closed, no new requests + * can be queued, and eventually the entire request queue will be drained. + * + * If the camera device was already closed, short circuit and do nothing; since + * no more internal device callbacks will fire anyway. + * + * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure + * the camera. Once that's done, fire #onClosed. + */ + try { + mDeviceImpl.stopRepeating(); + } catch (IllegalStateException e) { + // OK: Camera device may already be closed, nothing else to do - // TODO: Fire onClosed anytime we get the device onClosed or the ISE? - // or just suppress the ISE only and rely onClosed. - // Also skip any of the draining work if this is already closed. + // TODO: Fire onClosed anytime we get the device onClosed or the ISE? + // or just suppress the ISE only and rely onClosed. + // Also skip any of the draining work if this is already closed. - // Short-circuit; queue callback immediately and return - mStateCallback.onClosed(this); - return; - } catch (CameraAccessException e) { - // OK: close does not throw checked exceptions. - Log.e(TAG, mIdString + "Exception while stopping repeating: ", e); + // Short-circuit; queue callback immediately and return + mStateCallback.onClosed(this); + return; + } catch (CameraAccessException e) { + // OK: close does not throw checked exceptions. + Log.e(TAG, mIdString + "Exception while stopping repeating: ", e); - // TODO: call onError instead of onClosed if this happens + // TODO: call onError instead of onClosed if this happens + } + } } - // If no sequences are pending, fire #onClosed immediately - mSequenceDrainer.beginDrain(); + synchronized (this) { + // If no sequences are pending, fire #onClosed immediately + mSequenceDrainer.beginDrain(); + } } /** diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 8cd1da50c58a3da848f518730b0121c3c3dcdf78..1c8e124331124d13429b1f2bf823167012ce7579 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -21,6 +21,7 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; @@ -256,6 +257,12 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl return mSessionImpl.isAborting(); } + @Override + public void finishDeferredConfiguration(List deferredOutputConfigs) + throws CameraAccessException { + mSessionImpl.finishDeferredConfiguration(deferredOutputConfigs); + } + private class WrapperCallback extends StateCallback { private final StateCallback mCallback; @@ -263,26 +270,32 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl mCallback = callback; } + @Override public void onConfigured(CameraCaptureSession session) { mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this); } + @Override public void onConfigureFailed(CameraCaptureSession session) { mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this); } + @Override public void onReady(CameraCaptureSession session) { mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this); } + @Override public void onActive(CameraCaptureSession session) { mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this); } + @Override public void onClosed(CameraCaptureSession session) { mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this); } + @Override public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this, surface); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 0cee11445246c84606d5b520749a9ca935eab419..ee8a6d7720372b6ef1bf6038095416ed531c77f9 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -407,7 +407,10 @@ public class CameraDeviceImpl extends CameraDevice int streamId = mConfiguredOutputs.keyAt(i); OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i); - if (!outputs.contains(outConfig)) { + if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) { + // Always delete the deferred output configuration when the session + // is created, as the deferred output configuration doesn't have unique surface + // related identifies. deleteList.add(streamId); } else { addSet.remove(outConfig); // Don't create a stream previously created @@ -744,6 +747,37 @@ public class CameraDeviceImpl extends CameraDevice } } + public void finishDeferredConfig(List deferredConfigs) + throws CameraAccessException { + if (deferredConfigs == null || deferredConfigs.size() == 0) { + throw new IllegalArgumentException("deferred config is null or empty"); + } + + synchronized(mInterfaceLock) { + for (OutputConfiguration config : deferredConfigs) { + int streamId = -1; + for (int i = 0; i < mConfiguredOutputs.size(); i++) { + // Have to use equal here, as createCaptureSessionByOutputConfigurations() and + // createReprocessableCaptureSessionByConfigurations() do a copy of the configs. + if (config.equals(mConfiguredOutputs.valueAt(i))) { + streamId = mConfiguredOutputs.keyAt(i); + break; + } + } + if (streamId == -1) { + throw new IllegalArgumentException("Deferred config is not part of this " + + "session"); + } + + if (config.getSurface() == null) { + throw new IllegalArgumentException("The deferred config for stream " + streamId + + " must have a non-null surface"); + } + mRemoteDevice.setDeferredConfiguration(streamId, config); + } + } + } + public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { if (DEBUG) { @@ -2005,6 +2039,7 @@ public class CameraDeviceImpl extends CameraDevice * *

Handle binder death for ICameraDeviceUser. Trigger onError.

*/ + @Override public void binderDied() { Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly"); diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index ef5f6d71b8b6e0f6da5a4122298c81808e2e0687..d77f60bf0953d31b9c837660ee98ad8dbc9c5c7c 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -215,5 +215,14 @@ public class ICameraDeviceUserWrapper { } } + public void setDeferredConfiguration(int streamId, OutputConfiguration deferredConfig) + throws CameraAccessException { + try { + mRemoteDevice.setDeferredConfiguration(streamId, deferredConfig); + } catch (Throwable t) { + CameraManager.throwAsPublicException(t); + throw new UnsupportedOperationException("Unexpected exception", t); + } + } } diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index acbf214942c52458ced2b12a40fc8217fe3d8a45..b9e75eec1a34c77ca5f39c8bb06ca152aa7532ac 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -566,6 +566,13 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } } + @Override + public void setDeferredConfiguration(int steamId, OutputConfiguration config) { + String err = "Set deferred configuration is not supported on legacy devices"; + Log.e(TAG, err); + throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err); + } + @Override public int createInputStream(int width, int height, int format) { String err = "Creating input stream is not supported on legacy devices"; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 61b534bf22deb6c28367eeb9426bb408a5e9ded3..69c00e9873021a54313cacdca55a5c7f9f82872a 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -20,6 +20,8 @@ package android.hardware.camera2.params; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.utils.HashCodeHelpers; import android.hardware.camera2.utils.SurfaceUtils; @@ -94,6 +96,21 @@ public final class OutputConfiguration implements Parcelable { this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0); } + /** + * Unknown surface source type. + */ + private final int SURFACE_TYPE_UNKNOWN = -1; + + /** + * The surface is obtained from {@link android.view.SurfaceView}. + */ + private final int SURFACE_TYPE_SURFACE_VIEW = 0; + + /** + * The surface is obtained from {@link android.graphics.SurfaceTexture}. + */ + private final int SURFACE_TYPE_SURFACE_TEXTURE = 1; + /** * Create a new {@link OutputConfiguration} instance with a {@link Surface}, * with a surface group ID. @@ -179,12 +196,110 @@ public final class OutputConfiguration implements Parcelable { checkNotNull(surface, "Surface must not be null"); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); mSurfaceGroupId = surfaceGroupId; + mSurfaceType = SURFACE_TYPE_UNKNOWN; mSurface = surface; mRotation = rotation; mConfiguredSize = SurfaceUtils.getSurfaceSize(surface); mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface); mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface); mConfiguredGenerationId = surface.getGenerationId(); + mIsDeferredConfig = false; + } + + /** + * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface + * source class. + *

+ * This constructor takes an argument for desired Surface size and the Surface source class + * without providing the actual output Surface. This is used to setup a output configuration + * with a deferred Surface. The application can use this output configuration to create a + * session. + *

+ *

+ * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the + * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration} + * before submitting a request with this Surface target. The deferred Surface can only be + * obtained from either from {@link android.view.SurfaceView} by calling + * {@link android.view.SurfaceHolder#getSurface}, or from + * {@link android.graphics.SurfaceTexture} via + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). + *

+ * + * @param surfaceSize Size for the deferred surface. + * @param klass a non-{@code null} {@link Class} object reference that indicates the source of + * this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and + * {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported. + * @hide + */ + public OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class klass) { + checkNotNull(klass, "surfaceSize must not be null"); + checkNotNull(klass, "klass must not be null"); + if (klass == android.view.SurfaceHolder.class) { + mSurfaceType = SURFACE_TYPE_SURFACE_VIEW; + } else if (klass == android.graphics.SurfaceTexture.class) { + mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE; + } else { + mSurfaceType = SURFACE_TYPE_UNKNOWN; + throw new IllegalArgumentException("Unknow surface source class type"); + } + + mSurfaceGroupId = SURFACE_GROUP_ID_NONE; + mSurface = null; + mRotation = ROTATION_0; + mConfiguredSize = surfaceSize; + mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE); + mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); + mConfiguredGenerationId = 0; + mIsDeferredConfig = true; + } + + /** + * Check if this configuration has deferred configuration. + * + *

This will return true if the output configuration was constructed with surface deferred. + * It will return true even after the deferred surface is set later.

+ * + * @return true if this configuration has deferred surface. + * @hide + */ + public boolean isDeferredConfiguration() { + return mIsDeferredConfig; + } + + /** + * Set the deferred surface to this OutputConfiguration. + * + *

+ * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by + * calling {@link android.view.SurfaceHolder#getSurface}, or from + * {@link android.graphics.SurfaceTexture} via + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred + * surface is set, the application must finish the deferred surface configuration via + * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with + * this surface target. + *

+ * + * @param surface The deferred surface to be set. + * @throws IllegalArgumentException if the Surface is invalid. + * @throws IllegalStateException if a Surface was already set to this deferred + * OutputConfiguration. + * @hide + */ + public void setDeferredSurface(@NonNull Surface surface) { + checkNotNull(surface, "Surface must not be null"); + if (mSurface != null) { + throw new IllegalStateException("Deferred surface is already set!"); + } + + // This will throw IAE is the surface was abandoned. + Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); + if (!surfaceSize.equals(mConfiguredSize)) { + Log.w(TAG, "Deferred surface size " + surfaceSize + + " is different with pre-configured size " + mConfiguredSize + + ", the pre-configured size will be used."); + } + + mSurface = surface; } /** @@ -203,10 +318,12 @@ public final class OutputConfiguration implements Parcelable { this.mSurface = other.mSurface; this.mRotation = other.mRotation; this.mSurfaceGroupId = other.mSurfaceGroupId; + this.mSurfaceType = other.mSurfaceType; this.mConfiguredDataspace = other.mConfiguredDataspace; this.mConfiguredFormat = other.mConfiguredFormat; this.mConfiguredSize = other.mConfiguredSize; this.mConfiguredGenerationId = other.mConfiguredGenerationId; + this.mIsDeferredConfig = other.mIsDeferredConfig; } /** @@ -215,16 +332,30 @@ public final class OutputConfiguration implements Parcelable { private OutputConfiguration(@NonNull Parcel source) { int rotation = source.readInt(); int surfaceSetId = source.readInt(); + int surfaceType = source.readInt(); + int width = source.readInt(); + int height = source.readInt(); Surface surface = Surface.CREATOR.createFromParcel(source); - checkNotNull(surface, "Surface must not be null"); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); mSurfaceGroupId = surfaceSetId; mSurface = surface; mRotation = rotation; - mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface); - mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface); - mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface); - mConfiguredGenerationId = mSurface.getGenerationId(); + if (surface != null) { + mSurfaceType = SURFACE_TYPE_UNKNOWN; + mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface); + mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface); + mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface); + mConfiguredGenerationId = mSurface.getGenerationId(); + mIsDeferredConfig = true; + } else { + mSurfaceType = surfaceType; + mConfiguredSize = new Size(width, height); + mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE); + mConfiguredGenerationId = 0; + mConfiguredDataspace = + StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); + mIsDeferredConfig = false; + } } /** @@ -291,7 +422,12 @@ public final class OutputConfiguration implements Parcelable { } dest.writeInt(mRotation); dest.writeInt(mSurfaceGroupId); - mSurface.writeToParcel(dest, flags); + dest.writeInt(mSurfaceType); + dest.writeInt(mConfiguredSize.getWidth()); + dest.writeInt(mConfiguredSize.getHeight()); + if (mSurface != null) { + mSurface.writeToParcel(dest, flags); + } } /** @@ -311,13 +447,20 @@ public final class OutputConfiguration implements Parcelable { return true; } else if (obj instanceof OutputConfiguration) { final OutputConfiguration other = (OutputConfiguration) obj; + boolean iSSurfaceEqual = mSurface == other.mSurface && + mConfiguredGenerationId == other.mConfiguredGenerationId ; + if (mIsDeferredConfig) { + Log.i(TAG, "deferred config has the same surface"); + iSSurfaceEqual = true; + } return mRotation == other.mRotation && - mSurface == other.mSurface && - mConfiguredGenerationId == other.mConfiguredGenerationId && + iSSurfaceEqual&& mConfiguredSize.equals(other.mConfiguredSize) && mConfiguredFormat == other.mConfiguredFormat && mConfiguredDataspace == other.mConfiguredDataspace && - mSurfaceGroupId == other.mSurfaceGroupId; + mSurfaceGroupId == other.mSurfaceGroupId && + mSurfaceType == other.mSurfaceType && + mIsDeferredConfig == other.mIsDeferredConfig; } return false; } @@ -327,15 +470,26 @@ public final class OutputConfiguration implements Parcelable { */ @Override public int hashCode() { + // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise + // The deferred output configuration will be lost in the camera streammap after the deferred + // surface is set. + if (mIsDeferredConfig) { + return HashCodeHelpers.hashCode( + mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, + mSurfaceGroupId, mSurfaceType); + } + return HashCodeHelpers.hashCode( mRotation, mSurface.hashCode(), mConfiguredGenerationId, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId); } private static final String TAG = "OutputConfiguration"; - private final Surface mSurface; + private Surface mSurface; private final int mRotation; - private int mSurfaceGroupId; + private final int mSurfaceGroupId; + // Surface source type, this is only used by the deferred surface configuration objects. + private final int mSurfaceType; // The size, format, and dataspace of the surface when OutputConfiguration is created. private final Size mConfiguredSize; @@ -343,4 +497,6 @@ public final class OutputConfiguration implements Parcelable { private final int mConfiguredDataspace; // Surface generation ID to distinguish changes to Surface native internals private final int mConfiguredGenerationId; + // Flag indicating if this config has deferred surface. + private final boolean mIsDeferredConfig; } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 93da3e5af4e941f1595c3b1e44c2dfde8d6f16a5..826eb74aef65f54e2bb452b8f3688b2fb627ec65 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -354,9 +354,9 @@ public final class DisplayManagerGlobal { } } - public void requestColorTransform(int displayId, int colorTransformId) { + public void requestColorMode(int displayId, int colorMode) { try { - mDm.requestColorTransform(displayId, colorTransformId); + mDm.requestColorMode(displayId, colorMode); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 8a1abf18602ada2de4e21fadd5afdc1cf66d3962..f696c8d154463f00e65ba3e9e39221885974798a 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -59,8 +59,8 @@ interface IDisplayManager { // No permissions required. WifiDisplayStatus getWifiDisplayStatus(); - // Requires CONFIGURE_DISPLAY_COLOR_TRANSFORM - void requestColorTransform(int displayId, int colorTransformId); + // Requires CONFIGURE_DISPLAY_COLOR_MODE + void requestColorMode(int displayId, int colorMode); // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate // MediaProjection token for certain combinations of flags. diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java index 10fc8e6e366ab4e1f2bdd5fadb9cb71fc8189236..01a540484f1a94a1e9be6de16a93dff5ccea437f 100644 --- a/core/java/android/hardware/input/InputManagerInternal.java +++ b/core/java/android/hardware/input/InputManagerInternal.java @@ -59,4 +59,9 @@ public abstract class InputManagerInternal { * @param deviceId The id of input device. */ public abstract void toggleCapsLock(int deviceId); + + /** + * Set whether the input stack should deliver pulse gesture events when the device is asleep. + */ + public abstract void setPulseGestureEnabled(boolean enabled); } diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java index 43e596fe5566ba525f8e8f69bb2190d1d37d08fb..062c9580c1e42160c33c928d3627a404e720600c 100644 --- a/core/java/android/hardware/location/ContextHubService.java +++ b/core/java/android/hardware/location/ContextHubService.java @@ -16,6 +16,11 @@ package android.hardware.location; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; + import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; @@ -53,10 +58,14 @@ public class ContextHubService extends IContextHubService.Stub { private static final int PRE_LOADED_APP_MEM_REQ = 0; private static final int MSG_HEADER_SIZE = 4; - private static final int MSG_FIELD_TYPE = 0; - private static final int MSG_FIELD_VERSION = 1; - private static final int MSG_FIELD_HUB_HANDLE = 2; - private static final int MSG_FIELD_APP_INSTANCE = 3; + private static final int HEADER_FIELD_MSG_TYPE = 0; + private static final int HEADER_FIELD_MSG_VERSION = 1; + private static final int HEADER_FIELD_HUB_HANDLE = 2; + private static final int HEADER_FIELD_APP_INSTANCE = 3; + + private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE; + private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1; + private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2; private static final int OS_APP_INSTANCE = -1; @@ -146,15 +155,23 @@ public class ContextHubService extends IContextHubService.Stub { return -1; } - int[] msgHeader = new int[MSG_HEADER_SIZE]; - msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle; - msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; - msgHeader[MSG_FIELD_VERSION] = 0; - msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP; + int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE]; + msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle; + msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; + msgHeader[HEADER_FIELD_MSG_VERSION] = 0; + msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP; + + long appId = app.getAppId(); - if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) { + msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF); + msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF); + + int errVal = nativeSendMessage(msgHeader, app.getAppBinary()); + if (errVal != 0) { + Log.e(TAG, "Send Message returns error" + contextHubHandle); return -1; } + // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app return 0; } @@ -169,12 +186,14 @@ public class ContextHubService extends IContextHubService.Stub { // Call Native interface here int[] msgHeader = new int[MSG_HEADER_SIZE]; - msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB; - msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; - msgHeader[MSG_FIELD_VERSION] = 0; - msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP; + msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB; + msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle; + msgHeader[HEADER_FIELD_MSG_VERSION] = 0; + msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP; - if (nativeSendMessage(msgHeader, null) != 0) { + byte msg[] = new byte[0]; + + if (nativeSendMessage(msgHeader, msg) != 0) { return -1; } @@ -222,10 +241,10 @@ public class ContextHubService extends IContextHubService.Stub { checkPermissions(); int[] msgHeader = new int[MSG_HEADER_SIZE]; - msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle; - msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle; - msgHeader[MSG_FIELD_VERSION] = msg.getVersion(); - msgHeader[MSG_FIELD_TYPE] = msg.getMsgType(); + msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle; + msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle; + msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion(); + msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType(); return nativeSendMessage(msgHeader, msg.getData()); } @@ -269,15 +288,17 @@ public class ContextHubService extends IContextHubService.Stub { Log.v(TAG, "No message callbacks registered."); return 0; } - ContextHubMessage message = - new ContextHubMessage(header[MSG_FIELD_TYPE], header[MSG_FIELD_VERSION], data); + + ContextHubMessage msg = new ContextHubMessage(header[HEADER_FIELD_MSG_TYPE], + header[HEADER_FIELD_MSG_VERSION], + data); for (int i = 0; i < callbacksCount; ++i) { IContextHubCallback callback = mCallbacksList.getBroadcastItem(i); try { callback.onMessageReceipt( - header[MSG_FIELD_HUB_HANDLE], - header[MSG_FIELD_APP_INSTANCE], - message); + header[HEADER_FIELD_HUB_HANDLE], + header[HEADER_FIELD_APP_INSTANCE], + msg); } catch (RemoteException e) { Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ")."); continue; @@ -308,12 +329,20 @@ public class ContextHubService extends IContextHubService.Stub { return 0; } + private int deleteAppInstance(int appInstanceHandle) { + if (mNanoAppHash.remove(appInstanceHandle) == null) { + return -1; + } + + return 0; + } + private void sendVrStateChangeMessageToApp(NanoAppInstanceInfo app, boolean vrModeEnabled) { int[] msgHeader = new int[MSG_HEADER_SIZE]; - msgHeader[MSG_FIELD_TYPE] = 0; - msgHeader[MSG_FIELD_VERSION] = 0; - msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB; - msgHeader[MSG_FIELD_APP_INSTANCE] = app.getHandle(); + msgHeader[HEADER_FIELD_MSG_TYPE] = 0; + msgHeader[HEADER_FIELD_MSG_VERSION] = 0; + msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB; + msgHeader[HEADER_FIELD_APP_INSTANCE] = app.getHandle(); byte[] data = new byte[1]; data[0] = (byte) ((vrModeEnabled) ? 1 : 0); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index f9a7d192b5bb394435f7b86fe38ee50b1366168c..629db06a30e37dd5e10c66a92632ef7e251394e0 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -21,6 +21,7 @@ import com.android.internal.util.Preconditions; import android.app.PendingIntent; import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -42,7 +43,7 @@ import java.util.HashMap; *
*

Developer Guides

*

For more information about communicating with USB hardware, read the - * USB developer guide.

+ * USB developer guide.

*
*/ public class UsbManager { @@ -475,6 +476,26 @@ public class UsbManager { } } + /** + * Grants permission to specified package for USB device without showing system dialog. + * Only system components can call this function, as it requires the MANAGE_USB permission. + * @param device to request permissions for + * @param packageName of package to grant permissions + * + * {@hide} + */ + public void grantPermission(UsbDevice device, String packageName) { + try { + int uid = mContext.getPackageManager() + .getPackageUidAsUser(packageName, mContext.getUserId()); + mService.grantDevicePermission(device, uid); + } catch (NameNotFoundException e) { + Log.e(TAG, "Package " + packageName + " not found.", e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Returns true if the specified USB function is currently enabled when in device mode. *

diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 3531926d347ca313145ede6e15031b1ecf4ea8f2..29177b6b47cf81a7aeb122c69bb2ea2b721e96e7 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -16,11 +16,14 @@ package android.inputmethodservice; +import android.annotation.NonNull; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; @@ -208,7 +211,7 @@ public abstract class AbstractInputMethodService extends Service * * @param event The motion event being received. * @return True if the event was handled in this function, false otherwise. - * @see View#onTrackballEvent + * @see android.view.View#onTrackballEvent(MotionEvent) */ public boolean onTrackballEvent(MotionEvent event) { return false; @@ -219,9 +222,30 @@ public abstract class AbstractInputMethodService extends Service * * @param event The motion event being received. * @return True if the event was handled in this function, false otherwise. - * @see View#onGenericMotionEvent + * @see android.view.View#onGenericMotionEvent(MotionEvent) */ public boolean onGenericMotionEvent(MotionEvent event) { return false; } + + /** + * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access + * permission to the content. + * + *

Default implementation does nothing.

+ * + * @param inputContentInfo Content to be temporarily exposed from the input method to the + * application. + * This cannot be {@code null}. + * @param inputConnection {@link InputConnection} with which + * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be + * called. + * @return {@code false} if we cannot allow a temporary access permission. + * @hide + */ + public void exposeContent(@NonNull InputContentInfo inputContentInfo, + @NonNull InputConnection inputConnection) { + return; + } + } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index cc71a9c24bfbcc82f7b674018c1d89cc8af2179a..167d5a09a2dc42e3c37d920c3daec8d4f069c51f 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -168,7 +168,7 @@ class IInputMethodWrapper extends IInputMethod.Stub int missingMethods = msg.arg1; IInputContext inputContext = (IInputContext)args.arg1; InputConnection ic = inputContext != null - ? new InputConnectionWrapper(inputContext, missingMethods) : null; + ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; EditorInfo info = (EditorInfo)args.arg2; info.makeCompatible(mTargetSdkVersion); inputMethod.startInput(ic, info); @@ -180,7 +180,7 @@ class IInputMethodWrapper extends IInputMethod.Stub int missingMethods = msg.arg1; IInputContext inputContext = (IInputContext)args.arg1; InputConnection ic = inputContext != null - ? new InputConnectionWrapper(inputContext, missingMethods) : null; + ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; EditorInfo info = (EditorInfo)args.arg2; info.makeCompatible(mTargetSdkVersion); inputMethod.restartInput(ic, info); @@ -251,7 +251,7 @@ class IInputMethodWrapper extends IInputMethod.Stub public void bindInput(InputBinding binding) { // This IInputContext is guaranteed to implement all the methods. final int missingMethodFlags = 0; - InputConnection ic = new InputConnectionWrapper( + InputConnection ic = new InputConnectionWrapper(mTarget, IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags); InputBinding nu = new InputBinding(ic, binding); mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 963a21b1faf054202721b625c107b7ce0ddc589d..353eb885fa6aa4c6bfc735ef63510f3e108d3b60 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -23,6 +23,7 @@ import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.MainThread; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.Dialog; import android.content.Context; @@ -65,6 +66,7 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; @@ -2631,6 +2633,29 @@ public class InputMethodService extends AbstractInputMethodService { return mImm.getInputMethodWindowVisibleHeight(); } + /** + * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access + * permission to the content. + * + * @param inputContentInfo Content to be temporarily exposed from the input method to the + * application. + * This cannot be {@code null}. + * @param inputConnection {@link InputConnection} with which + * {@link InputConnection#commitContent(InputContentInfo, Bundle)} will be called. + * @hide + */ + @Override + public final void exposeContent(@NonNull InputContentInfo inputContentInfo, + @NonNull InputConnection inputConnection) { + if (inputConnection == null) { + return; + } + if (getCurrentInputConnection() != inputConnection) { + return; + } + mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo()); + } + /** * Performs a dump of the InputMethodService's internal state. Override * to add your own information to the dump. diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index a45e6f51d8030e7fa5fcb1415d66e74a7ef861f3..b9e9b283106f98dd6c2dfe27538551bbe0b734bc 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -342,6 +342,15 @@ public class ConnectivityManager { */ public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED"; + /** + * Action used to display a dialog that asks the user whether to avoid a network that is no + * longer validated. This intent is used to start the dialog in settings via startActivity. + * + * @hide + */ + public static final String ACTION_PROMPT_LOST_VALIDATION = + "android.net.conn.PROMPT_LOST_VALIDATION"; + /** * Invalid tethering type. * @see #startTethering(int, OnStartTetheringCallback, boolean) @@ -1034,6 +1043,26 @@ public class ConnectivityManager { } } + /** + * Request that this callback be invoked at ConnectivityService's earliest + * convenience with the current satisfying network's LinkProperties. + * If no such network exists no callback invocation is performed. + * + * The callback must have been registered with #requestNetwork() or + * #registerDefaultNetworkCallback(); callbacks registered with + * registerNetworkCallback() are not specific to any particular Network so + * do not cause any updates. + * + * @hide + */ + public void requestLinkProperties(NetworkCallback networkCallback) { + try { + mService.requestLinkProperties(networkCallback.networkRequest); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This * will return {@code null} if the network is unknown. @@ -1051,6 +1080,26 @@ public class ConnectivityManager { } } + /** + * Request that this callback be invoked at ConnectivityService's earliest + * convenience with the current satisfying network's NetworkCapabilities. + * If no such network exists no callback invocation is performed. + * + * The callback must have been registered with #requestNetwork() or + * #registerDefaultNetworkCallback(); callbacks registered with + * registerNetworkCallback() are not specific to any particular Network so + * do not cause any updates. + * + * @hide + */ + public void requestNetworkCapabilities(NetworkCallback networkCallback) { + try { + mService.requestNetworkCapabilities(networkCallback.networkRequest); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Gets the URL that should be used for resolving whether a captive portal is present. * 1. This URL should respond with a 204 response to a GET request to indicate no captive @@ -3103,14 +3152,11 @@ public class ConnectivityManager { throw new IllegalArgumentException("Invalid NetworkCallback"); } try { + // CallbackHandler will release callback when receiving CALLBACK_RELEASED. mService.releaseNetworkRequest(networkCallback.networkRequest); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - - synchronized (sNetworkCallback) { - sNetworkCallback.remove(networkCallback.networkRequest); - } } /** @@ -3151,6 +3197,27 @@ public class ConnectivityManager { } } + /** + * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is + * only meaningful if the system is configured not to penalize such networks, e.g., if the + * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code + * NETWORK_AVOID_BAD_WIFI setting is unset}. + * + *

This method requires the caller to hold the permission + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} + * + * @param network The network to accept. + * + * @hide + */ + public void setAvoidUnvalidated(Network network) { + try { + mService.setAvoidUnvalidated(network); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Resets all connectivity manager settings back to factory defaults. * @hide diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java index d8cdde94a2f189bef8fef99c9c1ee27cfd20c6c1..9a2d4e0a31242a01bf3b0a0f014d65eec2cd9cda 100644 --- a/core/java/android/net/ConnectivityMetricsLogger.java +++ b/core/java/android/net/ConnectivityMetricsLogger.java @@ -23,6 +23,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + /** {@hide} */ @SystemApi public class ConnectivityMetricsLogger { @@ -33,28 +35,58 @@ public class ConnectivityMetricsLogger { // Component Tags public static final int COMPONENT_TAG_CONNECTIVITY = 0; - public static final int COMPONENT_TAG_BLUETOOTH = 1; - public static final int COMPONENT_TAG_WIFI = 2; - public static final int COMPONENT_TAG_TELECOM = 3; - public static final int COMPONENT_TAG_TELEPHONY = 4; - - public static final int NUMBER_OF_COMPONENTS = 5; + public static final int COMPONENT_TAG_BLUETOOTH = 1; + public static final int COMPONENT_TAG_WIFI = 2; + public static final int COMPONENT_TAG_TELECOM = 3; + public static final int COMPONENT_TAG_TELEPHONY = 4; + public static final int NUMBER_OF_COMPONENTS = 5; // Event Tag public static final int TAG_SKIPPED_EVENTS = -1; public static final String DATA_KEY_EVENTS_COUNT = "count"; - private IConnectivityMetricsLogger mService; - - private long mServiceUnblockedTimestampMillis = 0; - private int mNumSkippedEvents = 0; + /** {@hide} */ protected IConnectivityMetricsLogger mService; + /** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis; + private int mNumSkippedEvents; public ConnectivityMetricsLogger() { - mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService( - CONNECTIVITY_METRICS_LOGGER_SERVICE)); + // TODO: consider not initializing mService in constructor + this(IConnectivityMetricsLogger.Stub.asInterface( + ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE))); + } + + /** {@hide} */ + @VisibleForTesting + public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) { + mService = service; } + /** {@hide} */ + protected boolean checkLoggerService() { + if (mService != null) { + return true; + } + // Two threads racing here will write the same pointer because getService + // is idempotent once MetricsLoggerService is initialized. + mService = IConnectivityMetricsLogger.Stub.asInterface( + ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)); + return mService != null; + } + + /** + * Log a ConnectivityMetricsEvent. + * + * This method keeps track of skipped events when MetricsLoggerService throttles input events. + * It skips logging when MetricsLoggerService is active. When throttling ends, it logs a + * meta-event containing the number of events dropped. It is not safe to call this method + * concurrently from different threads. + * + * @param timestamp is the epoch timestamp of the event in ms. + * @param componentTag is the COMPONENT_* constant the event belongs to. + * @param eventTag is an event type constant whose meaning is specific to the component tag. + * @param data is a Parcelable instance representing the event. + */ public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) { if (mService == null) { if (DBG) { @@ -77,6 +109,12 @@ public class ConnectivityMetricsLogger { // Log number of skipped events Bundle b = new Bundle(); b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents); + + // Log the skipped event. + // TODO: Note that some of the clients push all states events into the server, + // If we lose some states logged here, we might mess up the statistics happened at the + // backend. One of the options is to introduce a non-skippable flag for important events + // that are logged. skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis, componentTag, TAG_SKIPPED_EVENTS, b); @@ -104,7 +142,7 @@ public class ConnectivityMetricsLogger { } } } catch (RemoteException e) { - Log.e(TAG, "Error logging event " + e.getMessage()); + Log.e(TAG, "Error logging event", e); } } @@ -121,8 +159,8 @@ public class ConnectivityMetricsLogger { public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) { try { return mService.getEvents(reference); - } catch (RemoteException ex) { - Log.e(TAG, "IConnectivityMetricsLogger.getEvents: " + ex); + } catch (RemoteException e) { + Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e); return null; } } @@ -133,8 +171,8 @@ public class ConnectivityMetricsLogger { public boolean register(PendingIntent newEventsIntent) { try { return mService.register(newEventsIntent); - } catch (RemoteException ex) { - Log.e(TAG, "IConnectivityMetricsLogger.register: " + ex); + } catch (RemoteException e) { + Log.e(TAG, "IConnectivityMetricsLogger.register", e); return false; } } @@ -142,11 +180,10 @@ public class ConnectivityMetricsLogger { public boolean unregister(PendingIntent newEventsIntent) { try { mService.unregister(newEventsIntent); - } catch (RemoteException ex) { - Log.e(TAG, "IConnectivityMetricsLogger.unregister: " + ex); + return true; + } catch (RemoteException e) { + Log.e(TAG, "IConnectivityMetricsLogger.unregister", e); return false; } - - return true; } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 0d518cc14049a51d2fefa325ce2977d0424739a2..4aabda9eb09d217df491518f53e645faad277b6c 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -156,9 +156,12 @@ interface IConnectivityManager void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, in PendingIntent operation); + void requestLinkProperties(in NetworkRequest networkRequest); + void requestNetworkCapabilities(in NetworkRequest networkRequest); void releaseNetworkRequest(in NetworkRequest networkRequest); void setAcceptUnvalidated(in Network network, boolean accept, boolean always); + void setAvoidUnvalidated(in Network network); int getRestoreDefaultNetworkDelay(int networkType); diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 384ab1c8f50cc97208fc5eedf4b21e2b44265645..6e74f14bd138a9944d3ce5af1b9b12bf70e122cb 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -103,7 +103,7 @@ public class LinkAddress implements Parcelable { private boolean isIPv6ULA() { if (address != null && address instanceof Inet6Address) { byte[] bytes = address.getAddress(); - return ((bytes[0] & (byte)0xfc) == (byte)0xfc); + return ((bytes[0] & (byte)0xfe) == (byte)0xfc); } return false; } diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java index 5511a248b6aa9d47921847fc2bc085099b3709f5..69f50a272af0c8aa5c28d5b569aba87e8199c89f 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -51,6 +51,15 @@ public class NetworkMisc implements Parcelable { */ public boolean acceptUnvalidated; + /** + * Set to avoid surfacing the "Sign in to network" notification. + * if carrier receivers/apps are registered to handle the carrier-specific provisioning + * procedure, a carrier specific provisioning notification will be placed. + * only one notification should be displayed. This field is set based on + * which notification should be used for provisioning. + */ + public boolean provisioningNotificationDisabled; + /** * For mobile networks, this is the subscriber ID (such as IMSI). */ @@ -65,6 +74,7 @@ public class NetworkMisc implements Parcelable { explicitlySelected = nm.explicitlySelected; acceptUnvalidated = nm.acceptUnvalidated; subscriberId = nm.subscriberId; + provisioningNotificationDisabled = nm.provisioningNotificationDisabled; } } @@ -79,6 +89,7 @@ public class NetworkMisc implements Parcelable { out.writeInt(explicitlySelected ? 1 : 0); out.writeInt(acceptUnvalidated ? 1 : 0); out.writeString(subscriberId); + out.writeInt(provisioningNotificationDisabled ? 1 : 0); } public static final Creator CREATOR = new Creator() { @@ -89,6 +100,7 @@ public class NetworkMisc implements Parcelable { networkMisc.explicitlySelected = in.readInt() != 0; networkMisc.acceptUnvalidated = in.readInt() != 0; networkMisc.subscriberId = in.readString(); + networkMisc.provisioningNotificationDisabled = in.readInt() != 0; return networkMisc; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 51c45e056092ae343683129ee925e74d9acb3dda..11b861aef5aa3898843f2d193ebb20965389aee3 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -18,7 +18,6 @@ package android.net; import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.net.NetworkPolicy.CYCLE_NONE; -import static android.text.format.Time.MONTH_DAY; import android.content.Context; import android.content.Intent; @@ -27,12 +26,13 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.os.RemoteException; import android.os.UserHandle; -import android.text.format.Time; import android.util.DebugUtils; import com.google.android.collect.Sets; +import java.util.Calendar; import java.util.HashSet; +import java.util.TimeZone; /** * Manager for creating and modifying network policy rules. @@ -253,28 +253,18 @@ public class NetworkPolicyManager { throw new IllegalArgumentException("Unable to compute boundary without cycleDay"); } - final Time now = new Time(policy.cycleTimezone); - now.set(currentTime); - - // first, find cycle boundary for current month - final Time cycle = new Time(now); - cycle.hour = cycle.minute = cycle.second = 0; - snapToCycleDay(cycle, policy.cycleDay); - - if (Time.compare(cycle, now) >= 0) { - // cycle boundary is beyond now, use last cycle boundary; start by - // pushing ourselves squarely into last month. - final Time lastMonth = new Time(now); - lastMonth.hour = lastMonth.minute = lastMonth.second = 0; - lastMonth.monthDay = 1; - lastMonth.month -= 1; - lastMonth.normalize(true); - - cycle.set(lastMonth); - snapToCycleDay(cycle, policy.cycleDay); + final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone)); + cal.setTimeInMillis(currentTime); + snapToCycleDay(cal, policy.cycleDay); + + if (cal.getTimeInMillis() >= currentTime) { + // Cycle boundary is beyond now, use last cycle boundary + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.add(Calendar.MONTH, -1); + snapToCycleDay(cal, policy.cycleDay); } - return cycle.toMillis(true); + return cal.getTimeInMillis(); } /** {@hide} */ @@ -283,28 +273,18 @@ public class NetworkPolicyManager { throw new IllegalArgumentException("Unable to compute boundary without cycleDay"); } - final Time now = new Time(policy.cycleTimezone); - now.set(currentTime); - - // first, find cycle boundary for current month - final Time cycle = new Time(now); - cycle.hour = cycle.minute = cycle.second = 0; - snapToCycleDay(cycle, policy.cycleDay); - - if (Time.compare(cycle, now) <= 0) { - // cycle boundary is before now, use next cycle boundary; start by - // pushing ourselves squarely into next month. - final Time nextMonth = new Time(now); - nextMonth.hour = nextMonth.minute = nextMonth.second = 0; - nextMonth.monthDay = 1; - nextMonth.month += 1; - nextMonth.normalize(true); - - cycle.set(nextMonth); - snapToCycleDay(cycle, policy.cycleDay); + final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone)); + cal.setTimeInMillis(currentTime); + snapToCycleDay(cal, policy.cycleDay); + + if (cal.getTimeInMillis() <= currentTime) { + // Cycle boundary is before now, use next cycle boundary + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.add(Calendar.MONTH, 1); + snapToCycleDay(cal, policy.cycleDay); } - return cycle.toMillis(true); + return cal.getTimeInMillis(); } /** @@ -313,16 +293,17 @@ public class NetworkPolicyManager { * * @hide */ - public static void snapToCycleDay(Time time, int cycleDay) { - if (cycleDay > time.getActualMaximum(MONTH_DAY)) { - // cycle day isn't valid this month; snap to last second of month - time.month += 1; - time.monthDay = 1; - time.second = -1; + public static void snapToCycleDay(Calendar cal, int cycleDay) { + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) { + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.add(Calendar.MONTH, 1); + cal.add(Calendar.SECOND, -1); } else { - time.monthDay = cycleDay; + cal.set(Calendar.DAY_OF_MONTH, cycleDay); } - time.normalize(true); } /** diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index f1edcbe30df65305749051d9f2ef4d12c3b22e76..4501f7b089b96c1ccb62c3da438076ae180ebce7 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -19,6 +19,8 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * Defines a request for a network, made through {@link NetworkRequest.Builder} and used * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes @@ -47,15 +49,55 @@ public class NetworkRequest implements Parcelable { public final int legacyType; /** + * A NetworkRequest as used by the system can be one of three types: + * + * - LISTEN, for which the framework will issue callbacks about any + * and all networks that match the specified NetworkCapabilities, + * + * - REQUEST, capable of causing a specific network to be created + * first (e.g. a telephony DUN request), the framework will issue + * callbacks about the single, highest scoring current network + * (if any) that matches the specified NetworkCapabilities, or + * + * - TRACK_DEFAULT, a hybrid of the two designed such that the + * framework will issue callbacks for the single, highest scoring + * current network (if any) that matches the capabilities of the + * default Internet request (mDefaultRequest), but which cannot cause + * the framework to either create or retain the existence of any + * specific network. + * + * - The value NONE is used only by applications. When an application + * creates a NetworkRequest, it does not have a type; the type is set + * by the system depending on the method used to file the request + * (requestNetwork, registerNetworkCallback, etc.). + * + * @hide + */ + public static enum Type { + NONE, + LISTEN, + TRACK_DEFAULT, + REQUEST + }; + + /** + * The type of the request. This is only used by the system and is always NONE elsewhere. + * * @hide */ - public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) { + public final Type type; + + /** + * @hide + */ + public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId, Type type) { if (nc == null) { throw new NullPointerException(); } requestId = rId; networkCapabilities = nc; this.legacyType = legacyType; + this.type = type; } /** @@ -65,6 +107,7 @@ public class NetworkRequest implements Parcelable { networkCapabilities = new NetworkCapabilities(that.networkCapabilities); requestId = that.requestId; this.legacyType = that.legacyType; + this.type = that.type; } /** @@ -90,7 +133,7 @@ public class NetworkRequest implements Parcelable { final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities); nc.maybeMarkCapabilitiesRestricted(); return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE, - ConnectivityManager.REQUEST_ID_UNSET); + ConnectivityManager.REQUEST_ID_UNSET, Type.NONE); } /** @@ -223,6 +266,7 @@ public class NetworkRequest implements Parcelable { dest.writeParcelable(networkCapabilities, flags); dest.writeInt(legacyType); dest.writeInt(requestId); + dest.writeString(type.name()); } public static final Creator CREATOR = new Creator() { @@ -230,7 +274,8 @@ public class NetworkRequest implements Parcelable { NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); int legacyType = in.readInt(); int requestId = in.readInt(); - NetworkRequest result = new NetworkRequest(nc, legacyType, requestId); + Type type = Type.valueOf(in.readString()); // IllegalArgumentException if invalid. + NetworkRequest result = new NetworkRequest(nc, legacyType, requestId, type); return result; } public NetworkRequest[] newArray(int size) { @@ -238,8 +283,36 @@ public class NetworkRequest implements Parcelable { } }; + /** + * Returns true iff. the contained NetworkRequest is of type LISTEN. + * + * @hide + */ + public boolean isListen() { + return type == Type.LISTEN; + } + + /** + * Returns true iff. the contained NetworkRequest is one that: + * + * - should be associated with at most one satisfying network + * at a time; + * + * - should cause a network to be kept up if it is the best network + * which can satisfy the NetworkRequest. + * + * For full detail of how isRequest() is used for pairing Networks with + * NetworkRequests read rematchNetworkAndRequests(). + * + * @hide + */ + public boolean isRequest() { + return type == Type.TRACK_DEFAULT || type == Type.REQUEST; + } + public String toString() { - return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType + + return "NetworkRequest [ " + type + " id=" + requestId + + (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") + ", " + networkCapabilities.toString() + " ]"; } @@ -248,13 +321,11 @@ public class NetworkRequest implements Parcelable { NetworkRequest that = (NetworkRequest)obj; return (that.legacyType == this.legacyType && that.requestId == this.requestId && - ((that.networkCapabilities == null && this.networkCapabilities == null) || - (that.networkCapabilities != null && - that.networkCapabilities.equals(this.networkCapabilities)))); + that.type == this.type && + Objects.equals(that.networkCapabilities, this.networkCapabilities)); } public int hashCode() { - return requestId + (legacyType * 1013) + - (networkCapabilities.hashCode() * 1051); + return Objects.hash(requestId, legacyType, networkCapabilities, type); } } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 141af3d4956337e3bdcbed01f842bdd49a629827..35e3065b078f05cc418181bdfb47fba9784f2042 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -45,12 +45,19 @@ public class NetworkUtils { public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException; /** - * Attaches a socket filter that accepts ICMP6 router advertisement packets to the given socket. + * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket. * @param fd the socket's {@link FileDescriptor}. * @param packetType the hardware address type, one of ARPHRD_*. */ public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; + /** + * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. + * @param fd the socket's {@link FileDescriptor}. + * @param ifIndex the interface index. + */ + public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException; + /** * Binds the current process to the network designated by {@code netId}. All sockets created * in the future (and not explicitly bound via a bound {@link SocketFactory} (see diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..258d8e13951fadee7e731587f4361f559f913b97 --- /dev/null +++ b/core/java/android/net/metrics/ApfProgramEvent.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.SparseArray; + +import com.android.internal.util.MessageUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +/** + * An event logged when there is a change or event that requires updating the + * the APF program in place with a new APF program. + * {@hide} + */ +@SystemApi +public final class ApfProgramEvent implements Parcelable { + + // Bitflag constants describing what an Apf program filters. + // Bits are indexeds from LSB to MSB, starting at index 0. + public static final int FLAG_MULTICAST_FILTER_ON = 0; + public static final int FLAG_HAS_IPV4_ADDRESS = 1; + + /** {@hide} */ + @IntDef(flag = true, value = {FLAG_MULTICAST_FILTER_ON, FLAG_HAS_IPV4_ADDRESS}) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + public final long lifetime; // Lifetime of the program in seconds + public final int filteredRas; // Number of RAs filtered by the APF program + public final int currentRas; // Total number of current RAs at generation time + public final int programLength; // Length of the APF program in bytes + public final int flags; // Bitfield compound of FLAG_* constants + + /** {@hide} */ + public ApfProgramEvent( + long lifetime, int filteredRas, int currentRas, int programLength, @Flags int flags) { + this.lifetime = lifetime; + this.filteredRas = filteredRas; + this.currentRas = currentRas; + this.programLength = programLength; + this.flags = flags; + } + + private ApfProgramEvent(Parcel in) { + this.lifetime = in.readLong(); + this.filteredRas = in.readInt(); + this.currentRas = in.readInt(); + this.programLength = in.readInt(); + this.flags = in.readInt(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(lifetime); + out.writeInt(filteredRas); + out.writeInt(currentRas); + out.writeInt(programLength); + out.writeInt(flags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever"; + return String.format("ApfProgramEvent(%d/%d RAs %dB %s %s)", + filteredRas, currentRas, programLength, lifetimeString, namesOf(flags)); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public ApfProgramEvent createFromParcel(Parcel in) { + return new ApfProgramEvent(in); + } + + public ApfProgramEvent[] newArray(int size) { + return new ApfProgramEvent[size]; + } + }; + + /** {@hide} */ + public static @Flags int flagsFor(boolean hasIPv4, boolean multicastFilterOn) { + int bitfield = 0; + if (hasIPv4) { + bitfield |= (1 << FLAG_HAS_IPV4_ADDRESS); + } + if (multicastFilterOn) { + bitfield |= (1 << FLAG_MULTICAST_FILTER_ON); + } + return bitfield; + } + + private static String namesOf(@Flags int bitfield) { + List names = new ArrayList<>(Integer.bitCount(bitfield)); + BitSet set = BitSet.valueOf(new long[]{bitfield & Integer.MAX_VALUE}); + // Only iterate over flag bits which are set. + for (int bit = set.nextSetBit(0); bit >= 0; bit = set.nextSetBit(bit+1)) { + names.add(Decoder.constants.get(bit)); + } + return TextUtils.join("|", names); + } + + final static class Decoder { + static final SparseArray constants = + MessageUtils.findMessageNames( + new Class[]{ApfProgramEvent.class}, new String[]{"FLAG_"}); + } +} diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java new file mode 100644 index 0000000000000000000000000000000000000000..8451e539a7f677beb71c293a1c5d0d321c4fbbed --- /dev/null +++ b/core/java/android/net/metrics/ApfStats.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event logged for an interface with APF capabilities when its IpManager state machine exits. + * {@hide} + */ +@SystemApi +public final class ApfStats implements Parcelable { + + public final long durationMs; // time interval in milliseconds these stastistics covers + public final int receivedRas; // number of received RAs + public final int matchingRas; // number of received RAs matching a known RA + public final int droppedRas; // number of received RAs ignored due to the MAX_RAS limit + public final int zeroLifetimeRas; // number of received RAs with a minimum lifetime of 0 + public final int parseErrors; // number of received RAs that could not be parsed + public final int programUpdates; // number of APF program updates + public final int maxProgramSize; // maximum APF program size advertised by hardware + + /** {@hide} */ + public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas, + int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) { + this.durationMs = durationMs; + this.receivedRas = receivedRas; + this.matchingRas = matchingRas; + this.droppedRas = droppedRas; + this.zeroLifetimeRas = zeroLifetimeRas; + this.parseErrors = parseErrors; + this.programUpdates = programUpdates; + this.maxProgramSize = maxProgramSize; + } + + private ApfStats(Parcel in) { + this.durationMs = in.readLong(); + this.receivedRas = in.readInt(); + this.matchingRas = in.readInt(); + this.droppedRas = in.readInt(); + this.zeroLifetimeRas = in.readInt(); + this.parseErrors = in.readInt(); + this.programUpdates = in.readInt(); + this.maxProgramSize = in.readInt(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(durationMs); + out.writeInt(receivedRas); + out.writeInt(matchingRas); + out.writeInt(droppedRas); + out.writeInt(zeroLifetimeRas); + out.writeInt(parseErrors); + out.writeInt(programUpdates); + out.writeInt(maxProgramSize); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return new StringBuilder("ApfStats(") + .append(String.format("%dms ", durationMs)) + .append(String.format("%dB RA: {", maxProgramSize)) + .append(String.format("%d received, ", receivedRas)) + .append(String.format("%d matching, ", matchingRas)) + .append(String.format("%d dropped, ", droppedRas)) + .append(String.format("%d zero lifetime, ", zeroLifetimeRas)) + .append(String.format("%d parse errors, ", parseErrors)) + .append(String.format("%d program updates})", programUpdates)) + .toString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public ApfStats createFromParcel(Parcel in) { + return new ApfStats(in); + } + + public ApfStats[] newArray(int size) { + return new ApfStats[size]; + } + }; +} diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java index f8b59925cb9105b76610e107faf1672ca7b0dd27..9f0bad7ee9fe2d96d564f9462cac628425871d42 100644 --- a/core/java/android/net/metrics/DefaultNetworkEvent.java +++ b/core/java/android/net/metrics/DefaultNetworkEvent.java @@ -22,10 +22,11 @@ import android.os.Parcel; import android.os.Parcelable; /** + * An event recorded by ConnectivityService when there is a change in the default network. * {@hide} */ @SystemApi -public final class DefaultNetworkEvent extends IpConnectivityEvent implements Parcelable { +public final class DefaultNetworkEvent implements Parcelable { // The ID of the network that has become the new default or NETID_UNSET if none. public final int netId; // The list of transport types of the new default network, for example TRANSPORT_WIFI, as @@ -37,7 +38,8 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa public final boolean prevIPv4; public final boolean prevIPv6; - private DefaultNetworkEvent(int netId, int[] transportTypes, + /** {@hide} */ + public DefaultNetworkEvent(int netId, int[] transportTypes, int prevNetId, boolean prevIPv4, boolean prevIPv6) { this.netId = netId; this.transportTypes = transportTypes; @@ -54,6 +56,7 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa this.prevIPv6 = (in.readByte() > 0); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(netId); out.writeIntArray(transportTypes); @@ -62,6 +65,7 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa out.writeByte(prevIPv6 ? (byte) 1 : (byte) 0); } + @Override public int describeContents() { return 0; } @@ -105,6 +109,5 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa public static void logEvent( int netId, int[] transports, int prevNetId, boolean hadIPv4, boolean hadIPv6) { - logEvent(new DefaultNetworkEvent(netId, transports, prevNetId, hadIPv4, hadIPv6)); } -}; +} diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index ec560bf64ccc3d2ac250a39728d2798f8a00594c..4a9ff051124699380047033cde83df45295618e3 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -21,35 +21,50 @@ import android.os.Parcel; import android.os.Parcelable; /** + * An event recorded when a DhcpClient state machine transitions to a new state. * {@hide} */ @SystemApi -public final class DhcpClientEvent extends IpConnectivityEvent implements Parcelable { +public final class DhcpClientEvent implements Parcelable { + + // Names for recording DhcpClient pseudo-state transitions. + /** {@hide} Represents transitions from DhcpInitState to DhcpBoundState */ + public static final String INITIAL_BOUND = "InitialBoundState"; + /** {@hide} Represents transitions from and to DhcpBoundState via DhcpRenewingState */ + public static final String RENEWING_BOUND = "RenewingBoundState"; + public final String ifName; public final String msg; + public final int durationMs; - private DhcpClientEvent(String ifName, String msg) { + /** {@hide} */ + public DhcpClientEvent(String ifName, String msg, int durationMs) { this.ifName = ifName; this.msg = msg; + this.durationMs = durationMs; } private DhcpClientEvent(Parcel in) { this.ifName = in.readString(); this.msg = in.readString(); + this.durationMs = in.readInt(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(ifName); out.writeString(msg); + out.writeInt(durationMs); } + @Override public int describeContents() { return 0; } @Override public String toString() { - return String.format("DhcpClientEvent(%s, %s)", ifName, msg); + return String.format("DhcpClientEvent(%s, %s, %dms)", ifName, msg, durationMs); } public static final Parcelable.Creator CREATOR @@ -64,6 +79,5 @@ public final class DhcpClientEvent extends IpConnectivityEvent implements Parcel }; public static void logStateEvent(String ifName, String state) { - logEvent(new DhcpClientEvent(ifName, state)); } -}; +} diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java index 84795b89bb6b0369ef4b0c87514f2b14f7429fa1..59c5fb61b2116725cfd39277ceecc97a8847eb57 100644 --- a/core/java/android/net/metrics/DhcpErrorEvent.java +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -24,10 +24,11 @@ import android.util.SparseArray; import com.android.internal.util.MessageUtils; /** - * {@hide} Event class used to record error events when parsing DHCP response packets. + * Event class used to record error events when parsing DHCP response packets. + * {@hide} */ @SystemApi -public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcelable { +public final class DhcpErrorEvent implements Parcelable { public static final int L2_ERROR = 1; public static final int L3_ERROR = 2; public static final int L4_ERROR = 3; @@ -61,7 +62,8 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela // byte 3: optional code public final int errorCode; - private DhcpErrorEvent(String ifName, int errorCode) { + /** {@hide} */ + public DhcpErrorEvent(String ifName, int errorCode) { this.ifName = ifName; this.errorCode = errorCode; } @@ -71,11 +73,13 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela this.errorCode = in.readInt(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(ifName); out.writeInt(errorCode); } + @Override public int describeContents() { return 0; } @@ -92,11 +96,9 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela }; public static void logParseError(String ifName, int errorCode) { - logEvent(new DhcpErrorEvent(ifName, errorCode)); } public static void logReceiveError(String ifName) { - logEvent(new DhcpErrorEvent(ifName, RECEIVE_ERROR)); } public static int errorCodeWithOption(int errorCode, int option) { diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index b94dda079cd8efddabc235e05f7f9f4fc697c9df..4fc6b7aaf8425815d8a7db460812d048bd52a20f 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -21,10 +21,11 @@ import android.os.Parcel; import android.os.Parcelable; /** + * An event recorded by DnsEventListenerService. * {@hide} */ @SystemApi -final public class DnsEvent extends IpConnectivityEvent implements Parcelable { +final public class DnsEvent implements Parcelable { public final int netId; // The event type is currently only 1 or 2, so we store it as a byte. @@ -37,7 +38,8 @@ final public class DnsEvent extends IpConnectivityEvent implements Parcelable { // queries. public final int[] latenciesMs; - private DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { + /** {@hide} */ + public DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { this.netId = netId; this.eventTypes = eventTypes; this.returnCodes = returnCodes; @@ -59,6 +61,7 @@ final public class DnsEvent extends IpConnectivityEvent implements Parcelable { out.writeIntArray(latenciesMs); } + @Override public int describeContents() { return 0; } @@ -82,6 +85,5 @@ final public class DnsEvent extends IpConnectivityEvent implements Parcelable { public static void logEvent( int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { - logEvent(new DnsEvent(netId, eventTypes, returnCodes, latenciesMs)); } } diff --git a/core/java/android/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java deleted file mode 100644 index 95576c2853dde713fd6312ddd816a3a85ab5af76..0000000000000000000000000000000000000000 --- a/core/java/android/net/metrics/IpConnectivityEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.metrics; - -import android.net.ConnectivityMetricsLogger; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * {@hide} - */ -public abstract class IpConnectivityEvent { - private static final int COMPONENT_TAG = ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY; - - private static final ConnectivityMetricsLogger sMetricsLogger = new ConnectivityMetricsLogger(); - - public static void logEvent(T event) { - // TODO: consider using different component for DNS event. - sMetricsLogger.logEvent(System.currentTimeMillis(), COMPONENT_TAG, 0, event); - } -}; diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java new file mode 100644 index 0000000000000000000000000000000000000000..dd7bd1b915b852e84478620c5dab851c9bf66640 --- /dev/null +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.net.ConnectivityMetricsEvent; +import android.net.ConnectivityMetricsLogger; +import android.net.IConnectivityMetricsLogger; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Specialization of the ConnectivityMetricsLogger class for recording IP connectivity events. + * {@hide} + */ +public class IpConnectivityLog extends ConnectivityMetricsLogger { + private static String TAG = "IpConnectivityMetricsLogger"; + private static final boolean DBG = true; + + public IpConnectivityLog() { + // mService initialized in super constructor. + } + + @VisibleForTesting + public IpConnectivityLog(IConnectivityMetricsLogger service) { + super(service); + } + + /** + * Log an IpConnectivity event. Contrary to logEvent(), this method does not + * keep track of skipped events and is thread-safe for callers. + * + * @param timestamp is the epoch timestamp of the event in ms. + * @param data is a Parcelable instance representing the event. + * + * @return true if the event was successfully logged. + */ + public boolean log(long timestamp, Parcelable data) { + if (!checkLoggerService()) { + if (DBG) { + Log.d(TAG, CONNECTIVITY_METRICS_LOGGER_SERVICE + " service was not ready"); + } + return false; + } + + if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) { + if (DBG) { + Log.d(TAG, "skipping logging due to throttling for IpConnectivity component"); + } + return false; + } + + try { + final ConnectivityMetricsEvent event = + new ConnectivityMetricsEvent(timestamp, COMPONENT_TAG_CONNECTIVITY, 0, data); + final long result = mService.logEvent(event); + if (result >= 0) { + mServiceUnblockedTimestampMillis = result; + } + return (result == 0); + } catch (RemoteException e) { + Log.e(TAG, "Error logging event", e); + return false; + } + } + + public void log(Parcelable event) { + log(System.currentTimeMillis(), event); + } +} diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index 0940bd06c3d34587923dacbeb093b7c6b9b0e7d7..a5b4eb5aff86f7920c3e28bb5de7b770e5317333 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -16,6 +16,7 @@ package android.net.metrics; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,21 +24,32 @@ import android.util.SparseArray; import com.android.internal.util.MessageUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** + * An event recorded by IpManager when IP provisioning completes for a network or + * when a network disconnects. * {@hide} */ @SystemApi -public final class IpManagerEvent extends IpConnectivityEvent implements Parcelable { +public final class IpManagerEvent implements Parcelable { public static final int PROVISIONING_OK = 1; public static final int PROVISIONING_FAIL = 2; public static final int COMPLETE_LIFECYCLE = 3; + /** {@hide} */ + @IntDef(value = {PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + public final String ifName; - public final int eventType; + public final @EventType int eventType; public final long durationMs; - private IpManagerEvent(String ifName, int eventType, long duration) { + /** {@hide} */ + public IpManagerEvent(String ifName, @EventType int eventType, long duration) { this.ifName = ifName; this.eventType = eventType; this.durationMs = duration; @@ -49,12 +61,14 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela this.durationMs = in.readLong(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(ifName); out.writeInt(eventType); out.writeLong(durationMs); } + @Override public int describeContents() { return 0; } @@ -71,7 +85,6 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela }; public static void logEvent(int eventType, String ifName, long durationMs) { - logEvent(new IpManagerEvent(ifName, eventType, durationMs)); } @Override @@ -84,4 +97,4 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela static final SparseArray constants = MessageUtils.findMessageNames( new Class[]{IpManagerEvent.class}, new String[]{"PROVISIONING_", "COMPLETE_"}); } -}; +} diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java index d40389c03986712c6ca9953081cdc9066242c274..ee09e229266164b301742de36590d9ba5d6c1ad1 100644 --- a/core/java/android/net/metrics/IpReachabilityEvent.java +++ b/core/java/android/net/metrics/IpReachabilityEvent.java @@ -24,24 +24,35 @@ import android.util.SparseArray; import com.android.internal.util.MessageUtils; /** + * An event recorded when IpReachabilityMonitor sends a neighbor probe or receives + * a neighbor probe result. * {@hide} */ @SystemApi -public final class IpReachabilityEvent extends IpConnectivityEvent implements Parcelable { - - public static final int PROBE = 1 << 8; - public static final int NUD_FAILED = 2 << 8; - public static final int PROVISIONING_LOST = 3 << 8; +public final class IpReachabilityEvent implements Parcelable { + + // Event types. + /** A probe forced by IpReachabilityMonitor. */ + public static final int PROBE = 1 << 8; + /** Neighbor unreachable after a forced probe. */ + public static final int NUD_FAILED = 2 << 8; + /** Neighbor unreachable after a forced probe, IP provisioning is also lost. */ + public static final int PROVISIONING_LOST = 3 << 8; + /** {@hide} Neighbor unreachable notification from kernel. */ + public static final int NUD_FAILED_ORGANIC = 4 << 8; + /** {@hide} Neighbor unreachable notification from kernel, IP provisioning is also lost. */ + public static final int PROVISIONING_LOST_ORGANIC = 5 << 8; public final String ifName; // eventType byte format (MSB to LSB): // byte 0: unused // byte 1: unused // byte 2: type of event: PROBE, NUD_FAILED, PROVISIONING_LOST - // byte 3: kernel errno from RTNetlink or IpReachabilityMonitor + // byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor. public final int eventType; - private IpReachabilityEvent(String ifName, int eventType) { + /** {@hide} */ + public IpReachabilityEvent(String ifName, int eventType) { this.ifName = ifName; this.eventType = eventType; } @@ -51,11 +62,13 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa this.eventType = in.readInt(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(ifName); out.writeInt(eventType); } + @Override public int describeContents() { return 0; } @@ -72,21 +85,32 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa }; public static void logProbeEvent(String ifName, int nlErrorCode) { - logEvent(new IpReachabilityEvent(ifName, PROBE | (nlErrorCode & 0xFF))); } public static void logNudFailed(String ifName) { - logEvent(new IpReachabilityEvent(ifName, NUD_FAILED)); } public static void logProvisioningLost(String ifName) { - logEvent(new IpReachabilityEvent(ifName, PROVISIONING_LOST)); + } + + /** + * Returns the NUD failure event type code corresponding to the given conditions. + * {@hide} + */ + public static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) { + if (isFromProbe) { + return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED; + } else { + return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC; + } } @Override public String toString() { - return String.format("IpReachabilityEvent(%s, %s)", ifName, - Decoder.constants.get(eventType)); + int hi = eventType & 0xff00; + int lo = eventType & 0x00ff; + String eventName = Decoder.constants.get(hi); + return String.format("IpReachabilityEvent(%s, %s:%02x)", ifName, eventName, lo); } final static class Decoder { @@ -94,4 +118,4 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa MessageUtils.findMessageNames(new Class[]{IpReachabilityEvent.class}, new String[]{"PROBE", "PROVISIONING_", "NUD_"}); } -}; +} diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java index 08c9c758bbc8696aa04b76626c8b9719413a971e..3b3fa6976fc982181a7ee57103b47b62ef00bf93 100644 --- a/core/java/android/net/metrics/NetworkEvent.java +++ b/core/java/android/net/metrics/NetworkEvent.java @@ -16,6 +16,7 @@ package android.net.metrics; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,11 +24,14 @@ import android.util.SparseArray; import com.android.internal.util.MessageUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * {@hide} */ @SystemApi -public final class NetworkEvent extends IpConnectivityEvent implements Parcelable { +public final class NetworkEvent implements Parcelable { public static final int NETWORK_CONNECTED = 1; public static final int NETWORK_VALIDATED = 2; @@ -37,28 +41,49 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl public static final int NETWORK_UNLINGER = 6; public static final int NETWORK_DISCONNECTED = 7; + /** {@hide} */ + @IntDef(value = { + NETWORK_CONNECTED, + NETWORK_VALIDATED, + NETWORK_VALIDATION_FAILED, + NETWORK_CAPTIVE_PORTAL_FOUND, + NETWORK_LINGER, + NETWORK_UNLINGER, + NETWORK_DISCONNECTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + public final int netId; - public final int eventType; + public final @EventType int eventType; public final long durationMs; - private NetworkEvent(int netId, int eventType, long durationMs) { + /** {@hide} */ + public NetworkEvent(int netId, @EventType int eventType, long durationMs) { this.netId = netId; this.eventType = eventType; this.durationMs = durationMs; } + /** {@hide} */ + public NetworkEvent(int netId, @EventType int eventType) { + this(netId, eventType, 0); + } + private NetworkEvent(Parcel in) { netId = in.readInt(); eventType = in.readInt(); durationMs = in.readLong(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(netId); out.writeInt(eventType); out.writeLong(durationMs); } + @Override public int describeContents() { return 0; } @@ -75,15 +100,12 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl }; public static void logEvent(int netId, int eventType) { - logEvent(new NetworkEvent(netId, eventType, 0)); } public static void logValidated(int netId, long durationMs) { - logEvent(new NetworkEvent(netId, NETWORK_VALIDATED, durationMs)); } public static void logCaptivePortalFound(int netId, long durationMs) { - logEvent(new NetworkEvent(netId, NETWORK_CAPTIVE_PORTAL_FOUND, durationMs)); } @Override @@ -96,4 +118,4 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl static final SparseArray constants = MessageUtils.findMessageNames( new Class[]{NetworkEvent.class}, new String[]{"NETWORK_"}); } -}; +} diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..91bd023cd56cb19033a1337139747db98130702b --- /dev/null +++ b/core/java/android/net/metrics/RaEvent.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event logged when the APF packet socket receives an RA packet. + * {@hide} + */ +@SystemApi +public final class RaEvent implements Parcelable { + + /** {@hide} */ + public static final long NO_LIFETIME = -1L; + + // Lifetime in seconds of options found in a single RA packet. + // When an option is not set, the value of the associated field is -1; + public final long routerLifetime; + public final long prefixValidLifetime; + public final long prefixPreferredLifetime; + public final long routeInfoLifetime; + public final long rdnssLifetime; + public final long dnsslLifetime; + + /** {@hide} */ + public RaEvent(long routerLifetime, long prefixValidLifetime, long prefixPreferredLifetime, + long routeInfoLifetime, long rdnssLifetime, long dnsslLifetime) { + this.routerLifetime = routerLifetime; + this.prefixValidLifetime = prefixValidLifetime; + this.prefixPreferredLifetime = prefixPreferredLifetime; + this.routeInfoLifetime = routeInfoLifetime; + this.rdnssLifetime = rdnssLifetime; + this.dnsslLifetime = dnsslLifetime; + } + + private RaEvent(Parcel in) { + routerLifetime = in.readLong(); + prefixValidLifetime = in.readLong(); + prefixPreferredLifetime = in.readLong(); + routeInfoLifetime = in.readLong(); + rdnssLifetime = in.readLong(); + dnsslLifetime = in.readLong(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(routerLifetime); + out.writeLong(prefixValidLifetime); + out.writeLong(prefixPreferredLifetime); + out.writeLong(routeInfoLifetime); + out.writeLong(rdnssLifetime); + out.writeLong(dnsslLifetime); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return new StringBuilder("RaEvent(lifetimes: ") + .append(String.format("router=%ds, ", routerLifetime)) + .append(String.format("prefix_valid=%ds, ", prefixValidLifetime)) + .append(String.format("prefix_preferred=%ds, ", prefixPreferredLifetime)) + .append(String.format("route_info=%ds, ", routeInfoLifetime)) + .append(String.format("rdnss=%ds, ", rdnssLifetime)) + .append(String.format("dnssl=%ds)", dnsslLifetime)) + .toString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public RaEvent createFromParcel(Parcel in) { + return new RaEvent(in); + } + + public RaEvent[] newArray(int size) { + return new RaEvent[size]; + } + }; + + /** {@hide} */ + public static class Builder { + + long routerLifetime = NO_LIFETIME; + long prefixValidLifetime = NO_LIFETIME; + long prefixPreferredLifetime = NO_LIFETIME; + long routeInfoLifetime = NO_LIFETIME; + long rdnssLifetime = NO_LIFETIME; + long dnsslLifetime = NO_LIFETIME; + + public Builder() { + } + + public RaEvent build() { + return new RaEvent(routerLifetime, prefixValidLifetime, prefixPreferredLifetime, + routeInfoLifetime, rdnssLifetime, dnsslLifetime); + } + + public Builder updateRouterLifetime(long lifetime) { + routerLifetime = updateLifetime(routerLifetime, lifetime); + return this; + } + + public Builder updatePrefixValidLifetime(long lifetime) { + prefixValidLifetime = updateLifetime(prefixValidLifetime, lifetime); + return this; + } + + public Builder updatePrefixPreferredLifetime(long lifetime) { + prefixPreferredLifetime = updateLifetime(prefixPreferredLifetime, lifetime); + return this; + } + + public Builder updateRouteInfoLifetime(long lifetime) { + routeInfoLifetime = updateLifetime(routeInfoLifetime, lifetime); + return this; + } + + public Builder updateRdnssLifetime(long lifetime) { + rdnssLifetime = updateLifetime(rdnssLifetime, lifetime); + return this; + } + + public Builder updateDnsslLifetime(long lifetime) { + dnsslLifetime = updateLifetime(dnsslLifetime, lifetime); + return this; + } + + private long updateLifetime(long currentLifetime, long newLifetime) { + if (currentLifetime == RaEvent.NO_LIFETIME) { + return newLifetime; + } + return Math.min(currentLifetime, newLifetime); + } + } +} diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java index 751c35f8a1443c5a7c63d140fa1c81b0cc3c6cdf..331cf0cdd4ad7698534ecc268d77a02feea06397 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -16,6 +16,7 @@ package android.net.metrics; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,11 +24,15 @@ import android.util.SparseArray; import com.android.internal.util.MessageUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** + * An event recorded by NetworkMonitor when sending a probe for finding captive portals. * {@hide} */ @SystemApi -public final class ValidationProbeEvent extends IpConnectivityEvent implements Parcelable { +public final class ValidationProbeEvent implements Parcelable { public static final int PROBE_DNS = 0; public static final int PROBE_HTTP = 1; @@ -37,12 +42,24 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P public static final int DNS_FAILURE = 0; public static final int DNS_SUCCESS = 1; + /** {@hide} */ + @IntDef(value = {PROBE_DNS, PROBE_HTTP, PROBE_HTTPS, PROBE_PAC}) + @Retention(RetentionPolicy.SOURCE) + public @interface ProbeType {} + + /** {@hide} */ + @IntDef(value = {DNS_FAILURE, DNS_SUCCESS}) + @Retention(RetentionPolicy.SOURCE) + public @interface ReturnCode {} + public final int netId; public final long durationMs; - public final int probeType; - public final int returnCode; + public final @ProbeType int probeType; + public final @ReturnCode int returnCode; - private ValidationProbeEvent(int netId, long durationMs, int probeType, int returnCode) { + /** @hide */ + public ValidationProbeEvent( + int netId, long durationMs, @ProbeType int probeType, @ReturnCode int returnCode) { this.netId = netId; this.durationMs = durationMs; this.probeType = probeType; @@ -56,6 +73,7 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P returnCode = in.readInt(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(netId); out.writeLong(durationMs); @@ -63,6 +81,7 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P out.writeInt(returnCode); } + @Override public int describeContents() { return 0; } @@ -84,7 +103,6 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P } public static void logEvent(int netId, long durationMs, int probeType, int returnCode) { - logEvent(new ValidationProbeEvent(netId, durationMs, probeType, returnCode)); } @Override @@ -97,4 +115,4 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P static final SparseArray constants = MessageUtils.findMessageNames( new Class[]{ValidationProbeEvent.class}, new String[]{"PROBE_"}); } -}; +} diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md index fe13f7a5aab9883adfd3de2e2792e93a37d8992f..63ce1a2446430b675fb4e3bd6333305428c15184 100644 --- a/core/java/android/net/network-policy-restrictions.md +++ b/core/java/android/net/network-policy-restrictions.md @@ -29,8 +29,8 @@ More specifically: | **DS** | *WL* | ok | blk | ok | ok | | **ON** | *!WL* | blk | blk | blk | blk | | | *BL* | blk | blk | blk | blk | -| **DS** | *WL* | blk | ok | ok | ok | -| **OFF** | *!WL* | blk | ok | ok | ok | +| **DS** | *WL* | blk | blk | ok | ok | +| **OFF** | *!WL* | blk | blk | ok | ok | | | *BL* | blk | blk | blk | blk | diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 41ff9fdf8382b9067c3188d630914bebf1e4ce2a..783c25aa4d08502df6b15cc3623cb994659b5136 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -290,6 +290,7 @@ public final class NfcAdapter { // Guarded by NfcAdapter.class static boolean sIsInitialized = false; + static boolean sHasNfcFeature; // Final after first constructor, except for // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort @@ -434,6 +435,26 @@ public final class NfcAdapter { } } + /** + * Helper to check if this device is NFC HCE capable, by checking for + * FEATURE_NFC_HOST_CARD_EMULATION and/or FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * but without using a context. + */ + private static boolean hasNfcHceFeature() { + IPackageManager pm = ActivityThread.getPackageManager(); + if (pm == null) { + Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); + return false; + } + try { + return pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0); + } catch (RemoteException e) { + Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); + return false; + } + } + /** * Returns the NfcAdapter for application context, * or throws if NFC is not available. @@ -441,36 +462,39 @@ public final class NfcAdapter { */ public static synchronized NfcAdapter getNfcAdapter(Context context) { if (!sIsInitialized) { + sHasNfcFeature = hasNfcFeature(); + boolean hasHceFeature = hasNfcHceFeature(); /* is this device meant to have NFC */ - if (!hasNfcFeature()) { + if (!sHasNfcFeature && !hasHceFeature) { Log.v(TAG, "this device does not have NFC support"); throw new UnsupportedOperationException(); } - sService = getServiceInterface(); if (sService == null) { Log.e(TAG, "could not retrieve NFC service"); throw new UnsupportedOperationException(); } - try { - sTagService = sService.getNfcTagInterface(); - } catch (RemoteException e) { - Log.e(TAG, "could not retrieve NFC Tag service"); - throw new UnsupportedOperationException(); - } - - try { - sCardEmulationService = sService.getNfcCardEmulationInterface(); - } catch (RemoteException e) { - Log.e(TAG, "could not retrieve card emulation service"); - throw new UnsupportedOperationException(); + if (sHasNfcFeature) { + try { + sTagService = sService.getNfcTagInterface(); + } catch (RemoteException e) { + Log.e(TAG, "could not retrieve NFC Tag service"); + throw new UnsupportedOperationException(); + } } - - try { - sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); - } catch (RemoteException e) { - Log.e(TAG, "could not retrieve NFC-F card emulation service"); - throw new UnsupportedOperationException(); + if (hasHceFeature) { + try { + sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); + } catch (RemoteException e) { + Log.e(TAG, "could not retrieve NFC-F card emulation service"); + throw new UnsupportedOperationException(); + } + try { + sCardEmulationService = sService.getNfcCardEmulationInterface(); + } catch (RemoteException e) { + Log.e(TAG, "could not retrieve card emulation service"); + throw new UnsupportedOperationException(); + } } sIsInitialized = true; @@ -838,8 +862,14 @@ public final class NfcAdapter { * * @param uris an array of Uri(s) to push over Android Beam * @param activity activity for which the Uri(s) will be pushed + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void setBeamPushUris(Uri[] uris, Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null) { throw new NullPointerException("activity cannot be null"); } @@ -914,8 +944,14 @@ public final class NfcAdapter { * * @param callback callback, or null to disable * @param activity activity for which the Uri(s) will be pushed + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null) { throw new NullPointerException("activity cannot be null"); } @@ -992,9 +1028,15 @@ public final class NfcAdapter { * @param activities optional additional activities, however we strongly recommend * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void setNdefPushMessage(NdefMessage message, Activity activity, Activity ... activities) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } int targetSdkVersion = getSdkVersion(); try { if (activity == null) { @@ -1024,6 +1066,11 @@ public final class NfcAdapter { */ @SystemApi public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null) { throw new NullPointerException("activity cannot be null"); } @@ -1094,9 +1141,15 @@ public final class NfcAdapter { * @param activities optional additional activities, however we strongly recommend * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, Activity ... activities) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } int targetSdkVersion = getSdkVersion(); try { if (activity == null) { @@ -1168,9 +1221,15 @@ public final class NfcAdapter { * @param activities optional additional activities, however we strongly recommend * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, Activity activity, Activity ... activities) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } int targetSdkVersion = getSdkVersion(); try { if (activity == null) { @@ -1227,9 +1286,15 @@ public final class NfcAdapter { * @param techLists the tech lists used to perform matching for dispatching of the * {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent * @throws IllegalStateException if the Activity is not currently in the foreground + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void enableForegroundDispatch(Activity activity, PendingIntent intent, IntentFilter[] filters, String[][] techLists) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null || intent == null) { throw new NullPointerException(); } @@ -1263,8 +1328,14 @@ public final class NfcAdapter { * * @param activity the Activity to disable dispatch to * @throws IllegalStateException if the Activity has already been paused + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void disableForegroundDispatch(Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, mForegroundDispatchListener); disableForegroundDispatchInternal(activity, false); @@ -1309,9 +1380,15 @@ public final class NfcAdapter { * @param callback the callback to be called when a tag is discovered * @param flags Flags indicating poll technologies and other optional parameters * @param extras Additional extras for configuring reader mode. + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags, Bundle extras) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } mNfcActivityManager.enableReaderMode(activity, callback, flags, extras); } @@ -1321,8 +1398,14 @@ public final class NfcAdapter { * all supported tag technologies. * * @param activity the Activity that currently has reader mode enabled + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public void disableReaderMode(Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } mNfcActivityManager.disableReaderMode(activity); } @@ -1349,8 +1432,14 @@ public final class NfcAdapter { * * @param activity the current foreground Activity that has registered data to share * @return whether the Beam animation was successfully invoked + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public boolean invokeBeam(Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null) { throw new NullPointerException("activity may not be null."); } @@ -1404,10 +1493,16 @@ public final class NfcAdapter { * @param activity foreground activity * @param message a NDEF Message to push over NFC * @throws IllegalStateException if the activity is not currently in the foreground + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. * @deprecated use {@link #setNdefPushMessage} instead */ @Deprecated public void enableForegroundNdefPush(Activity activity, NdefMessage message) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null || message == null) { throw new NullPointerException(); } @@ -1432,10 +1527,16 @@ public final class NfcAdapter { * * @param activity the Foreground activity * @throws IllegalStateException if the Activity has already been paused + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. * @deprecated use {@link #setNdefPushMessage} instead */ @Deprecated public void disableForegroundNdefPush(Activity activity) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } if (activity == null) { throw new NullPointerException(); } @@ -1452,6 +1553,9 @@ public final class NfcAdapter { */ @SystemApi public boolean enableNdefPush() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } try { return sService.enableNdefPush(); } catch (RemoteException e) { @@ -1467,6 +1571,11 @@ public final class NfcAdapter { */ @SystemApi public boolean disableNdefPush() { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } try { return sService.disableNdefPush(); } catch (RemoteException e) { @@ -1497,8 +1606,14 @@ public final class NfcAdapter { * * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS * @return true if NDEF Push feature is enabled + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ public boolean isNdefPushEnabled() { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } try { return sService.isNdefPushEnabled(); } catch (RemoteException e) { @@ -1623,6 +1738,11 @@ public final class NfcAdapter { @SystemApi public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler, String[] tagTechnologies) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } // If there are no tag technologies, don't bother adding unlock handler if (tagTechnologies.length == 0) { return false; @@ -1666,6 +1786,11 @@ public final class NfcAdapter { */ @SystemApi public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) { + synchronized (NfcAdapter.class) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + } try { synchronized (mLock) { if (mNfcUnlockHandlers.containsKey(unlockHandler)) { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 63f39c59b1b7f0010eab71aa1a80c0486b189100..4f4e7223acb98240e4c154e3e793101016596ce2 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -35,8 +35,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** - *

AsyncTask enables proper and easy use of the UI thread. This class allows to - * perform background operations and publish results on the UI thread without + *

AsyncTask enables proper and easy use of the UI thread. This class allows you + * to perform background operations and publish results on the UI thread without * having to manipulate threads and/or handlers.

* *

AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} @@ -55,7 +55,7 @@ import java.util.concurrent.atomic.AtomicInteger; *

*

Developer Guides

*

For more information about using tasks and threads, read the - * Processes and + * Processes and * Threads developer guide.

*
* diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4acb729b6a76026adfaf001c44ad7e1bc507a47e..a0c2efd407ba0387936c732bf175ab7ec5bd41b3 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -30,6 +30,8 @@ import android.content.pm.ApplicationInfo; import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.Log; +import android.util.LongSparseArray; import android.util.MutableBoolean; import android.util.Pair; import android.util.Printer; @@ -47,6 +49,7 @@ import com.android.internal.os.BatteryStatsHelper; * @hide */ public abstract class BatteryStats implements Parcelable { + private static final String TAG = "BatteryStats"; private static final boolean LOCAL_LOGV = false; @@ -175,8 +178,11 @@ public abstract class BatteryStats implements Parcelable { /** * Current version of checkin data format. + * + * New in version 19: + * - Wakelock data (wl) gets current and max times. */ - static final String CHECKIN_VERSION = "18"; + static final String CHECKIN_VERSION = "19"; /** * Old version, we hit 9 and ran out of room, need to remove. @@ -351,6 +357,32 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs); + /** + * Returns the max duration if it is being tracked. + * Not all Timer subclasses track the max duration and the current duration. + + */ + public long getMaxDurationMsLocked(long elapsedRealtimeMs) { + return -1; + } + + /** + * Returns the current time the timer has been active, if it is being tracked. + * Not all Timer subclasses track the max duration and the current duration. + */ + public long getCurrentDurationMsLocked(long elapsedRealtimeMs) { + return -1; + } + + /** + * Returns whether the timer is currently running. Some types of timers + * (e.g. BatchTimers) don't know whether the event is currently active, + * and report false. + */ + public boolean isRunningLocked() { + return false; + } + /** * Temporary for debugging. */ @@ -546,6 +578,20 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getTimeAtCpuSpeed(int cluster, int step, int which); + /** + * Returns the number of times this UID woke up the Application Processor to + * process a mobile radio packet. + * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. + */ + public abstract long getMobileRadioApWakeupCount(int which); + + /** + * Returns the number of times this UID woke up the Application Processor to + * process a WiFi packet. + * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. + */ + public abstract long getWifiRadioApWakeupCount(int which); + public static abstract class Sensor { /* * FIXME: it's not correct to use this magic value because it @@ -1285,9 +1331,14 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_TEMP_WHITELIST = 0x0011; // Event for the screen waking up. public static final int EVENT_SCREEN_WAKE_UP = 0x0012; + // Event for the UID that woke up the application processor. + // Used for wakeups coming from WiFi, modem, etc. + public static final int EVENT_WAKEUP_AP = 0x0013; + // Event for reporting that a specific partial wake lock has been held for a long duration. + public static final int EVENT_LONG_WAKE_LOCK = 0x0014; // Number of event types. - public static final int EVENT_COUNT = 0x0013; + public static final int EVENT_COUNT = 0x0015; // Mask to extract out only the type part of the event. public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); @@ -1315,6 +1366,10 @@ public abstract class BatteryStats implements Parcelable { EVENT_TEMP_WHITELIST | EVENT_FLAG_START; public static final int EVENT_TEMP_WHITELIST_FINISH = EVENT_TEMP_WHITELIST | EVENT_FLAG_FINISH; + public static final int EVENT_LONG_WAKE_LOCK_START = + EVENT_LONG_WAKE_LOCK | EVENT_FLAG_START; + public static final int EVENT_LONG_WAKE_LOCK_FINISH = + EVENT_LONG_WAKE_LOCK | EVENT_FLAG_FINISH; // For CMD_EVENT. public int eventCode; @@ -1979,13 +2034,13 @@ public abstract class BatteryStats implements Parcelable { public static final String[] HISTORY_EVENT_NAMES = new String[] { "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn", "active", "pkginst", "pkgunin", "alarm", "stats", "inactive", "active", "tmpwhitelist", - "screenwake", + "screenwake", "wakeupap", "longwake" }; public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] { "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn", "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw", - "Esw", + "Esw", "Ewa", "Elw" }; /** @@ -2535,6 +2590,22 @@ public abstract class BatteryStats implements Parcelable { sb.append('('); sb.append(count); sb.append(" times)"); + final long maxDurationMs = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000); + if (maxDurationMs >= 0) { + sb.append(" max="); + sb.append(maxDurationMs); + } + if (timer.isRunningLocked()) { + final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000); + if (currentMs >= 0) { + sb.append(" (running for "); + sb.append(currentMs); + sb.append("ms)"); + } else { + sb.append(" (running)"); + } + } + return ", "; } } @@ -2542,6 +2613,7 @@ public abstract class BatteryStats implements Parcelable { } /** + * Prints details about a timer, if its total time was greater than 0. * * @param pw a PrintWriter object to print to. * @param sb a StringBuilder object. @@ -2550,24 +2622,40 @@ public abstract class BatteryStats implements Parcelable { * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. * @param prefix a String to be prepended to each line of output. * @param type the name of the timer. + * @return true if anything was printed. */ private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer, - long rawRealtime, int which, String prefix, String type) { + long rawRealtimeUs, int which, String prefix, String type) { if (timer != null) { // Convert from microseconds to milliseconds with rounding - final long totalTime = (timer.getTotalTimeLocked( - rawRealtime, which) + 500) / 1000; + final long totalTimeMs = (timer.getTotalTimeLocked( + rawRealtimeUs, which) + 500) / 1000; final int count = timer.getCountLocked(which); - if (totalTime != 0) { + if (totalTimeMs != 0) { sb.setLength(0); sb.append(prefix); sb.append(" "); sb.append(type); sb.append(": "); - formatTimeMs(sb, totalTime); + formatTimeMs(sb, totalTimeMs); sb.append("realtime ("); sb.append(count); sb.append(" times)"); + final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs/1000); + if (maxDurationMs >= 0) { + sb.append(" max="); + sb.append(maxDurationMs); + } + if (timer.isRunningLocked()) { + final long currentMs = timer.getCurrentDurationMsLocked(rawRealtimeUs/1000); + if (currentMs >= 0) { + sb.append(" (running for "); + sb.append(currentMs); + sb.append("ms)"); + } else { + sb.append(" (running)"); + } + } pw.println(sb.toString()); return true; } @@ -2590,15 +2678,23 @@ public abstract class BatteryStats implements Parcelable { long elapsedRealtimeUs, String name, int which, String linePrefix) { long totalTimeMicros = 0; int count = 0; + long max = -1; + long current = -1; if (timer != null) { totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which); count = timer.getCountLocked(which); + current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000); + max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000); } sb.append(linePrefix); sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding sb.append(','); sb.append(name != null ? name + "," : ""); sb.append(count); + sb.append(','); + sb.append(current); + sb.append(','); + sb.append(max); return ","; } @@ -3105,20 +3201,22 @@ public abstract class BatteryStats implements Parcelable { final long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which); final long mobileActiveTime = u.getMobileRadioActiveTime(which); final int mobileActiveCount = u.getMobileRadioActiveCount(which); + final long mobileWakeup = u.getMobileRadioApWakeupCount(which); final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which); final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which); + final long wifiWakeup = u.getWifiRadioApWakeupCount(which); final long btBytesRx = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which); final long btBytesTx = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which); if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0 || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0 || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0 - || btBytesRx > 0 || btBytesTx > 0) { + || btBytesRx > 0 || btBytesTx > 0 || mobileWakeup > 0 || wifiWakeup > 0) { dumpLine(pw, uid, category, NETWORK_DATA, mobileBytesRx, mobileBytesTx, wifiBytesRx, wifiBytesTx, mobilePacketsRx, mobilePacketsTx, wifiPacketsRx, wifiPacketsTx, mobileActiveTime, mobileActiveCount, - btBytesRx, btBytesTx); + btBytesRx, btBytesTx, mobileWakeup, wifiWakeup); } // Dump modem controller data, per UID. @@ -4125,6 +4223,9 @@ public abstract class BatteryStats implements Parcelable { final int wifiScanCount = u.getWifiScanCount(which); final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which); + final long mobileWakeup = u.getMobileRadioApWakeupCount(which); + final long wifiWakeup = u.getWifiRadioApWakeupCount(which); + if (mobileRxBytes > 0 || mobileTxBytes > 0 || mobileRxPackets > 0 || mobileTxPackets > 0) { pw.print(prefix); pw.print(" Mobile network: "); @@ -4150,6 +4251,14 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + if (mobileWakeup > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Mobile radio AP wakeups: "); + sb.append(mobileWakeup); + pw.println(sb.toString()); + } + printControllerActivityIfInteresting(pw, sb, prefix + " ", "Modem", u.getModemControllerActivity(), which); @@ -4181,6 +4290,14 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } + if (wifiWakeup > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" WiFi AP wakeups: "); + sb.append(wifiWakeup); + pw.println(sb.toString()); + } + printControllerActivityIfInteresting(pw, sb, prefix + " ", "WiFi", u.getWifiControllerActivity(), which); @@ -4922,7 +5039,9 @@ public abstract class BatteryStats implements Parcelable { pw.print(','); pw.print(rec.stepDetails.statIdlTime); pw.print(','); - pw.print(rec.stepDetails.statPlatformIdleState); + if (rec.stepDetails.statPlatformIdleState != null) { + pw.print(rec.stepDetails.statPlatformIdleState); + } pw.println(); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index dc7be6b5ad613f3a9c18c9fad08089e060e86955..3d3dc9cc3418ccb16ef9493cb8e7d9c4b5fa96b6 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -669,9 +669,54 @@ public class Build { public static final int M = 23; /** - * N is for ¯\_(ツ)_/¯. + * N is for Nougat. + * + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • {@link android.app.DownloadManager.Request#setAllowedNetworkTypes + * DownloadManager.Request.setAllowedNetworkTypes} + * will disable "allow over metered" when specifying only + * {@link android.app.DownloadManager.Request#NETWORK_WIFI}.
  • + *
  • {@link android.app.DownloadManager} no longer allows access to raw + * file paths.
  • + *
  • {@link android.app.Notification.Builder#setShowWhen + * Notification.Builder.setShowWhen} + * must be called explicitly to have the time shown, and various other changes in + * {@link android.app.Notification.Builder Notification.Builder} to how notifications + * are shown.
  • + *
  • {@link android.content.Context#MODE_WORLD_READABLE} and + * {@link android.content.Context#MODE_WORLD_WRITEABLE} are no longer supported.
  • + *
  • {@link android.os.FileUriExposedException} will be thrown to applications.
  • + *
  • Applications will see global drag and drops as per + * {@link android.view.View#DRAG_FLAG_GLOBAL}.
  • + *
  • {@link android.webkit.WebView#evaluateJavascript WebView.evaluateJavascript} + * will not persist state from an empty WebView.
  • + *
  • {@link android.animation.AnimatorSet} will not ignore calls to end() before + * start().
  • + *
  • {@link android.app.AlarmManager#cancel(android.app.PendingIntent) + * AlarmManager.cancel} will throw a NullPointerException if given a null operation.
  • + *
  • {@link android.app.FragmentManager} will ensure fragments have been created + * before being placed on the back stack.
  • + *
  • {@link android.app.FragmentManager} restores fragments in + * {@link android.app.Fragment#onCreate Fragment.onCreate} rather than after the + * method returns.
  • + *
  • {@link android.R.attr#resizeableActivity} defaults to true.
  • + *
  • {@link android.graphics.drawable.AnimatedVectorDrawable} throws exceptions when + * opening invalid VectorDrawable animations.
  • + *
  • {@link android.view.ViewGroup.MarginLayoutParams} will no longer be dropped + * when converting between some types of layout params (such as + * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} to + * {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams}).
  • + *
  • Your application processes will not be killed when the device density changes.
  • + *
*/ public static final int N = 24; + + /** + * N MR1: Nougat++. + */ + public static final int N_MR1 = 25; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index d8be2b6dcc4c4f4064127fb2b2e1e564c395a0c7..4616af8f7768963dc31bbc9421e6ae5c2e7a5e70 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -285,6 +285,11 @@ public class Environment { return buildPath(getDataDirectory(), "misc", "profiles", "cur", String.valueOf(userId)); } + /** {@hide} */ + public static File getReferenceProfile(String packageName) { + return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName); + } + /** {@hide} */ public static File getDataProfilesDePackageDirectory(int userId, String packageName) { return buildPath(getDataProfilesDeDirectory(userId), packageName); @@ -339,6 +344,41 @@ public class Environment { return new File(getDataUserDeDirectory(volumeUuid, userId), packageName); } + /** + * Return preloads directory. + *

This directory may contain pre-loaded content such as + * {@link #getDataPreloadsDemoDirectory() demo videos} and + * {@link #getDataPreloadsAppsDirectory() APK files} . + * {@hide} + */ + public static File getDataPreloadsDirectory() { + return new File(getDataDirectory(), "preloads"); + } + + /** + * @see #getDataPreloadsDirectory() + * {@hide} + */ + public static File getDataPreloadsDemoDirectory() { + return new File(getDataPreloadsDirectory(), "demo"); + } + + /** + * @see #getDataPreloadsDirectory() + * {@hide} + */ + public static File getDataPreloadsAppsDirectory() { + return new File(getDataPreloadsDirectory(), "apps"); + } + + /** + * @see #getDataPreloadsDirectory() + * {@hide} + */ + public static File getDataPreloadsMediaDirectory() { + return new File(getDataPreloadsDirectory(), "media"); + } + /** * Return the primary shared/external storage directory. This directory may * not currently be accessible if it has been mounted by the user on their diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index e1f2eaf0d9710c0efb2d98f1139157047c2fefe5..b6d3655f4b1a3bc7b9b7d77d7d4c0caa72a06bee 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -18,6 +18,7 @@ package android.os; import android.net.InterfaceConfiguration; +import android.net.INetd; import android.net.INetworkManagementEventObserver; import android.net.Network; import android.net.NetworkStats; @@ -36,7 +37,7 @@ interface INetworkManagementService **/ /** - * Register an observer to receive events + * Register an observer to receive events. */ void registerObserver(INetworkManagementEventObserver obs); @@ -45,6 +46,11 @@ interface INetworkManagementService */ void unregisterObserver(INetworkManagementEventObserver obs); + /** + * Retrieve an INetd to talk to netd. + */ + INetd getNetdService(); + /** * Returns a list of currently known network interfaces */ @@ -446,6 +452,7 @@ interface INetworkManagementService void addInterfaceToLocalNetwork(String iface, in List routes); void removeInterfaceFromLocalNetwork(String iface); + int removeRoutesFromLocalNetwork(in List routes); void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges); /** diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index b27cb32bd4eb6216834c2593762361bb01ad2f6f..eeb641d33c20d4da561c1dc8f577e7b6e89b95a6 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -81,4 +81,5 @@ interface IUserManager { void clearSeedAccountData(); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean isManagedProfile(int userId); + boolean isDemoUser(int userId); } diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index b58ff1fd4755afd2913763fede2d7f944ccf2637..d299672ea1d5e15b3d7375d888595c4e0a774435 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -147,7 +147,7 @@ public final class Looper { } final long traceTag = me.mTraceTag; - if (traceTag != 0) { + if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 74dcc0787b3bd91ed1009cd1bad48465c8a565df..f6e6ad6067bb0bae21b2aed51ac111e489e19f3e 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -36,6 +36,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -2571,6 +2572,20 @@ public final class Parcel { return p; } + /** @hide */ + public final T[] readParcelableArray(ClassLoader loader, + Class clazz) { + int N = readInt(); + if (N < 0) { + return null; + } + T[] p = (T[]) Array.newInstance(clazz, N); + for (int i = 0; i < N; i++) { + p[i] = readParcelable(loader); + } + return p; + } + /** * Read and return a new Serializable object from the parcel. * @return the Serializable object, or null if the Serializable name diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 8104c69803fa3538afa8761bcc356aeabb5082d5..bbafb5662cd64de10655b6a4dd38bcf6e8b59973 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -57,14 +57,19 @@ public abstract class PowerManagerInternal { /** * Power hint: * Interaction: The user is interacting with the device. The corresponding data field must be - * the expected duration of the fling, or 0 if unknown. + * the expected duration of the interaction, or 0 if unknown. * - * Sustained Performance Mode: Enable/Disables Sustained Performance Mode. + * Sustained Performance Mode: The corresponding data field must be Enable/Disable + * Sustained Performance Mode. + * + * Launch: This is specific for activity launching. The corresponding data field must be + * the expected duration of the required boost, or 0 if unknown. * * These must be kept in sync with the values in hardware/libhardware/include/hardware/power.h */ public static final int POWER_HINT_INTERACTION = 2; public static final int POWER_HINT_SUSTAINED_PERFORMANCE_MODE = 6; + public static final int POWER_HINT_LAUNCH = 8; public static String wakefulnessToString(int wakefulness) { switch (wakefulness) { diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 21b3f6ed4582e3bffde05bc46e1cde4ae88fd9f4..e1b7fdad25e7dff4f30f3c9ca725e2ac36ae700e 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,9 +16,11 @@ package android.os; +import android.annotation.TestApi; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.system.Os; +import android.system.OsConstants; import android.util.Log; import com.android.internal.os.Zygote; import dalvik.system.VMRuntime; @@ -322,6 +324,12 @@ public class Process { */ public static final int SCHED_IDLE = 5; + /** + * Reset scheduler choice on fork. + * @hide + */ + public static final int SCHED_RESET_ON_FORK = 0x40000000; + // Keep in sync with SP_* constants of enum type SchedPolicy // declared in system/core/include/cutils/sched_policy.h, // except THREAD_GROUP_DEFAULT does not correspond to any SP_* value. @@ -973,6 +981,9 @@ public class Process { * priority. * If the thread is a thread group leader, that is it's gettid() == getpid(), * then the other threads in the same thread group are _not_ affected. + * + * Does not set cpuset for some historical reason, just calls + * libcutils::set_sched_policy(). */ public static final native void setThreadGroup(int tid, int group) throws IllegalArgumentException, SecurityException; @@ -994,6 +1005,8 @@ public class Process { * priority threads alone. group == THREAD_GROUP_BG_NONINTERACTIVE moves all * threads, regardless of priority, to the background scheduling group. * group == THREAD_GROUP_FOREGROUND is not allowed. + * + * Always sets cpusets. */ public static final native void setProcessGroup(int pid, int group) throws IllegalArgumentException, SecurityException; @@ -1064,6 +1077,24 @@ public class Process { public static final native int getThreadPriority(int tid) throws IllegalArgumentException; + /** + * Return the current scheduling policy of a thread, based on Linux. + * + * @param tid The identifier of the thread/process to get the scheduling policy. + * + * @throws IllegalArgumentException Throws IllegalArgumentException if + * tid does not exist, or if priority is out of range for the policy. + * @throws SecurityException Throws SecurityException if your process does + * not have permission to modify the given thread, or to use the given + * scheduling policy or priority. + * + * {@hide} + */ + + @TestApi + public static final native int getThreadScheduler(int tid) + throws IllegalArgumentException; + /** * Set the scheduling policy and priority of a thread, based on Linux. * @@ -1079,6 +1110,7 @@ public class Process { * * {@hide} */ + public static final native void setThreadScheduler(int tid, int policy, int priority) throws IllegalArgumentException; @@ -1252,4 +1284,26 @@ public class Process { * @hide */ public static final native void removeAllProcessGroups(); + + /** + * Check to see if a thread belongs to a given process. This may require + * more permissions than apps generally have. + * @return true if this thread belongs to a process + * @hide + */ + public static final boolean isThreadInProcess(int tid, int pid) { + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) { + return true; + } else { + return false; + } + } catch (Exception e) { + return false; + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + + } } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index dd7be53d98659c9fe7de4452a082db07d0dec567..507379baaa9495874a98d0640e0437dc8a54a180 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -570,18 +570,19 @@ public class RecoverySystem { * @throws SecurityException if the current user is not allowed to wipe data. */ public static void rebootWipeUserData(Context context) throws IOException { - rebootWipeUserData(context, false, context.getPackageName()); + rebootWipeUserData(context, false /* shutdown */, context.getPackageName(), + false /* force */); } /** {@hide} */ public static void rebootWipeUserData(Context context, String reason) throws IOException { - rebootWipeUserData(context, false, reason); + rebootWipeUserData(context, false /* shutdown */, reason, false /* force */); } /** {@hide} */ public static void rebootWipeUserData(Context context, boolean shutdown) throws IOException { - rebootWipeUserData(context, shutdown, context.getPackageName()); + rebootWipeUserData(context, shutdown, context.getPackageName(), false /* force */); } /** @@ -595,6 +596,9 @@ public class RecoverySystem { * @param shutdown if true, the device will be powered down after * the wipe completes, rather than being rebooted * back to the regular system. + * @param reason the reason for the wipe that is visible in the logs + * @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction + * should be ignored * * @throws IOException if writing the recovery command file * fails, or if the reboot itself fails. @@ -602,10 +606,10 @@ public class RecoverySystem { * * @hide */ - public static void rebootWipeUserData(Context context, boolean shutdown, String reason) - throws IOException { + public static void rebootWipeUserData(Context context, boolean shutdown, String reason, + boolean force) throws IOException { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); - if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { + if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { throw new SecurityException("Wiping data is not allowed for this user."); } final ConditionVariable condition = new ConditionVariable(); @@ -657,6 +661,31 @@ public class RecoverySystem { bootCommand(context, "--wipe_cache", reasonArg, localeArg); } + /** + * Reboot into recovery and wipe the A/B device. + * + * @param Context the Context to use. + * @param packageFile the wipe package to be applied. + * @param reason the reason to wipe. + * + * @throws IOException if something goes wrong. + * + * @hide + */ + @SystemApi + public static void rebootWipeAb(Context context, File packageFile, String reason) + throws IOException { + String reasonArg = null; + if (!TextUtils.isEmpty(reason)) { + reasonArg = "--reason=" + sanitizeArg(reason); + } + + final String filename = packageFile.getCanonicalPath(); + final String filenameArg = "--wipe_package=" + filename; + final String localeArg = "--locale=" + Locale.getDefault().toString(); + bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg); + } + /** * Reboot into the recovery system with the supplied argument. * @param args to pass to the recovery utility. diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 388403809d66159436411cc9023fa15918b34c5f..9e778cf36970e8536992c71cf9e8daea3015a71c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -580,6 +580,16 @@ public class UserManager { */ public static final String DISALLOW_CAMERA = "no_camera"; + /** + * Specifies if a user is not allowed to unmute the device's master volume. + * + * @see DevicePolicyManager#setMasterVolumeMuted(ComponentName, boolean) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLLOW_UNMUTE_DEVICE = "disallow_unmute_device"; + /** * Specifies if a user is not allowed to use cellular data when roaming. This can only be set by * device owners. The default value is false. @@ -603,6 +613,18 @@ public class UserManager { */ public static final String DISALLOW_SET_USER_ICON = "no_set_user_icon"; + /** + * Specifies if a user is not allowed to enable the oem unlock setting. The default value is + * false. Setting this restriction has no effect if the bootloader is already + * unlocked. + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock"; + /** * Allows apps in the parent profile to handle web links from the managed profile. * @@ -802,7 +824,7 @@ public class UserManager { */ public boolean isPrimaryUser() { UserInfo user = getUserInfo(UserHandle.myUserId()); - return user != null ? user.isPrimary() : false; + return user != null && user.isPrimary(); } /** @@ -868,7 +890,21 @@ public class UserManager { */ public boolean isGuestUser() { UserInfo user = getUserInfo(UserHandle.myUserId()); - return user != null ? user.isGuest() : false; + return user != null && user.isGuest(); + } + + /** + * Checks if the calling app is running in a demo user. When running in a demo user, + * apps can be more helpful to the user, or explain their features in more detail. + * + * @return whether the caller is a demo user. + */ + public boolean isDemoUser() { + try { + return mService.isDemoUser(UserHandle.myUserId()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** @@ -1988,6 +2024,10 @@ public class UserManager { if (!supportsMultipleUsers()) { return false; } + // If Demo Mode is on, don't show user switcher + if (isDeviceInDemoMode(mContext)) { + return false; + } List users = getUsers(true); if (users == null) { return false; @@ -2003,6 +2043,14 @@ public class UserManager { return switchableUserCount > 1 || guestEnabled; } + /** + * @hide + */ + public static boolean isDeviceInDemoMode(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.DEVICE_DEMO_MODE, 0) > 0; + } + /** * Returns a serial number on this device for a given userHandle. User handles can be recycled * when deleting and creating users, but serial numbers are not reused until the device is wiped. diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 485bbd105603b23790a507fec5ce4f24f40f3e62..c5507b9841bc6b2694ff885dc1350a9db7d5d988 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -20,6 +20,7 @@ import static android.net.TrafficStats.MB_IN_BYTES; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SdkConstant; import android.app.ActivityThread; import android.content.ContentResolver; import android.content.Context; @@ -45,8 +46,11 @@ import android.util.SparseArray; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; @@ -92,6 +96,19 @@ public class StorageManager { /** {@hide} */ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; + + /** + * Activity Action: Allows the user to manage their storage. This activity provides the ability + * to free up space on the device by deleting data such as apps. + *

+ * Input: Nothing. + *

+ * Output: Nothing. + */ + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_STORAGE + = "android.os.storage.action.MANAGE_STORAGE"; + /** {@hide} */ public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; /** {@hide} */ @@ -116,6 +133,15 @@ public class StorageManager { private static volatile IMountService sMountService = null; + // TODO: the location of the primary storage block varies from device to device, so we need to + // try the most likely candidates - a long-term solution would be a device-specific vold + // function that returns the calculated size. + private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { + "/sys/block/mmcblk0/size", + "/sys/block/sda/size" + }; + private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; + private final Context mContext; private final ContentResolver mResolver; @@ -903,6 +929,27 @@ public class StorageManager { return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; } + /** {@hide} */ + public long getPrimaryStorageSize() { + for (String path : INTERNAL_STORAGE_SIZE_PATHS) { + final long numberBlocks = readLong(path); + if (numberBlocks > 0) { + return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE; + } + } + return 0; + } + + private long readLong(String path) { + try (final FileInputStream fis = new FileInputStream(path); + final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { + return Long.parseLong(reader.readLine()); + } catch (Exception e) { + Slog.w(TAG, "Could not read " + path, e); + return 0; + } + } + /** @removed */ public @NonNull StorageVolume[] getVolumeList() { return getVolumeList(mContext.getUserId(), 0); diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 3e496b6eb550b9bd7630961e9967cd8ae843c3d9..73fa01e592011cca5bb4686bec29ce51ff02ed5a 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -27,12 +27,14 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.text.TextUtils; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnKeyListener; import android.view.ViewGroup; import android.widget.ListView; +import android.widget.TextView; /** * Shows a hierarchy of {@link Preference} objects as @@ -189,12 +191,10 @@ public abstract class PreferenceFragment extends Fragment implements 0); ListView lv = (ListView) view.findViewById(android.R.id.list); - if (lv != null) { - Drawable divider = - a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider); - if (divider != null) { - lv.setDivider(divider); - } + if (lv != null + && a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceFragment_divider)) { + lv.setDivider( + a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider)); } a.recycle(); @@ -368,6 +368,20 @@ public abstract class PreferenceFragment extends Fragment implements private void bindPreferences() { final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { + View root = getView(); + if (root != null) { + View titleView = root.findViewById(android.R.id.title); + if (titleView instanceof TextView) { + CharSequence title = preferenceScreen.getTitle(); + if (TextUtils.isEmpty(title)) { + titleView.setVisibility(View.GONE); + } else { + ((TextView) titleView).setText(title); + titleView.setVisibility(View.VISIBLE); + } + } + } + preferenceScreen.bind(getListView()); } onBindPreferences(); diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index b1317e656e3e4287d5cfe8b2618846d176d66b18..2305b0564e4995033ab4de326e8e81e2338af83a 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -19,6 +19,8 @@ package android.preference; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -31,6 +33,7 @@ import android.widget.Adapter; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; +import android.widget.TextView; /** * Represents a top-level {@link Preference} that @@ -91,13 +94,33 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi private Dialog mDialog; private ListView mListView; - + + private int mLayoutResId = com.android.internal.R.layout.preference_list_fragment; + private Drawable mDividerDrawable; + private boolean mDividerSpecified; + /** * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}. * @hide- */ public PreferenceScreen(Context context, AttributeSet attrs) { super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle); + + TypedArray a = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.PreferenceScreen, + com.android.internal.R.attr.preferenceScreenStyle, + 0); + + mLayoutResId = a.getResourceId( + com.android.internal.R.styleable.PreferenceScreen_screenLayout, + mLayoutResId); + if (a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceScreen_divider)) { + mDividerDrawable = + a.getDrawable(com.android.internal.R.styleable.PreferenceScreen_divider); + mDividerSpecified = true; + } + + a.recycle(); } /** @@ -163,18 +186,30 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View childPrefScreen = inflater.inflate( - com.android.internal.R.layout.preference_list_fragment, null); + View childPrefScreen = inflater.inflate(mLayoutResId, null); + View titleView = childPrefScreen.findViewById(android.R.id.title); mListView = (ListView) childPrefScreen.findViewById(android.R.id.list); + if (mDividerSpecified) { + mListView.setDivider(mDividerDrawable); + } + bind(mListView); // Set the title bar if title is available, else no title bar final CharSequence title = getTitle(); Dialog dialog = mDialog = new Dialog(context, context.getThemeResId()); if (TextUtils.isEmpty(title)) { + if (titleView != null) { + titleView.setVisibility(View.GONE); + } dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); } else { - dialog.setTitle(title); + if (titleView instanceof TextView) { + ((TextView) titleView).setText(title); + titleView.setVisibility(View.VISIBLE); + } else { + dialog.setTitle(title); + } } dialog.setContentView(childPrefScreen); dialog.setOnDismissListener(this); diff --git a/core/java/android/print/PrintServiceRecommendationsLoader.java b/core/java/android/print/PrintServiceRecommendationsLoader.java index bb5d065c6430e59aa98ff47dcded0f03eb9e3d95..c6a4d5103a479f764b9f4137c6374641f9d4d73a 100644 --- a/core/java/android/print/PrintServiceRecommendationsLoader.java +++ b/core/java/android/print/PrintServiceRecommendationsLoader.java @@ -36,7 +36,7 @@ public class PrintServiceRecommendationsLoader extends Loader> { private final @NonNull PrintManager mPrintManager; /** Handler to sequentialize the delivery of the results to the main thread */ - private Handler mHandler; + private final @NonNull Handler mHandler; /** Listens for updates to the data from the platform */ private PrintManager.PrintServicesChangeListener mListener; @@ -54,6 +54,7 @@ public class PrintServicesLoader extends Loader> { public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context, int selectionFlags) { super(Preconditions.checkNotNull(context)); + mHandler = new MyHandler(); mPrintManager = Preconditions.checkNotNull(printManager); mSelectionFlags = Preconditions.checkFlagsArgument(selectionFlags, PrintManager.ALL_SERVICES); @@ -75,7 +76,6 @@ public class PrintServicesLoader extends Loader> { @Override protected void onStartLoading() { - mHandler = new MyHandler(); mListener = new PrintManager.PrintServicesChangeListener() { @Override public void onPrintServicesChanged() { queueNewResult(); @@ -95,10 +95,7 @@ public class PrintServicesLoader extends Loader> { mListener = null; } - if (mHandler != null) { - mHandler.removeMessages(0); - mHandler = null; - } + mHandler.removeMessages(0); } @Override @@ -119,8 +116,6 @@ public class PrintServicesLoader extends Loader> { @Override public void handleMessage(Message msg) { - super.handleMessage(msg); - if (isStarted()) { deliverResult((List) msg.obj); } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index ee0db27ce66fa20c94b6c95112df017a9fd8841d..26e59ad839ffaf14d9108185f58721aa7920f6c4 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -179,6 +179,7 @@ public class CallLog { *

  • {@link #VOICEMAIL_TYPE}
  • *
  • {@link #REJECTED_TYPE}
  • *
  • {@link #BLOCKED_TYPE}
  • + *
  • {@link #ANSWERED_EXTERNALLY_TYPE}
  • * *

    */ @@ -200,7 +201,6 @@ public class CallLog { * Call log type for a call which was answered on another device. Used in situations where * a call rings on multiple devices simultaneously and it ended up being answered on a * device other than the current one. - * @hide */ public static final int ANSWERED_EXTERNALLY_TYPE = 7; @@ -234,10 +234,7 @@ public class CallLog { /** Call had video. */ public static final int FEATURES_VIDEO = 0x1; - /** - * Call was pulled externally. - * @hide - */ + /** Call was pulled externally. */ public static final int FEATURES_PULLED_EXTERNALLY = 0x2; /** diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 1158776f5fd1715af3f3399bd83039a85eabc7df..d587ba80a18c7dbfddfcbb2cf740af00d4a77452 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -235,7 +235,6 @@ public final class DocumentsContract { * @see #FLAG_DIR_PREFERS_GRID * @see #FLAG_DIR_PREFERS_LAST_MODIFIED * @see #FLAG_VIRTUAL_DOCUMENT - * @see #FLAG_ARCHIVE * @see #FLAG_SUPPORTS_COPY * @see #FLAG_SUPPORTS_MOVE * @see #FLAG_SUPPORTS_REMOVE @@ -326,7 +325,7 @@ public final class DocumentsContract { * Flag indicating that a document can be renamed. * * @see #COLUMN_FLAGS - * @see DocumentsContract#renameDocument(ContentProviderClient, Uri, + * @see DocumentsContract#renameDocument(ContentResolver, Uri, * String) * @see DocumentsProvider#renameDocument(String, String) */ @@ -337,7 +336,7 @@ public final class DocumentsContract { * within the same document provider. * * @see #COLUMN_FLAGS - * @see DocumentsContract#copyDocument(ContentProviderClient, Uri, Uri) + * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri) * @see DocumentsProvider#copyDocument(String, String) */ public static final int FLAG_SUPPORTS_COPY = 1 << 7; @@ -347,7 +346,7 @@ public final class DocumentsContract { * within the same document provider. * * @see #COLUMN_FLAGS - * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri) + * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri) * @see DocumentsProvider#moveDocument(String, String, String) */ public static final int FLAG_SUPPORTS_MOVE = 1 << 8; @@ -368,7 +367,7 @@ public final class DocumentsContract { * Flag indicating that a document can be removed from a parent. * * @see #COLUMN_FLAGS - * @see DocumentsContract#removeDocument(ContentProviderClient, Uri, Uri) + * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri) * @see DocumentsProvider#removeDocument(String, String) */ public static final int FLAG_SUPPORTS_REMOVE = 1 << 10; @@ -870,7 +869,7 @@ public final class DocumentsContract { * Test if the given URI represents a {@link Document} tree. * * @see #buildTreeDocumentUri(String, String) - * @see #getTreeDocumentId(Uri, String) + * @see #getTreeDocumentId(Uri) */ public static boolean isTreeUri(Uri uri) { final List paths = uri.getPathSegments(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 30e53f0dc76c1e09c9096daadeb9941c26216855..0645dd8af678d4d63dc3687a6e385c8e9f742fc7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -442,6 +442,22 @@ public final class Settings { public static final String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS"; + /** + * Activity Action: Show settings to allow configuration of Night display. + *

    + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + *

    + * Input: Nothing. + *

    + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NIGHT_DISPLAY_SETTINGS = + "android.settings.NIGHT_DISPLAY_SETTINGS"; + /** * Activity Action: Show settings to allow configuration of locale. *

    @@ -5478,15 +5494,6 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_DALTONIZER = "accessibility_display_daltonizer"; - /** - * Float list that specifies the color matrix to apply to - * the display. Valid values are defined in AccessibilityManager. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_COLOR_MATRIX = - "accessibility_display_color_matrix"; - /** * Setting that specifies whether automatic click when the mouse pointer stops moving is * enabled. @@ -6195,6 +6202,17 @@ public final class Settings { */ public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled"; + /** + * Specifies whether the screen will show an animation if screen contents are sent to the + * assist application (active voice interaction service). + * + * Note that the disclosure will be forced for third-party assistants or if the device + * does not support disabling it. + * + * @hide + */ + public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled"; + /** * Names of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. @@ -6336,42 +6354,39 @@ public final class Settings { "camera_double_tap_power_gesture_disabled"; /** - * Behavior of twilight on the device. - * One of {@link #TWILIGHT_MODE_LOCKED_OFF}, {@link #TWILIGHT_MODE_LOCKED_ON} - * or {@link #TWILIGHT_MODE_AUTO}. - * @hide - */ - public static final String TWILIGHT_MODE = "twilight_mode"; - - /** - * Twilight mode always off. + * Whether the camera double twist gesture to flip between front and back mode should be + * enabled. + * * @hide */ - public static final int TWILIGHT_MODE_LOCKED_OFF = 0; + public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED = + "camera_double_twist_to_flip_enabled"; /** - * Twilight mode always on. + * Control whether Night display is currently activated. * @hide */ - public static final int TWILIGHT_MODE_LOCKED_ON = 1; + public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated"; /** - * Twilight mode auto. + * Control whether Night display will automatically activate/deactivate. * @hide */ - public static final int TWILIGHT_MODE_AUTO = 2; + public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode"; /** - * Twilight mode auto, temporarily overriden to on. + * Custom time when Night display is scheduled to activate. + * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ - public static final int TWILIGHT_MODE_AUTO_OVERRIDE_OFF = 3; + public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; /** - * Twilight mode auto, temporarily overriden to off. + * Custom time when Night display is scheduled to deactivate. + * Represented as milliseconds from midnight (e.g. 21600000 == 6am). * @hide */ - public static final int TWILIGHT_MODE_AUTO_OVERRIDE_ON = 4; + public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; /** * Whether brightness should automatically adjust based on twilight state. @@ -6414,6 +6429,20 @@ public final class Settings { */ public static final int VR_DISPLAY_MODE_OFF = 1; + /** + * Whether CarrierAppUtils#disableCarrierAppsUntilPrivileged has been executed at least + * once. + * + *

    This is used to ensure that we only take one pass which will disable apps that are not + * privileged (if any). From then on, we only want to enable apps (when a matching SIM is + * inserted), to avoid disabling an app that the user might actively be using. + * + *

    Will be set to 1 once executed. + * + * @hide + */ + public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; + /** * Whether parent user can access remote contact in managed profile. * @@ -6434,12 +6463,67 @@ public final class Settings { */ public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + /** + * Whether or not the automatic storage manager is enabled and should run on the device. + * + * @hide + */ + public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED = + "automatic_storage_manager_enabled"; + + /** + * How many days of information for the automatic storage manager to retain on the device. + * + * @hide + */ + public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN = + "automatic_storage_manager_days_to_retain"; + + /** + * Default number of days of information for the automatic storage manager to retain. + * + * @hide + */ + public static final int AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT = 90; + + /** + * How many bytes the automatic storage manager has cleared out. + * + * @hide + */ + public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED = + "automatic_storage_manager_bytes_cleared"; + + + /** + * Last run time for the automatic storage manager. + * + * @hide + */ + public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN = + "automatic_storage_manager_last_run"; + + + /** + * Whether SystemUI navigation keys is enabled. + * @hide + */ + public static final String SYSTEM_NAVIGATION_KEYS_ENABLED = + "system_navigation_keys_enabled"; + /** * Holds comma separated list of ordering of QS tiles. * @hide */ public static final String QS_TILES = "sysui_qs_tiles"; + /** + * Whether preloaded APKs have been installed for the user. + * @hide + */ + public static final String DEMO_USER_SETUP_COMPLETE + = "demo_user_setup_complete"; + /** * This are the settings to be backed up. * @@ -6457,7 +6541,6 @@ public final class Settings { USB_MASS_STORAGE_ENABLED, // moved to global ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, ACCESSIBILITY_DISPLAY_DALTONIZER, - ACCESSIBILITY_DISPLAY_COLOR_MATRIX, ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, @@ -6516,7 +6599,14 @@ public final class Settings { INCALL_POWER_BUTTON_BEHAVIOR, WIFI_DISCONNECT_DELAY_DURATION, ADVANCED_REBOOT, - QS_TILES + NIGHT_DISPLAY_CUSTOM_START_TIME, + NIGHT_DISPLAY_CUSTOM_END_TIME, + NIGHT_DISPLAY_AUTO_MODE, + NIGHT_DISPLAY_ACTIVATED, + CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, + CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, + SYSTEM_NAVIGATION_KEYS_ENABLED, + QS_TILES, }; /** @@ -7532,27 +7622,54 @@ public final class Settings { public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY = "webview_data_reduction_proxy_key"; - /** - * Whether or not the WebView fallback mechanism should be enabled. - * 0=disabled, 1=enabled. - * @hide - */ - public static final String WEBVIEW_FALLBACK_LOGIC_ENABLED = - "webview_fallback_logic_enabled"; + /** + * Whether or not the WebView fallback mechanism should be enabled. + * 0=disabled, 1=enabled. + * @hide + */ + public static final String WEBVIEW_FALLBACK_LOGIC_ENABLED = + "webview_fallback_logic_enabled"; - /** - * Name of the package used as WebView provider (if unset the provider is instead determined - * by the system). - * @hide - */ - public static final String WEBVIEW_PROVIDER = "webview_provider"; + /** + * Name of the package used as WebView provider (if unset the provider is instead determined + * by the system). + * @hide + */ + public static final String WEBVIEW_PROVIDER = "webview_provider"; - /** - * Developer setting to enable WebView multiprocess rendering. - * @hide - */ - @SystemApi - public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; + /** + * Developer setting to enable WebView multiprocess rendering. + * @hide + */ + @SystemApi + public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; + + /** + * The maximum number of notifications shown in 24 hours when switching networks. + * @hide + */ + public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = + "network_switch_notification_daily_limit"; + + /** + * The minimum time in milliseconds between notifications when switching networks. + * @hide + */ + public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = + "network_switch_notification_rate_limit_millis"; + + /** + * Whether to automatically switch away from wifi networks that lose Internet access. + * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always + * avoids such networks. Valid values are: + * + * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. + * null: Ask the user whether to switch away from bad wifi. + * 1: Avoid bad wifi. + * + * @hide + */ + public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; /** * Whether Wifi display is enabled/disabled @@ -8721,8 +8838,6 @@ public final class Settings { /** * The name of the device - * - * @hide */ public static final String DEVICE_NAME = "device_name"; @@ -8805,6 +8920,24 @@ public final class Settings { public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES = "ephemeral_cookie_max_size_bytes"; + /** + * A mask applied to the ephemeral hash to generate the hash prefix. + *

    + * Type: int + * + * @hide + */ + public static final String EPHEMERAL_HASH_PREFIX_MASK = "ephemeral_hash_prefix_mask"; + + /** + * Number of hash prefixes to send during ephemeral resolution. + *

    + * Type: int + * + * @hide + */ + public static final String EPHEMERAL_HASH_PREFIX_COUNT = "ephemeral_hash_prefix_count"; + /** * The duration for caching uninstalled ephemeral apps. *

    @@ -8840,6 +8973,31 @@ public final class Settings { */ public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed"; + /** + * Whether this device is currently in retail demo mode. If true, device + * usage is severely limited. + *

    + * Type: int (0 for false, 1 for true) + * @hide + */ + public static final String DEVICE_DEMO_MODE = "device_demo_mode"; + + /** + * Retail mode specific settings. This is encoded as a key=value list, separated by commas. + * Ex: "user_inactivity_timeout_ms=30000,warning_dialog_timeout_ms=10000". The following + * keys are supported: + * + *

    +         * user_inactivity_timeout_ms  (long)
    +         * warning_dialog_timeout_ms   (long)
    +         * 
    + *

    + * Type: string + * + * @hide + */ + public static final String RETAIL_DEMO_MODE_CONSTANTS = "retail_demo_mode_constants"; + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. @@ -9290,6 +9448,12 @@ public final class Settings { * @hide */ public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate"; + + /** + * Whether cell is enabled/disabled + * @hide + */ + public static final String CELL_ON = "cell_on"; } /** diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 6a3cc0205f1812873336dbf08d29e180fb8e932e..5099eebf6eb1600000495e4ee17c6115f6282214 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -25,13 +25,11 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; -import android.database.Cursor; import android.net.Uri; import android.provider.CallLog.Calls; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.Voicemail; - import java.util.List; /** @@ -106,12 +104,74 @@ public class VoicemailContract { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SYNC_VOICEMAIL = "android.provider.action.SYNC_VOICEMAIL"; + /** + * Broadcast intent to inform a new visual voicemail SMS has been received. This intent will + * only be delivered to the voicemail client. The intent will have the following extra values: + * + *

      + *
    • {@link #EXTRA_VOICEMAIL_SMS_TYPE} - (String) The event type of the SMS. Common + * values are "SYNC" or "STATUS"
    • + *
    • {@link #EXTRA_VOICEMAIL_SMS_DATA} - (Bundle) The fields sent by the SMS
    • + *
    • {@link #EXTRA_VOICEMAIL_SMS_SUBID} - (Integer) The subscription ID of the + * phone account that received the SMS
    • + *
    + */ + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_VOICEMAIL_SMS_RECEIVED = + "android.intent.action.VOICEMAIL_SMS_RECEIVED"; + + /** + * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to + * indicate the event type of the SMS. Common values are "SYNC" or "STATUS". The extra will not + * exist if the framework cannot parse the SMS as voicemail but the carrier pattern indicates + * it is. + */ + /** @hide */ + public static final String EXTRA_VOICEMAIL_SMS_PREFIX = + "com.android.voicemail.extra.VOICEMAIL_SMS_PREFIX"; + + /** + * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to + * indicate the fields sent by the SMS. The extra will not exist if the framework cannot + * parse the SMS as voicemail but the carrier pattern indicates it is. + */ + /** @hide */ + public static final String EXTRA_VOICEMAIL_SMS_FIELDS = + "com.android.voicemail.extra.VOICEMAIL_SMS_FIELDS"; + + /** + * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate the + * message body of the SMS. This extra is included if the framework cannot + * parse the SMS as voicemail but the carrier pattern indicates it is. + */ + /** + * @hide + */ + public static final String EXTRA_VOICEMAIL_SMS_MESSAGE_BODY = + "com.android.voicemail.extra.VOICEMAIL_SMS_MESSAGE_BODY"; + + /** + * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate he + * subscription ID of the phone account that received the SMS. + */ + /** @hide */ + public static final String EXTRA_VOICEMAIL_SMS_SUBID = + "com.android.voicemail.extra.VOICEMAIL_SMS_SUBID"; + /** * Extra included in {@link Intent#ACTION_PROVIDER_CHANGED} broadcast intents to indicate if the * receiving package made this change. */ public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; + /** + * Extra included in {@link #ACTION_SYNC_VOICEMAIL} broadcast intents to indicate which {@link + * PhoneAccountHandle} to sync. + */ + public static final String EXTRA_PHONE_ACCOUNT_HANDLE = + "android.provider.extra.PHONE_ACCOUNT_HANDLE"; + /** * Name of the source package field, which must be same across all voicemail related tables. * This is an internal field. @@ -360,6 +420,20 @@ public class VoicemailContract { */ public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD; + /** + * The type of the source, which determines how to interpret source-specific states. + * Typically this will be set to the same string as + * {@link android.telephony.CarrierConfigManager#KEY_VVM_TYPE_STRING}. For example, + * "vvm_type_omtp". + * + *

    Type: TEXT

    + * + * @see #CONFIGURATION_STATE + * @see #DATA_CHANNEL_STATE + * @see #NOTIFICATION_CHANNEL_STATE + */ + public static final String SOURCE_TYPE = "source_type"; + // Note: Multiple entries may exist for a single source if they are differentiated by the // PHONE_ACCOUNT_* fields. @@ -392,21 +466,21 @@ public class VoicemailContract { public static final String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; /** * The configuration state of the voicemail source. + * + *

    Negative values are reserved to the source for source-specific states, see + * {@link #SOURCE_TYPE} + * *

    Possible values: * {@link #CONFIGURATION_STATE_OK}, * {@link #CONFIGURATION_STATE_NOT_CONFIGURED}, * {@link #CONFIGURATION_STATE_CAN_BE_CONFIGURED} + * {@link #CONFIGURATION_STATE_CONFIGURING} + * {@link #CONFIGURATION_STATE_FAILED} + * {@link #CONFIGURATION_STATE_DISABLED} *

    Type: INTEGER

    */ public static final String CONFIGURATION_STATE = "configuration_state"; - /** - * Value of {@link #CONFIGURATION_STATE} passed into - * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the - * {@link #CONFIGURATION_STATE} field is not to be changed - * - * @hide - */ - public static final int CONFIGURATION_STATE_IGNORE = -1; + /** Value of {@link #CONFIGURATION_STATE} to indicate an all OK configuration status. */ public static final int CONFIGURATION_STATE_OK = 0; /** @@ -421,9 +495,28 @@ public class VoicemailContract { * upgraded to visual voicemail and would like to show a set up invitation message. */ public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; + /** + * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail still is being + * configured. + */ + public static final int CONFIGURATION_STATE_CONFIGURING = 3; + /** + * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail has failed to + * be configured. + */ + public static final int CONFIGURATION_STATE_FAILED = 4; + /** + * Value of {@link #CONFIGURATION_STATE} to indicate that visual voicemail is disabled by + * the user. + */ + public static final int CONFIGURATION_STATE_DISABLED = 5; /** * The data channel state of the voicemail source. This the channel through which the source * pulls voicemail data from a remote server. + * + *

    Negative values are reserved to the source for source-specific states, see + * {@link #SOURCE_TYPE} + * *

    Possible values: * {@link #DATA_CHANNEL_STATE_OK}, * {@link #DATA_CHANNEL_STATE_NO_CONNECTION} @@ -431,14 +524,7 @@ public class VoicemailContract { *

    Type: INTEGER

    */ public static final String DATA_CHANNEL_STATE = "data_channel_state"; - /** - * Value of {@link #DATA_CHANNEL_STATE} passed into - * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the - * {@link #DATA_CHANNEL_STATE} field is not to be changed - * - * @hide - */ - public static final int DATA_CHANNEL_STATE_IGNORE = -1; + /** * Value of {@link #DATA_CHANNEL_STATE} to indicate that data channel is working fine. */ @@ -478,6 +564,10 @@ public class VoicemailContract { /** * The notification channel state of the voicemail source. This is the channel through which * the source gets notified of new voicemails on the remote server. + * + *

    Negative values are reserved to the source for source-specific states, see + * {@link #SOURCE_TYPE} + * *

    Possible values: * {@link #NOTIFICATION_CHANNEL_STATE_OK}, * {@link #NOTIFICATION_CHANNEL_STATE_NO_CONNECTION}, @@ -486,14 +576,7 @@ public class VoicemailContract { *

    Type: INTEGER

    */ public static final String NOTIFICATION_CHANNEL_STATE = "notification_channel_state"; - /** - * Value of {@link #NOTIFICATION_CHANNEL_STATE} passed into - * {@link #setStatus(Context, PhoneAccountHandle, int, int, int)} to indicate that the - * {@link #NOTIFICATION_CHANNEL_STATE} field is not to be changed - * - * @hide - */ - public static final int NOTIFICATION_CHANNEL_STATE_IGNORE = -1; + /** * Value of {@link #NOTIFICATION_CHANNEL_STATE} to indicate that the notification channel is * working fine. @@ -543,67 +626,5 @@ public class VoicemailContract { return Status.CONTENT_URI.buildUpon() .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build(); } - - /** - * A helper method to set the status of a voicemail source. - * - * @param context The context from the package calling the method. This will be the source. - * @param accountHandle The handle for the account the source is associated with. - * @param configurationState See {@link Status#CONFIGURATION_STATE} - * @param dataChannelState See {@link Status#DATA_CHANNEL_STATE} - * @param notificationChannelState See {@link Status#NOTIFICATION_CHANNEL_STATE} - * - * @hide - */ - public static void setStatus(Context context, PhoneAccountHandle accountHandle, - int configurationState, int dataChannelState, int notificationChannelState) { - ContentValues values = new ContentValues(); - values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, - accountHandle.getComponentName().flattenToString()); - values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId()); - if(configurationState != CONFIGURATION_STATE_IGNORE) { - values.put(Status.CONFIGURATION_STATE, configurationState); - } - if(dataChannelState != DATA_CHANNEL_STATE_IGNORE) { - values.put(Status.DATA_CHANNEL_STATE, dataChannelState); - } - if(notificationChannelState != NOTIFICATION_CHANNEL_STATE_IGNORE) { - values.put(Status.NOTIFICATION_CHANNEL_STATE, notificationChannelState); - } - ContentResolver contentResolver = context.getContentResolver(); - Uri statusUri = buildSourceUri(context.getPackageName()); - contentResolver.insert(statusUri, values); - } - - /** - * A helper method to set the quota of a voicemail source. Unit is unspecified. - * - * @param context The context from the package calling the method. This will be the source. - * @param accountHandle The handle for the account the source is associated with. - * @param occupied See {@link Status#QUOTA_OCCUPIED} - * @param total See {@link Status#QUOTA_TOTAL} - * - * @hide - */ - public static void setQuota(Context context, PhoneAccountHandle accountHandle, int occupied, - int total) { - if (occupied == QUOTA_UNAVAILABLE && total == QUOTA_UNAVAILABLE) { - return; - } - ContentValues values = new ContentValues(); - values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, - accountHandle.getComponentName().flattenToString()); - values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId()); - if (occupied != QUOTA_UNAVAILABLE) { - values.put(Status.QUOTA_OCCUPIED,occupied); - } - if (total != QUOTA_UNAVAILABLE) { - values.put(Status.QUOTA_TOTAL,total); - } - - ContentResolver contentResolver = context.getContentResolver(); - Uri statusUri = buildSourceUri(context.getPackageName()); - contentResolver.insert(statusUri, values); - } } } diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java index a70c24d6886b2fe697f13e6fe6ac71a873b105c0..b47e872d47f832a82cc569589ceb55345bb05ff0 100644 --- a/core/java/android/service/carrier/CarrierIdentifier.java +++ b/core/java/android/service/carrier/CarrierIdentifier.java @@ -126,4 +126,13 @@ public class CarrierIdentifier implements Parcelable { mGid1 = in.readString(); mGid2 = in.readString(); } + + /** @hide */ + public interface MatchType { + int ALL = 0; + int SPN = 1; + int IMSI_PREFIX = 2; + int GID1 = 3; + int GID2 = 4; + } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 6911b0161704745f00ec2cd6e159d75f6605990d..e90a3ba5435134091b170f549f6d10b99cd0b14e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -20,6 +20,8 @@ import android.app.ActivityManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.Parcel; @@ -42,6 +44,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; @@ -118,6 +121,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ZEN = "zen"; private static final String RULE_ATT_CONDITION_ID = "conditionId"; private static final String RULE_ATT_CREATION_TIME = "creationTime"; + private static final String RULE_ATT_ENABLER = "enabler"; public boolean allowCalls = DEFAULT_ALLOW_CALLS; public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; @@ -502,6 +506,7 @@ public class ZenModeConfig implements Parcelable { rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); + rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); return rt; } @@ -520,6 +525,9 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); } out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime)); + if (rule.enabler != null) { + out.attribute(null, RULE_ATT_ENABLER, rule.enabler); + } if (rule.condition != null) { writeConditionXml(rule.condition, out); } @@ -889,9 +897,13 @@ public class ZenModeConfig implements Parcelable { ", endHour=" + endHour + ", endMinute=" + endMinute + ", exitAtAlarm=" + exitAtAlarm + - ", nextAlarm=" + nextAlarm + + ", nextAlarm=" + ts(nextAlarm) + '}'; } + + protected static String ts(long time) { + return new Date(time) + " (" + time + ")"; + } } // ==== Built-in system condition: event ==== @@ -989,6 +1001,25 @@ public class ZenModeConfig implements Parcelable { return UUID.randomUUID().toString().replace("-", ""); } + private static String getOwnerCaption(Context context, String owner) { + final PackageManager pm = context.getPackageManager(); + try { + final ApplicationInfo info = pm.getApplicationInfo(owner, 0); + if (info != null) { + final CharSequence seq = info.loadLabel(pm); + if (seq != null) { + final String str = seq.toString().trim(); + if (str.length() > 0) { + return str; + } + } + } + } catch (Throwable e) { + Slog.w(TAG, "Error loading owner caption", e); + } + return ""; + } + public static String getConditionSummary(Context context, ZenModeConfig config, int userHandle, boolean shortVersion) { return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); @@ -997,23 +1028,28 @@ public class ZenModeConfig implements Parcelable { private static String getConditionLine(Context context, ZenModeConfig config, int userHandle, boolean useLine1, boolean shortVersion) { if (config == null) return ""; + String summary = ""; if (config.manualRule != null) { final Uri id = config.manualRule.conditionId; - if (id == null) { - return context.getString(com.android.internal.R.string.zen_mode_forever); - } - final long time = tryParseCountdownConditionId(id); - Condition c = config.manualRule.condition; - if (time > 0) { - final long now = System.currentTimeMillis(); - final long span = time - now; - c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), - userHandle, shortVersion); + if (config.manualRule.enabler != null) { + summary = getOwnerCaption(context, config.manualRule.enabler); + } else { + if (id == null) { + summary = context.getString(com.android.internal.R.string.zen_mode_forever); + } else { + final long time = tryParseCountdownConditionId(id); + Condition c = config.manualRule.condition; + if (time > 0) { + final long now = System.currentTimeMillis(); + final long span = time - now; + c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), + userHandle, shortVersion); + } + final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; + summary = TextUtils.isEmpty(rt) ? "" : rt; + } } - final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; - return TextUtils.isEmpty(rt) ? "" : rt; } - String summary = ""; for (ZenRule automaticRule : config.automaticRules.values()) { if (automaticRule.isAutomaticActive()) { if (summary.isEmpty()) { @@ -1023,6 +1059,7 @@ public class ZenModeConfig implements Parcelable { .getString(R.string.zen_mode_rule_name_combination, summary, automaticRule.name); } + } } return summary; @@ -1038,6 +1075,7 @@ public class ZenModeConfig implements Parcelable { public ComponentName component; // optional public String id; // required for automatic (unique) public long creationTime; // required for automatic + public String enabler; // package name, only used for manual rules. public ZenRule() { } @@ -1055,6 +1093,9 @@ public class ZenModeConfig implements Parcelable { id = source.readString(); } creationTime = source.readLong(); + if (source.readInt() == 1) { + enabler = source.readString(); + } } @Override @@ -1083,6 +1124,12 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(0); } dest.writeLong(creationTime); + if (enabler != null) { + dest.writeInt(1); + dest.writeString(enabler); + } else { + dest.writeInt(0); + } } @Override @@ -1097,6 +1144,7 @@ public class ZenModeConfig implements Parcelable { .append(",component=").append(component) .append(",id=").append(id) .append(",creationTime=").append(creationTime) + .append(",enabler=").append(enabler) .append(']').toString(); } @@ -1143,6 +1191,9 @@ public class ZenModeConfig implements Parcelable { if (creationTime != to.creationTime) { d.addLine(item, "creationTime", creationTime, to.creationTime); } + if (enabler != to.enabler) { + d.addLine(item, "enabler", enabler, to.enabler); + } } @Override @@ -1158,13 +1209,14 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.condition, condition) && Objects.equals(other.component, component) && Objects.equals(other.id, id) - && other.creationTime == creationTime; + && other.creationTime == creationTime + && Objects.equals(other.enabler, enabler); } @Override public int hashCode() { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component, id, creationTime); + component, id, creationTime, enabler); } public boolean isAutomaticActive() { diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java index cfeed51da86f2bd3e6f3edc6792556582fcabe5f..0f92ed04a457b91786150dc30c4b2b339bc1d437 100644 --- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java +++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java @@ -163,10 +163,9 @@ public class PersistentDataBlockManager { /** * Retrieves available information about this device's flash lock state. * - * @return FLASH_LOCK_STATE_LOCKED if device bootloader is locked, - * FLASH_LOCK_STATE_UNLOCKED if device bootloader is unlocked, - * or FLASH_LOCK_STATE unknown if this information cannot be ascertained - * on this device. + * @return {@link #FLASH_LOCK_LOCKED} if device bootloader is locked, + * {@link #FLASH_LOCK_UNLOCKED} if device bootloader is unlocked, or {@link #FLASH_LOCK_UNKNOWN} + * if this information cannot be ascertained on this device. */ @FlashLockState public int getFlashLockState() { diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl index bf963570b040e036e0626c3571221442d2d1a10d..d03ff93be5ceefb1e3267b6fb02b701aa23cb5c2 100644 --- a/core/java/android/service/quicksettings/IQSService.aidl +++ b/core/java/android/service/quicksettings/IQSService.aidl @@ -23,16 +23,16 @@ import android.service.quicksettings.Tile; * @hide */ interface IQSService { - Tile getTile(in ComponentName component); - void updateQsTile(in Tile tile); - void updateStatusIcon(in Tile tile, in Icon icon, + Tile getTile(in IBinder tile); + void updateQsTile(in Tile tile, in IBinder service); + void updateStatusIcon(in IBinder tile, in Icon icon, String contentDescription); - void onShowDialog(in Tile tile); - void onStartActivity(in Tile tile); + void onShowDialog(in IBinder tile); + void onStartActivity(in IBinder tile); boolean isLocked(); boolean isSecure(); - void startUnlockAndRun(in Tile tile); + void startUnlockAndRun(in IBinder tile); - void onDialogHidden(in Tile tile); - void onStartSuccessful(in Tile tile); + void onDialogHidden(in IBinder tile); + void onStartSuccessful(in IBinder tile); } diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java index 3d7d53ea67e8a2bb9d6f5455ff7ae534bddb2e5c..4b81a726085227f32aef95fb8f53b08783a04cb1 100644 --- a/core/java/android/service/quicksettings/Tile.java +++ b/core/java/android/service/quicksettings/Tile.java @@ -15,8 +15,8 @@ */ package android.service.quicksettings; -import android.content.ComponentName; import android.graphics.drawable.Icon; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -59,7 +59,7 @@ public final class Tile implements Parcelable { */ public static final int STATE_ACTIVE = 2; - private ComponentName mComponentName; + private IBinder mToken; private Icon mIcon; private CharSequence mLabel; private CharSequence mContentDescription; @@ -78,29 +78,15 @@ public final class Tile implements Parcelable { /** * @hide */ - public Tile(ComponentName componentName) { - mComponentName = componentName; + public Tile() { } /** * @hide */ - public void setService(IQSService service) { + public void setService(IQSService service, IBinder stub) { mService = service; - } - - /** - * @hide - */ - public ComponentName getComponentName() { - return mComponentName; - } - - /** - * @hide - */ - public IQSService getQsService() { - return mService; + mToken = stub; } /** @@ -193,7 +179,7 @@ public final class Tile implements Parcelable { */ public void updateTile() { try { - mService.updateQsTile(this); + mService.updateQsTile(this, mToken); } catch (RemoteException e) { Log.e(TAG, "Couldn't update tile"); } @@ -201,12 +187,6 @@ public final class Tile implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - if (mComponentName != null) { - dest.writeByte((byte) 1); - mComponentName.writeToParcel(dest, flags); - } else { - dest.writeByte((byte) 0); - } if (mIcon != null) { dest.writeByte((byte) 1); mIcon.writeToParcel(dest, flags); @@ -219,11 +199,6 @@ public final class Tile implements Parcelable { } private void readFromParcel(Parcel source) { - if (source.readByte() != 0) { - mComponentName = ComponentName.CREATOR.createFromParcel(source); - } else { - mComponentName = null; - } if (source.readByte() != 0) { mIcon = Icon.CREATOR.createFromParcel(source); } else { diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index 50411ab7d15e926f1286ed4ed13073f20f2cfc35..887f4b6577b9171fb587cce22807dc3bf33b96ed 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -120,6 +120,11 @@ public class TileService extends Service { */ public static final String EXTRA_SERVICE = "service"; + /** + * @hide + */ + public static final String EXTRA_TOKEN = "token"; + /** * @hide */ @@ -132,6 +137,7 @@ public class TileService extends Service { private IBinder mToken; private IQSService mService; private Runnable mUnlockRunnable; + private IBinder mTileToken; @Override public void onDestroy() { @@ -197,7 +203,7 @@ public class TileService extends Service { public final void setStatusIcon(Icon icon, String contentDescription) { if (mService != null) { try { - mService.updateStatusIcon(mTile, icon, contentDescription); + mService.updateStatusIcon(mTileToken, icon, contentDescription); } catch (RemoteException e) { } } @@ -224,14 +230,14 @@ public class TileService extends Service { @Override public void onViewDetachedFromWindow(View v) { try { - mService.onDialogHidden(getQsTile()); + mService.onDialogHidden(mTileToken); } catch (RemoteException e) { } } }); dialog.show(); try { - mService.onShowDialog(mTile); + mService.onShowDialog(mTileToken); } catch (RemoteException e) { } } @@ -246,7 +252,7 @@ public class TileService extends Service { public final void unlockAndRun(Runnable runnable) { mUnlockRunnable = runnable; try { - mService.startUnlockAndRun(mTile); + mService.startUnlockAndRun(mTileToken); } catch (RemoteException e) { } } @@ -292,7 +298,7 @@ public class TileService extends Service { public final void startActivityAndCollapse(Intent intent) { startActivity(intent); try { - mService.onStartActivity(mTile); + mService.onStartActivity(mTileToken); } catch (RemoteException e) { } } @@ -311,14 +317,14 @@ public class TileService extends Service { @Override public IBinder onBind(Intent intent) { mService = IQSService.Stub.asInterface(intent.getIBinderExtra(EXTRA_SERVICE)); + mTileToken = intent.getIBinderExtra(EXTRA_TOKEN); try { - ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT); - mTile = mService.getTile(component); + mTile = mService.getTile(mTileToken); } catch (RemoteException e) { throw new RuntimeException("Unable to reach IQSService", e); } if (mTile != null) { - mTile.setService(mService); + mTile.setService(mService, mTileToken); mHandler.sendEmptyMessage(H.MSG_START_SUCCESS); } return new IQSTileService.Stub() { @@ -403,7 +409,7 @@ public class TileService extends Service { break; case MSG_START_SUCCESS: try { - mService.onStartSuccessful(mTile); + mService.onStartSuccessful(mTileToken); } catch (RemoteException e) { } break; diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index de8133b33aa6fa6cf83a04b07d814310658fe98d..06d87f84699d1052f52a60d83e3747f9fe5173b3 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -55,6 +55,7 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; import java.io.FileDescriptor; @@ -628,9 +629,9 @@ public abstract class WallpaperService extends Service { mCurWindowFlags = mWindowFlags; mLayout.flags = mWindowFlags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - ; + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mCurWindowPrivateFlags = mWindowPrivateFlags; mLayout.privateFlags = mWindowPrivateFlags; diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java index b62cc66b52690f8752f506d685da640361e47564..b6d720d453e9eebf6f7e9ada6533b20e68e56119 100644 --- a/core/java/android/text/Emoji.java +++ b/core/java/android/text/Emoji.java @@ -32,111 +32,112 @@ public class Emoji { 0x23EC, 0x23ED, 0x23EE, 0x23EF, 0x23F0, 0x23F1, 0x23F2, 0x23F3, 0x23F8, 0x23F9, 0x23FA, 0x24C2, 0x25AA, 0x25AB, 0x25B6, 0x25C0, 0x25FB, 0x25FC, 0x25FD, 0x25FE, 0x2600, 0x2601, 0x2602, 0x2603, 0x2604, 0x260E, 0x2611, 0x2614, 0x2615, 0x2618, 0x261D, 0x2620, 0x2622, - 0x2623, 0x2626, 0x262A, 0x262E, 0x262F, 0x2638, 0x2639, 0x263A, 0x2648, 0x2649, 0x264A, - 0x264B, 0x264C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653, 0x2660, 0x2663, - 0x2665, 0x2666, 0x2668, 0x267B, 0x267F, 0x2692, 0x2693, 0x2694, 0x2696, 0x2697, 0x2699, - 0x269B, 0x269C, 0x26A0, 0x26A1, 0x26AA, 0x26AB, 0x26B0, 0x26B1, 0x26BD, 0x26BE, 0x26C4, - 0x26C5, 0x26C8, 0x26CE, 0x26CF, 0x26D1, 0x26D3, 0x26D4, 0x26E9, 0x26EA, 0x26F0, 0x26F1, - 0x26F2, 0x26F3, 0x26F4, 0x26F5, 0x26F7, 0x26F8, 0x26F9, 0x26FA, 0x26FD, 0x2702, 0x2705, - 0x2708, 0x2709, 0x270A, 0x270B, 0x270C, 0x270D, 0x270F, 0x2712, 0x2714, 0x2716, 0x271D, - 0x2721, 0x2728, 0x2733, 0x2734, 0x2744, 0x2747, 0x274C, 0x274E, 0x2753, 0x2754, 0x2755, - 0x2757, 0x2763, 0x2764, 0x2795, 0x2796, 0x2797, 0x27A1, 0x27B0, 0x27BF, 0x2934, 0x2935, - 0x2B05, 0x2B06, 0x2B07, 0x2B1B, 0x2B1C, 0x2B50, 0x2B55, 0x3030, 0x303D, 0x3297, 0x3299, - 0x1F004, 0x1F0CF, 0x1F170, 0x1F171, 0x1F17E, 0x1F17F, 0x1F18E, 0x1F191, 0x1F192, 0x1F193, - 0x1F194, 0x1F195, 0x1F196, 0x1F197, 0x1F198, 0x1F199, 0x1F19A, 0x1F1E6, 0x1F1E7, 0x1F1E8, - 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1ED, 0x1F1EE, 0x1F1EF, 0x1F1F0, 0x1F1F1, 0x1F1F2, - 0x1F1F3, 0x1F1F4, 0x1F1F5, 0x1F1F6, 0x1F1F7, 0x1F1F8, 0x1F1F9, 0x1F1FA, 0x1F1FB, 0x1F1FC, - 0x1F1FD, 0x1F1FE, 0x1F1FF, 0x1F201, 0x1F202, 0x1F21A, 0x1F22F, 0x1F232, 0x1F233, 0x1F234, - 0x1F235, 0x1F236, 0x1F237, 0x1F238, 0x1F239, 0x1F23A, 0x1F250, 0x1F251, 0x1F300, 0x1F301, - 0x1F302, 0x1F303, 0x1F304, 0x1F305, 0x1F306, 0x1F307, 0x1F308, 0x1F309, 0x1F30A, 0x1F30B, - 0x1F30C, 0x1F30D, 0x1F30E, 0x1F30F, 0x1F310, 0x1F311, 0x1F312, 0x1F313, 0x1F314, 0x1F315, - 0x1F316, 0x1F317, 0x1F318, 0x1F319, 0x1F31A, 0x1F31B, 0x1F31C, 0x1F31D, 0x1F31E, 0x1F31F, - 0x1F320, 0x1F321, 0x1F324, 0x1F325, 0x1F326, 0x1F327, 0x1F328, 0x1F329, 0x1F32A, 0x1F32B, - 0x1F32C, 0x1F32D, 0x1F32E, 0x1F32F, 0x1F330, 0x1F331, 0x1F332, 0x1F333, 0x1F334, 0x1F335, - 0x1F336, 0x1F337, 0x1F338, 0x1F339, 0x1F33A, 0x1F33B, 0x1F33C, 0x1F33D, 0x1F33E, 0x1F33F, - 0x1F340, 0x1F341, 0x1F342, 0x1F343, 0x1F344, 0x1F345, 0x1F346, 0x1F347, 0x1F348, 0x1F349, - 0x1F34A, 0x1F34B, 0x1F34C, 0x1F34D, 0x1F34E, 0x1F34F, 0x1F350, 0x1F351, 0x1F352, 0x1F353, - 0x1F354, 0x1F355, 0x1F356, 0x1F357, 0x1F358, 0x1F359, 0x1F35A, 0x1F35B, 0x1F35C, 0x1F35D, - 0x1F35E, 0x1F35F, 0x1F360, 0x1F361, 0x1F362, 0x1F363, 0x1F364, 0x1F365, 0x1F366, 0x1F367, - 0x1F368, 0x1F369, 0x1F36A, 0x1F36B, 0x1F36C, 0x1F36D, 0x1F36E, 0x1F36F, 0x1F370, 0x1F371, - 0x1F372, 0x1F373, 0x1F374, 0x1F375, 0x1F376, 0x1F377, 0x1F378, 0x1F379, 0x1F37A, 0x1F37B, - 0x1F37C, 0x1F37D, 0x1F37E, 0x1F37F, 0x1F380, 0x1F381, 0x1F382, 0x1F383, 0x1F384, 0x1F385, - 0x1F386, 0x1F387, 0x1F388, 0x1F389, 0x1F38A, 0x1F38B, 0x1F38C, 0x1F38D, 0x1F38E, 0x1F38F, - 0x1F390, 0x1F391, 0x1F392, 0x1F393, 0x1F396, 0x1F397, 0x1F399, 0x1F39A, 0x1F39B, 0x1F39E, - 0x1F39F, 0x1F3A0, 0x1F3A1, 0x1F3A2, 0x1F3A3, 0x1F3A4, 0x1F3A5, 0x1F3A6, 0x1F3A7, 0x1F3A8, - 0x1F3A9, 0x1F3AA, 0x1F3AB, 0x1F3AC, 0x1F3AD, 0x1F3AE, 0x1F3AF, 0x1F3B0, 0x1F3B1, 0x1F3B2, - 0x1F3B3, 0x1F3B4, 0x1F3B5, 0x1F3B6, 0x1F3B7, 0x1F3B8, 0x1F3B9, 0x1F3BA, 0x1F3BB, 0x1F3BC, - 0x1F3BD, 0x1F3BE, 0x1F3BF, 0x1F3C0, 0x1F3C1, 0x1F3C2, 0x1F3C3, 0x1F3C4, 0x1F3C5, 0x1F3C6, - 0x1F3C7, 0x1F3C8, 0x1F3C9, 0x1F3CA, 0x1F3CB, 0x1F3CC, 0x1F3CD, 0x1F3CE, 0x1F3CF, 0x1F3D0, - 0x1F3D1, 0x1F3D2, 0x1F3D3, 0x1F3D4, 0x1F3D5, 0x1F3D6, 0x1F3D7, 0x1F3D8, 0x1F3D9, 0x1F3DA, - 0x1F3DB, 0x1F3DC, 0x1F3DD, 0x1F3DE, 0x1F3DF, 0x1F3E0, 0x1F3E1, 0x1F3E2, 0x1F3E3, 0x1F3E4, - 0x1F3E5, 0x1F3E6, 0x1F3E7, 0x1F3E8, 0x1F3E9, 0x1F3EA, 0x1F3EB, 0x1F3EC, 0x1F3ED, 0x1F3EE, - 0x1F3EF, 0x1F3F0, 0x1F3F3, 0x1F3F4, 0x1F3F5, 0x1F3F7, 0x1F3F8, 0x1F3F9, 0x1F3FA, 0x1F3FB, - 0x1F3FC, 0x1F3FD, 0x1F3FE, 0x1F3FF, 0x1F400, 0x1F401, 0x1F402, 0x1F403, 0x1F404, 0x1F405, - 0x1F406, 0x1F407, 0x1F408, 0x1F409, 0x1F40A, 0x1F40B, 0x1F40C, 0x1F40D, 0x1F40E, 0x1F40F, - 0x1F410, 0x1F411, 0x1F412, 0x1F413, 0x1F414, 0x1F415, 0x1F416, 0x1F417, 0x1F418, 0x1F419, - 0x1F41A, 0x1F41B, 0x1F41C, 0x1F41D, 0x1F41E, 0x1F41F, 0x1F420, 0x1F421, 0x1F422, 0x1F423, - 0x1F424, 0x1F425, 0x1F426, 0x1F427, 0x1F428, 0x1F429, 0x1F42A, 0x1F42B, 0x1F42C, 0x1F42D, - 0x1F42E, 0x1F42F, 0x1F430, 0x1F431, 0x1F432, 0x1F433, 0x1F434, 0x1F435, 0x1F436, 0x1F437, - 0x1F438, 0x1F439, 0x1F43A, 0x1F43B, 0x1F43C, 0x1F43D, 0x1F43E, 0x1F43F, 0x1F440, 0x1F441, - 0x1F442, 0x1F443, 0x1F444, 0x1F445, 0x1F446, 0x1F447, 0x1F448, 0x1F449, 0x1F44A, 0x1F44B, - 0x1F44C, 0x1F44D, 0x1F44E, 0x1F44F, 0x1F450, 0x1F451, 0x1F452, 0x1F453, 0x1F454, 0x1F455, - 0x1F456, 0x1F457, 0x1F458, 0x1F459, 0x1F45A, 0x1F45B, 0x1F45C, 0x1F45D, 0x1F45E, 0x1F45F, - 0x1F460, 0x1F461, 0x1F462, 0x1F463, 0x1F464, 0x1F465, 0x1F466, 0x1F467, 0x1F468, 0x1F469, - 0x1F46A, 0x1F46B, 0x1F46C, 0x1F46D, 0x1F46E, 0x1F46F, 0x1F470, 0x1F471, 0x1F472, 0x1F473, - 0x1F474, 0x1F475, 0x1F476, 0x1F477, 0x1F478, 0x1F479, 0x1F47A, 0x1F47B, 0x1F47C, 0x1F47D, - 0x1F47E, 0x1F47F, 0x1F480, 0x1F481, 0x1F482, 0x1F483, 0x1F484, 0x1F485, 0x1F486, 0x1F487, - 0x1F488, 0x1F489, 0x1F48A, 0x1F48B, 0x1F48C, 0x1F48D, 0x1F48E, 0x1F48F, 0x1F490, 0x1F491, - 0x1F492, 0x1F493, 0x1F494, 0x1F495, 0x1F496, 0x1F497, 0x1F498, 0x1F499, 0x1F49A, 0x1F49B, - 0x1F49C, 0x1F49D, 0x1F49E, 0x1F49F, 0x1F4A0, 0x1F4A1, 0x1F4A2, 0x1F4A3, 0x1F4A4, 0x1F4A5, - 0x1F4A6, 0x1F4A7, 0x1F4A8, 0x1F4A9, 0x1F4AA, 0x1F4AB, 0x1F4AC, 0x1F4AD, 0x1F4AE, 0x1F4AF, - 0x1F4B0, 0x1F4B1, 0x1F4B2, 0x1F4B3, 0x1F4B4, 0x1F4B5, 0x1F4B6, 0x1F4B7, 0x1F4B8, 0x1F4B9, - 0x1F4BA, 0x1F4BB, 0x1F4BC, 0x1F4BD, 0x1F4BE, 0x1F4BF, 0x1F4C0, 0x1F4C1, 0x1F4C2, 0x1F4C3, - 0x1F4C4, 0x1F4C5, 0x1F4C6, 0x1F4C7, 0x1F4C8, 0x1F4C9, 0x1F4CA, 0x1F4CB, 0x1F4CC, 0x1F4CD, - 0x1F4CE, 0x1F4CF, 0x1F4D0, 0x1F4D1, 0x1F4D2, 0x1F4D3, 0x1F4D4, 0x1F4D5, 0x1F4D6, 0x1F4D7, - 0x1F4D8, 0x1F4D9, 0x1F4DA, 0x1F4DB, 0x1F4DC, 0x1F4DD, 0x1F4DE, 0x1F4DF, 0x1F4E0, 0x1F4E1, - 0x1F4E2, 0x1F4E3, 0x1F4E4, 0x1F4E5, 0x1F4E6, 0x1F4E7, 0x1F4E8, 0x1F4E9, 0x1F4EA, 0x1F4EB, - 0x1F4EC, 0x1F4ED, 0x1F4EE, 0x1F4EF, 0x1F4F0, 0x1F4F1, 0x1F4F2, 0x1F4F3, 0x1F4F4, 0x1F4F5, - 0x1F4F6, 0x1F4F7, 0x1F4F8, 0x1F4F9, 0x1F4FA, 0x1F4FB, 0x1F4FC, 0x1F4FD, 0x1F4FF, 0x1F500, - 0x1F501, 0x1F502, 0x1F503, 0x1F504, 0x1F505, 0x1F506, 0x1F507, 0x1F508, 0x1F509, 0x1F50A, - 0x1F50B, 0x1F50C, 0x1F50D, 0x1F50E, 0x1F50F, 0x1F510, 0x1F511, 0x1F512, 0x1F513, 0x1F514, - 0x1F515, 0x1F516, 0x1F517, 0x1F518, 0x1F519, 0x1F51A, 0x1F51B, 0x1F51C, 0x1F51D, 0x1F51E, - 0x1F51F, 0x1F520, 0x1F521, 0x1F522, 0x1F523, 0x1F524, 0x1F525, 0x1F526, 0x1F527, 0x1F528, - 0x1F529, 0x1F52A, 0x1F52B, 0x1F52C, 0x1F52D, 0x1F52E, 0x1F52F, 0x1F530, 0x1F531, 0x1F532, - 0x1F533, 0x1F534, 0x1F535, 0x1F536, 0x1F537, 0x1F538, 0x1F539, 0x1F53A, 0x1F53B, 0x1F53C, - 0x1F53D, 0x1F549, 0x1F54A, 0x1F54B, 0x1F54C, 0x1F54D, 0x1F54E, 0x1F550, 0x1F551, 0x1F552, - 0x1F553, 0x1F554, 0x1F555, 0x1F556, 0x1F557, 0x1F558, 0x1F559, 0x1F55A, 0x1F55B, 0x1F55C, - 0x1F55D, 0x1F55E, 0x1F55F, 0x1F560, 0x1F561, 0x1F562, 0x1F563, 0x1F564, 0x1F565, 0x1F566, - 0x1F567, 0x1F56F, 0x1F570, 0x1F573, 0x1F574, 0x1F575, 0x1F576, 0x1F577, 0x1F578, 0x1F579, - 0x1F57A, 0x1F587, 0x1F58A, 0x1F58B, 0x1F58C, 0x1F58D, 0x1F590, 0x1F595, 0x1F596, 0x1F5A4, - 0x1F5A5, 0x1F5A8, 0x1F5B1, 0x1F5B2, 0x1F5BC, 0x1F5C2, 0x1F5C3, 0x1F5C4, 0x1F5D1, 0x1F5D2, - 0x1F5D3, 0x1F5DC, 0x1F5DD, 0x1F5DE, 0x1F5E1, 0x1F5E3, 0x1F5E8, 0x1F5EF, 0x1F5F3, 0x1F5FA, - 0x1F5FB, 0x1F5FC, 0x1F5FD, 0x1F5FE, 0x1F5FF, 0x1F600, 0x1F601, 0x1F602, 0x1F603, 0x1F604, - 0x1F605, 0x1F606, 0x1F607, 0x1F608, 0x1F609, 0x1F60A, 0x1F60B, 0x1F60C, 0x1F60D, 0x1F60E, - 0x1F60F, 0x1F610, 0x1F611, 0x1F612, 0x1F613, 0x1F614, 0x1F615, 0x1F616, 0x1F617, 0x1F618, - 0x1F619, 0x1F61A, 0x1F61B, 0x1F61C, 0x1F61D, 0x1F61E, 0x1F61F, 0x1F620, 0x1F621, 0x1F622, - 0x1F623, 0x1F624, 0x1F625, 0x1F626, 0x1F627, 0x1F628, 0x1F629, 0x1F62A, 0x1F62B, 0x1F62C, - 0x1F62D, 0x1F62E, 0x1F62F, 0x1F630, 0x1F631, 0x1F632, 0x1F633, 0x1F634, 0x1F635, 0x1F636, - 0x1F637, 0x1F638, 0x1F639, 0x1F63A, 0x1F63B, 0x1F63C, 0x1F63D, 0x1F63E, 0x1F63F, 0x1F640, - 0x1F641, 0x1F642, 0x1F643, 0x1F644, 0x1F645, 0x1F646, 0x1F647, 0x1F648, 0x1F649, 0x1F64A, - 0x1F64B, 0x1F64C, 0x1F64D, 0x1F64E, 0x1F64F, 0x1F680, 0x1F681, 0x1F682, 0x1F683, 0x1F684, - 0x1F685, 0x1F686, 0x1F687, 0x1F688, 0x1F689, 0x1F68A, 0x1F68B, 0x1F68C, 0x1F68D, 0x1F68E, - 0x1F68F, 0x1F690, 0x1F691, 0x1F692, 0x1F693, 0x1F694, 0x1F695, 0x1F696, 0x1F697, 0x1F698, - 0x1F699, 0x1F69A, 0x1F69B, 0x1F69C, 0x1F69D, 0x1F69E, 0x1F69F, 0x1F6A0, 0x1F6A1, 0x1F6A2, - 0x1F6A3, 0x1F6A4, 0x1F6A5, 0x1F6A6, 0x1F6A7, 0x1F6A8, 0x1F6A9, 0x1F6AA, 0x1F6AB, 0x1F6AC, - 0x1F6AD, 0x1F6AE, 0x1F6AF, 0x1F6B0, 0x1F6B1, 0x1F6B2, 0x1F6B3, 0x1F6B4, 0x1F6B5, 0x1F6B6, - 0x1F6B7, 0x1F6B8, 0x1F6B9, 0x1F6BA, 0x1F6BB, 0x1F6BC, 0x1F6BD, 0x1F6BE, 0x1F6BF, 0x1F6C0, - 0x1F6C1, 0x1F6C2, 0x1F6C3, 0x1F6C4, 0x1F6C5, 0x1F6CB, 0x1F6CC, 0x1F6CD, 0x1F6CE, 0x1F6CF, - 0x1F6D0, 0x1F6D1, 0x1F6D2, 0x1F6E0, 0x1F6E1, 0x1F6E2, 0x1F6E3, 0x1F6E4, 0x1F6E5, 0x1F6E9, - 0x1F6EB, 0x1F6EC, 0x1F6F0, 0x1F6F3, 0x1F6F4, 0x1F6F5, 0x1F6F6, 0x1F910, 0x1F911, 0x1F912, - 0x1F913, 0x1F914, 0x1F915, 0x1F916, 0x1F917, 0x1F918, 0x1F919, 0x1F91A, 0x1F91B, 0x1F91C, - 0x1F91D, 0x1F91E, 0x1F920, 0x1F921, 0x1F922, 0x1F923, 0x1F924, 0x1F925, 0x1F926, 0x1F927, - 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938, 0x1F939, 0x1F93A, 0x1F93B, - 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943, 0x1F944, 0x1F945, 0x1F946, - 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951, 0x1F952, 0x1F953, 0x1F954, - 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B, 0x1F95C, 0x1F95D, 0x1F95E, - 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986, 0x1F987, 0x1F988, 0x1F989, - 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990, 0x1F991, 0x1F9C0 + 0x2623, 0x2626, 0x262A, 0x262E, 0x262F, 0x2638, 0x2639, 0x263A, 0x2640, 0x2642, 0x2648, + 0x2649, 0x264A, 0x264B, 0x264C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653, + 0x2660, 0x2663, 0x2665, 0x2666, 0x2668, 0x267B, 0x267F, 0x2692, 0x2693, 0x2694, 0x2695, + 0x2696, 0x2697, 0x2699, 0x269B, 0x269C, 0x26A0, 0x26A1, 0x26AA, 0x26AB, 0x26B0, 0x26B1, + 0x26BD, 0x26BE, 0x26C4, 0x26C5, 0x26C8, 0x26CE, 0x26CF, 0x26D1, 0x26D3, 0x26D4, 0x26E9, + 0x26EA, 0x26F0, 0x26F1, 0x26F2, 0x26F3, 0x26F4, 0x26F5, 0x26F7, 0x26F8, 0x26F9, 0x26FA, + 0x26FD, 0x2702, 0x2705, 0x2708, 0x2709, 0x270A, 0x270B, 0x270C, 0x270D, 0x270F, 0x2712, + 0x2714, 0x2716, 0x271D, 0x2721, 0x2728, 0x2733, 0x2734, 0x2744, 0x2747, 0x274C, 0x274E, + 0x2753, 0x2754, 0x2755, 0x2757, 0x2763, 0x2764, 0x2795, 0x2796, 0x2797, 0x27A1, 0x27B0, + 0x27BF, 0x2934, 0x2935, 0x2B05, 0x2B06, 0x2B07, 0x2B1B, 0x2B1C, 0x2B50, 0x2B55, 0x3030, + 0x303D, 0x3297, 0x3299, 0x1F004, 0x1F0CF, 0x1F170, 0x1F171, 0x1F17E, 0x1F17F, 0x1F18E, + 0x1F191, 0x1F192, 0x1F193, 0x1F194, 0x1F195, 0x1F196, 0x1F197, 0x1F198, 0x1F199, 0x1F19A, + 0x1F1E6, 0x1F1E7, 0x1F1E8, 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1ED, 0x1F1EE, 0x1F1EF, + 0x1F1F0, 0x1F1F1, 0x1F1F2, 0x1F1F3, 0x1F1F4, 0x1F1F5, 0x1F1F6, 0x1F1F7, 0x1F1F8, 0x1F1F9, + 0x1F1FA, 0x1F1FB, 0x1F1FC, 0x1F1FD, 0x1F1FE, 0x1F1FF, 0x1F201, 0x1F202, 0x1F21A, 0x1F22F, + 0x1F232, 0x1F233, 0x1F234, 0x1F235, 0x1F236, 0x1F237, 0x1F238, 0x1F239, 0x1F23A, 0x1F250, + 0x1F251, 0x1F300, 0x1F301, 0x1F302, 0x1F303, 0x1F304, 0x1F305, 0x1F306, 0x1F307, 0x1F308, + 0x1F309, 0x1F30A, 0x1F30B, 0x1F30C, 0x1F30D, 0x1F30E, 0x1F30F, 0x1F310, 0x1F311, 0x1F312, + 0x1F313, 0x1F314, 0x1F315, 0x1F316, 0x1F317, 0x1F318, 0x1F319, 0x1F31A, 0x1F31B, 0x1F31C, + 0x1F31D, 0x1F31E, 0x1F31F, 0x1F320, 0x1F321, 0x1F324, 0x1F325, 0x1F326, 0x1F327, 0x1F328, + 0x1F329, 0x1F32A, 0x1F32B, 0x1F32C, 0x1F32D, 0x1F32E, 0x1F32F, 0x1F330, 0x1F331, 0x1F332, + 0x1F333, 0x1F334, 0x1F335, 0x1F336, 0x1F337, 0x1F338, 0x1F339, 0x1F33A, 0x1F33B, 0x1F33C, + 0x1F33D, 0x1F33E, 0x1F33F, 0x1F340, 0x1F341, 0x1F342, 0x1F343, 0x1F344, 0x1F345, 0x1F346, + 0x1F347, 0x1F348, 0x1F349, 0x1F34A, 0x1F34B, 0x1F34C, 0x1F34D, 0x1F34E, 0x1F34F, 0x1F350, + 0x1F351, 0x1F352, 0x1F353, 0x1F354, 0x1F355, 0x1F356, 0x1F357, 0x1F358, 0x1F359, 0x1F35A, + 0x1F35B, 0x1F35C, 0x1F35D, 0x1F35E, 0x1F35F, 0x1F360, 0x1F361, 0x1F362, 0x1F363, 0x1F364, + 0x1F365, 0x1F366, 0x1F367, 0x1F368, 0x1F369, 0x1F36A, 0x1F36B, 0x1F36C, 0x1F36D, 0x1F36E, + 0x1F36F, 0x1F370, 0x1F371, 0x1F372, 0x1F373, 0x1F374, 0x1F375, 0x1F376, 0x1F377, 0x1F378, + 0x1F379, 0x1F37A, 0x1F37B, 0x1F37C, 0x1F37D, 0x1F37E, 0x1F37F, 0x1F380, 0x1F381, 0x1F382, + 0x1F383, 0x1F384, 0x1F385, 0x1F386, 0x1F387, 0x1F388, 0x1F389, 0x1F38A, 0x1F38B, 0x1F38C, + 0x1F38D, 0x1F38E, 0x1F38F, 0x1F390, 0x1F391, 0x1F392, 0x1F393, 0x1F396, 0x1F397, 0x1F399, + 0x1F39A, 0x1F39B, 0x1F39E, 0x1F39F, 0x1F3A0, 0x1F3A1, 0x1F3A2, 0x1F3A3, 0x1F3A4, 0x1F3A5, + 0x1F3A6, 0x1F3A7, 0x1F3A8, 0x1F3A9, 0x1F3AA, 0x1F3AB, 0x1F3AC, 0x1F3AD, 0x1F3AE, 0x1F3AF, + 0x1F3B0, 0x1F3B1, 0x1F3B2, 0x1F3B3, 0x1F3B4, 0x1F3B5, 0x1F3B6, 0x1F3B7, 0x1F3B8, 0x1F3B9, + 0x1F3BA, 0x1F3BB, 0x1F3BC, 0x1F3BD, 0x1F3BE, 0x1F3BF, 0x1F3C0, 0x1F3C1, 0x1F3C2, 0x1F3C3, + 0x1F3C4, 0x1F3C5, 0x1F3C6, 0x1F3C7, 0x1F3C8, 0x1F3C9, 0x1F3CA, 0x1F3CB, 0x1F3CC, 0x1F3CD, + 0x1F3CE, 0x1F3CF, 0x1F3D0, 0x1F3D1, 0x1F3D2, 0x1F3D3, 0x1F3D4, 0x1F3D5, 0x1F3D6, 0x1F3D7, + 0x1F3D8, 0x1F3D9, 0x1F3DA, 0x1F3DB, 0x1F3DC, 0x1F3DD, 0x1F3DE, 0x1F3DF, 0x1F3E0, 0x1F3E1, + 0x1F3E2, 0x1F3E3, 0x1F3E4, 0x1F3E5, 0x1F3E6, 0x1F3E7, 0x1F3E8, 0x1F3E9, 0x1F3EA, 0x1F3EB, + 0x1F3EC, 0x1F3ED, 0x1F3EE, 0x1F3EF, 0x1F3F0, 0x1F3F3, 0x1F3F4, 0x1F3F5, 0x1F3F7, 0x1F3F8, + 0x1F3F9, 0x1F3FA, 0x1F3FB, 0x1F3FC, 0x1F3FD, 0x1F3FE, 0x1F3FF, 0x1F400, 0x1F401, 0x1F402, + 0x1F403, 0x1F404, 0x1F405, 0x1F406, 0x1F407, 0x1F408, 0x1F409, 0x1F40A, 0x1F40B, 0x1F40C, + 0x1F40D, 0x1F40E, 0x1F40F, 0x1F410, 0x1F411, 0x1F412, 0x1F413, 0x1F414, 0x1F415, 0x1F416, + 0x1F417, 0x1F418, 0x1F419, 0x1F41A, 0x1F41B, 0x1F41C, 0x1F41D, 0x1F41E, 0x1F41F, 0x1F420, + 0x1F421, 0x1F422, 0x1F423, 0x1F424, 0x1F425, 0x1F426, 0x1F427, 0x1F428, 0x1F429, 0x1F42A, + 0x1F42B, 0x1F42C, 0x1F42D, 0x1F42E, 0x1F42F, 0x1F430, 0x1F431, 0x1F432, 0x1F433, 0x1F434, + 0x1F435, 0x1F436, 0x1F437, 0x1F438, 0x1F439, 0x1F43A, 0x1F43B, 0x1F43C, 0x1F43D, 0x1F43E, + 0x1F43F, 0x1F440, 0x1F441, 0x1F442, 0x1F443, 0x1F444, 0x1F445, 0x1F446, 0x1F447, 0x1F448, + 0x1F449, 0x1F44A, 0x1F44B, 0x1F44C, 0x1F44D, 0x1F44E, 0x1F44F, 0x1F450, 0x1F451, 0x1F452, + 0x1F453, 0x1F454, 0x1F455, 0x1F456, 0x1F457, 0x1F458, 0x1F459, 0x1F45A, 0x1F45B, 0x1F45C, + 0x1F45D, 0x1F45E, 0x1F45F, 0x1F460, 0x1F461, 0x1F462, 0x1F463, 0x1F464, 0x1F465, 0x1F466, + 0x1F467, 0x1F468, 0x1F469, 0x1F46A, 0x1F46B, 0x1F46C, 0x1F46D, 0x1F46E, 0x1F46F, 0x1F470, + 0x1F471, 0x1F472, 0x1F473, 0x1F474, 0x1F475, 0x1F476, 0x1F477, 0x1F478, 0x1F479, 0x1F47A, + 0x1F47B, 0x1F47C, 0x1F47D, 0x1F47E, 0x1F47F, 0x1F480, 0x1F481, 0x1F482, 0x1F483, 0x1F484, + 0x1F485, 0x1F486, 0x1F487, 0x1F488, 0x1F489, 0x1F48A, 0x1F48B, 0x1F48C, 0x1F48D, 0x1F48E, + 0x1F48F, 0x1F490, 0x1F491, 0x1F492, 0x1F493, 0x1F494, 0x1F495, 0x1F496, 0x1F497, 0x1F498, + 0x1F499, 0x1F49A, 0x1F49B, 0x1F49C, 0x1F49D, 0x1F49E, 0x1F49F, 0x1F4A0, 0x1F4A1, 0x1F4A2, + 0x1F4A3, 0x1F4A4, 0x1F4A5, 0x1F4A6, 0x1F4A7, 0x1F4A8, 0x1F4A9, 0x1F4AA, 0x1F4AB, 0x1F4AC, + 0x1F4AD, 0x1F4AE, 0x1F4AF, 0x1F4B0, 0x1F4B1, 0x1F4B2, 0x1F4B3, 0x1F4B4, 0x1F4B5, 0x1F4B6, + 0x1F4B7, 0x1F4B8, 0x1F4B9, 0x1F4BA, 0x1F4BB, 0x1F4BC, 0x1F4BD, 0x1F4BE, 0x1F4BF, 0x1F4C0, + 0x1F4C1, 0x1F4C2, 0x1F4C3, 0x1F4C4, 0x1F4C5, 0x1F4C6, 0x1F4C7, 0x1F4C8, 0x1F4C9, 0x1F4CA, + 0x1F4CB, 0x1F4CC, 0x1F4CD, 0x1F4CE, 0x1F4CF, 0x1F4D0, 0x1F4D1, 0x1F4D2, 0x1F4D3, 0x1F4D4, + 0x1F4D5, 0x1F4D6, 0x1F4D7, 0x1F4D8, 0x1F4D9, 0x1F4DA, 0x1F4DB, 0x1F4DC, 0x1F4DD, 0x1F4DE, + 0x1F4DF, 0x1F4E0, 0x1F4E1, 0x1F4E2, 0x1F4E3, 0x1F4E4, 0x1F4E5, 0x1F4E6, 0x1F4E7, 0x1F4E8, + 0x1F4E9, 0x1F4EA, 0x1F4EB, 0x1F4EC, 0x1F4ED, 0x1F4EE, 0x1F4EF, 0x1F4F0, 0x1F4F1, 0x1F4F2, + 0x1F4F3, 0x1F4F4, 0x1F4F5, 0x1F4F6, 0x1F4F7, 0x1F4F8, 0x1F4F9, 0x1F4FA, 0x1F4FB, 0x1F4FC, + 0x1F4FD, 0x1F4FF, 0x1F500, 0x1F501, 0x1F502, 0x1F503, 0x1F504, 0x1F505, 0x1F506, 0x1F507, + 0x1F508, 0x1F509, 0x1F50A, 0x1F50B, 0x1F50C, 0x1F50D, 0x1F50E, 0x1F50F, 0x1F510, 0x1F511, + 0x1F512, 0x1F513, 0x1F514, 0x1F515, 0x1F516, 0x1F517, 0x1F518, 0x1F519, 0x1F51A, 0x1F51B, + 0x1F51C, 0x1F51D, 0x1F51E, 0x1F51F, 0x1F520, 0x1F521, 0x1F522, 0x1F523, 0x1F524, 0x1F525, + 0x1F526, 0x1F527, 0x1F528, 0x1F529, 0x1F52A, 0x1F52B, 0x1F52C, 0x1F52D, 0x1F52E, 0x1F52F, + 0x1F530, 0x1F531, 0x1F532, 0x1F533, 0x1F534, 0x1F535, 0x1F536, 0x1F537, 0x1F538, 0x1F539, + 0x1F53A, 0x1F53B, 0x1F53C, 0x1F53D, 0x1F549, 0x1F54A, 0x1F54B, 0x1F54C, 0x1F54D, 0x1F54E, + 0x1F550, 0x1F551, 0x1F552, 0x1F553, 0x1F554, 0x1F555, 0x1F556, 0x1F557, 0x1F558, 0x1F559, + 0x1F55A, 0x1F55B, 0x1F55C, 0x1F55D, 0x1F55E, 0x1F55F, 0x1F560, 0x1F561, 0x1F562, 0x1F563, + 0x1F564, 0x1F565, 0x1F566, 0x1F567, 0x1F56F, 0x1F570, 0x1F573, 0x1F574, 0x1F575, 0x1F576, + 0x1F577, 0x1F578, 0x1F579, 0x1F57A, 0x1F587, 0x1F58A, 0x1F58B, 0x1F58C, 0x1F58D, 0x1F590, + 0x1F595, 0x1F596, 0x1F5A4, 0x1F5A5, 0x1F5A8, 0x1F5B1, 0x1F5B2, 0x1F5BC, 0x1F5C2, 0x1F5C3, + 0x1F5C4, 0x1F5D1, 0x1F5D2, 0x1F5D3, 0x1F5DC, 0x1F5DD, 0x1F5DE, 0x1F5E1, 0x1F5E3, 0x1F5E8, + 0x1F5EF, 0x1F5F3, 0x1F5FA, 0x1F5FB, 0x1F5FC, 0x1F5FD, 0x1F5FE, 0x1F5FF, 0x1F600, 0x1F601, + 0x1F602, 0x1F603, 0x1F604, 0x1F605, 0x1F606, 0x1F607, 0x1F608, 0x1F609, 0x1F60A, 0x1F60B, + 0x1F60C, 0x1F60D, 0x1F60E, 0x1F60F, 0x1F610, 0x1F611, 0x1F612, 0x1F613, 0x1F614, 0x1F615, + 0x1F616, 0x1F617, 0x1F618, 0x1F619, 0x1F61A, 0x1F61B, 0x1F61C, 0x1F61D, 0x1F61E, 0x1F61F, + 0x1F620, 0x1F621, 0x1F622, 0x1F623, 0x1F624, 0x1F625, 0x1F626, 0x1F627, 0x1F628, 0x1F629, + 0x1F62A, 0x1F62B, 0x1F62C, 0x1F62D, 0x1F62E, 0x1F62F, 0x1F630, 0x1F631, 0x1F632, 0x1F633, + 0x1F634, 0x1F635, 0x1F636, 0x1F637, 0x1F638, 0x1F639, 0x1F63A, 0x1F63B, 0x1F63C, 0x1F63D, + 0x1F63E, 0x1F63F, 0x1F640, 0x1F641, 0x1F642, 0x1F643, 0x1F644, 0x1F645, 0x1F646, 0x1F647, + 0x1F648, 0x1F649, 0x1F64A, 0x1F64B, 0x1F64C, 0x1F64D, 0x1F64E, 0x1F64F, 0x1F680, 0x1F681, + 0x1F682, 0x1F683, 0x1F684, 0x1F685, 0x1F686, 0x1F687, 0x1F688, 0x1F689, 0x1F68A, 0x1F68B, + 0x1F68C, 0x1F68D, 0x1F68E, 0x1F68F, 0x1F690, 0x1F691, 0x1F692, 0x1F693, 0x1F694, 0x1F695, + 0x1F696, 0x1F697, 0x1F698, 0x1F699, 0x1F69A, 0x1F69B, 0x1F69C, 0x1F69D, 0x1F69E, 0x1F69F, + 0x1F6A0, 0x1F6A1, 0x1F6A2, 0x1F6A3, 0x1F6A4, 0x1F6A5, 0x1F6A6, 0x1F6A7, 0x1F6A8, 0x1F6A9, + 0x1F6AA, 0x1F6AB, 0x1F6AC, 0x1F6AD, 0x1F6AE, 0x1F6AF, 0x1F6B0, 0x1F6B1, 0x1F6B2, 0x1F6B3, + 0x1F6B4, 0x1F6B5, 0x1F6B6, 0x1F6B7, 0x1F6B8, 0x1F6B9, 0x1F6BA, 0x1F6BB, 0x1F6BC, 0x1F6BD, + 0x1F6BE, 0x1F6BF, 0x1F6C0, 0x1F6C1, 0x1F6C2, 0x1F6C3, 0x1F6C4, 0x1F6C5, 0x1F6CB, 0x1F6CC, + 0x1F6CD, 0x1F6CE, 0x1F6CF, 0x1F6D0, 0x1F6D1, 0x1F6D2, 0x1F6E0, 0x1F6E1, 0x1F6E2, 0x1F6E3, + 0x1F6E4, 0x1F6E5, 0x1F6E9, 0x1F6EB, 0x1F6EC, 0x1F6F0, 0x1F6F3, 0x1F6F4, 0x1F6F5, 0x1F6F6, + 0x1F910, 0x1F911, 0x1F912, 0x1F913, 0x1F914, 0x1F915, 0x1F916, 0x1F917, 0x1F918, 0x1F919, + 0x1F91A, 0x1F91B, 0x1F91C, 0x1F91D, 0x1F91E, 0x1F920, 0x1F921, 0x1F922, 0x1F923, 0x1F924, + 0x1F925, 0x1F926, 0x1F927, 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938, + 0x1F939, 0x1F93A, 0x1F93B, 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943, + 0x1F944, 0x1F945, 0x1F946, 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951, + 0x1F952, 0x1F953, 0x1F954, 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B, + 0x1F95C, 0x1F95D, 0x1F95E, 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986, + 0x1F987, 0x1F988, 0x1F989, 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990, + 0x1F991, 0x1F9C0 }; // See http://www.unicode.org/Public/emoji/3.0/emoji-data.txt diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index 3770a45d2172e9c39359b1fbceef359868a70993..2f327f3cb4950fc9387ab62840cb28fa7604b143 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -129,8 +129,8 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener // The offset is immediately before a variation selector. final int STATE_BEFORE_VS = 6; - // The offset is immediately before a ZWJ emoji. - final int STATE_BEFORE_ZWJ_EMOJI = 7; + // The offset is immediately before an emoji. + final int STATE_BEFORE_EMOJI = 7; // The offset is immediately before a ZWJ that were seen before a ZWJ emoji. final int STATE_BEFORE_ZWJ = 8; // The offset is immediately before a variation selector and a ZWJ that were seen before a @@ -169,7 +169,7 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener } else if (codePoint == Emoji.COMBINING_ENCLOSING_KEYCAP) { state = STATE_BEFORE_KEYCAP; } else if (Emoji.isEmoji(codePoint)) { - state = STATE_BEFORE_ZWJ_EMOJI; + state = STATE_BEFORE_EMOJI; } else { state = STATE_FINISHED; } @@ -232,7 +232,7 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener case STATE_BEFORE_VS: if (Emoji.isEmoji(codePoint)) { deleteCharCount += Character.charCount(codePoint); - state = STATE_BEFORE_ZWJ_EMOJI; + state = STATE_BEFORE_EMOJI; break; } @@ -242,7 +242,7 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener } state = STATE_FINISHED; break; - case STATE_BEFORE_ZWJ_EMOJI: + case STATE_BEFORE_EMOJI: if (codePoint == Emoji.ZERO_WIDTH_JOINER) { state = STATE_BEFORE_ZWJ; } else { @@ -252,7 +252,8 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener case STATE_BEFORE_ZWJ: if (Emoji.isEmoji(codePoint)) { deleteCharCount += Character.charCount(codePoint) + 1; // +1 for ZWJ. - state = STATE_BEFORE_ZWJ_EMOJI; + state = Emoji.isEmojiModifier(codePoint) ? + STATE_BEFORE_EMOJI_MODIFIER : STATE_BEFORE_EMOJI; } else if (isVariationSelector(codePoint)) { lastSeenVSCharCount = Character.charCount(codePoint); state = STATE_BEFORE_VS_AND_ZWJ; @@ -265,7 +266,7 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener // +1 for ZWJ. deleteCharCount += lastSeenVSCharCount + 1 + Character.charCount(codePoint); lastSeenVSCharCount = 0; - state = STATE_BEFORE_ZWJ_EMOJI; + state = STATE_BEFORE_EMOJI; } else { state = STATE_FINISHED; } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 316c7e3f6084408222c6c4ec6132b1af0e23f3f4..8823605c52b6ef251d2ca5e699bb0e86f05375e9 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1940,6 +1940,26 @@ public abstract class Transition implements Cloneable { } } + /** + * Force the transition to move to its end state, ending all the animators. + * + * @hide + */ + void forceToEnd(ViewGroup sceneRoot) { + ArrayMap runningAnimators = getRunningAnimators(); + int numOldAnims = runningAnimators.size(); + if (sceneRoot != null) { + WindowId windowId = sceneRoot.getWindowId(); + for (int i = numOldAnims - 1; i >= 0; i--) { + AnimationInfo info = runningAnimators.valueAt(i); + if (info.view != null && windowId != null && windowId.equals(info.windowId)) { + Animator anim = runningAnimators.keyAt(i); + anim.end(); + } + } + } + } + /** * This method cancels a transition that is currently running. * diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 71c80991bb197c9bbd379c47b0cd7c7af6cf69ba..f2c871e3c718d77a425e613c5039451dec36b230 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -440,7 +440,7 @@ public class TransitionManager { ArrayList copy = new ArrayList(runningTransitions); for (int i = copy.size() - 1; i >= 0; i--) { final Transition transition = copy.get(i); - transition.end(); + transition.forceToEnd(sceneRoot); } } diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 583dc0f1ef2c4062fb367fb152ae130d262fde5d..a41fe64d0be10655b309795ab6a7fc9371f63acd 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -16,8 +16,6 @@ package android.transition; -import com.android.internal.R; - import android.animation.TimeInterpolator; import android.content.Context; import android.content.res.TypedArray; @@ -26,6 +24,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import com.android.internal.R; + import java.util.ArrayList; /** @@ -498,6 +498,16 @@ public class TransitionSet extends Transition { } } + /** @hide */ + @Override + void forceToEnd(ViewGroup sceneRoot) { + super.forceToEnd(sceneRoot); + int numTransitions = mTransitions.size(); + for (int i = 0; i < numTransitions; ++i) { + mTransitions.get(i).forceToEnd(sceneRoot); + } + } + @Override TransitionSet setSceneRoot(ViewGroup sceneRoot) { super.setSceneRoot(sceneRoot); diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index d201adef2d4d4e51e479985d40de8c4e44063b3d..f4db4d6611eaff35e44182a377d82ade4dbe652c 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -61,6 +61,13 @@ public class DisplayMetrics { */ public static final int DENSITY_HIGH = 240; + /** + * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and + * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them. + */ + public static final int DENSITY_260 = 260; + /** * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target, @@ -68,11 +75,26 @@ public class DisplayMetrics { */ public static final int DENSITY_280 = 280; + /** + * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and + * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them. + */ + public static final int DENSITY_300 = 300; + /** * Standard quantized DPI for extra-high-density screens. */ public static final int DENSITY_XHIGH = 320; + /** + * Intermediate density for screens that sit somewhere between + * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi). + * This is not a density that applications should target, instead relying + * on the system to scale their {@link #DENSITY_XXHIGH} assets for them. + */ + public static final int DENSITY_340 = 340; + /** * Intermediate density for screens that sit somewhere between * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi). diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index d3db74d1ea249e27ccdcf3158441fa813b3e881c..3316f3aeb60b5b0ccde7b2f8e7e0c52b78a312ba 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -25,6 +25,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.util.Log; import android.util.TimeUtils; +import android.view.animation.AnimationUtils; import java.io.PrintWriter; @@ -608,6 +609,7 @@ public final class Choreographer { try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); + AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); @@ -620,6 +622,7 @@ public final class Choreographer { doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { + AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 2f2fe57f68fa01db41d98ad3d9eb4e34424ebf43..85a4bf9d9e8f92e4b6d61b8e828c4e2fc0593604 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -36,7 +36,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; -import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM; +import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; /** * Provides information about the size and density of a logical display. @@ -284,6 +284,27 @@ public final class Display { */ public static final int STATE_DOZE_SUSPEND = 4; + /* The color mode constants defined below must be kept in sync with the ones in + * system/graphics.h */ + + /** + * Display color mode: The current color mode is unknown or invalid. + * @hide + */ + public static final int COLOR_MODE_INVALID = -1; + + /** + * Display color mode: The default or native gamut of the display. + * @hide + */ + public static final int COLOR_MODE_DEFAULT = 0; + + /** + * Display color mode: SRGB + * @hide + */ + public static final int COLOR_MODE_SRGB = 7; + /** * Internal method to create a display. * Applications should use {@link android.view.WindowManager#getDefaultDisplay()} @@ -463,20 +484,29 @@ public final class Display { /** * Gets the size of the display, in pixels. + * Value returned by this method does not necessarily represent the actual raw size + * (native resolution) of the display. *

    - * Note that this value should not be used for computing layouts, - * since a device will typically have screen decoration (such as a status bar) - * along the edges of the display that reduce the amount of application - * space available from the size returned here. Layouts should instead use - * the window size. - *

    - * The size is adjusted based on the current rotation of the display. + * 1. The returned size may be adjusted to exclude certain system decor elements + * that are always visible. *

    - * The size returned by this method does not necessarily represent the - * actual raw size (native resolution) of the display. The returned size may - * be adjusted to exclude certain system decoration elements that are always visible. - * It may also be scaled to provide compatibility with older applications that + * 2. It may be scaled to provide compatibility with older applications that * were originally designed for smaller displays. + *

    + * 3. It can be different depending on the WindowManager to which the display belongs. + *

    + * - If requested from non-Activity context (e.g. Application context via + * {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}) + * it will report the size of the entire display based on current rotation and with subtracted + * system decoration areas. + *

    + * - If requested from activity (either using {@code getWindowManager()} or + * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting size will + * correspond to current app window size. In this case it can be smaller than physical size in + * multi-window mode. + *

    + * Typically for the purposes of layout apps should make a request from activity context + * to obtain size available for the app content. *

    * * @param outSize A {@link Point} object to receive the size information. @@ -687,33 +717,22 @@ public final class Display { } /** - * Request the display applies a color transform. + * Request the display applies a color mode. * @hide */ - @RequiresPermission(CONFIGURE_DISPLAY_COLOR_TRANSFORM) - public void requestColorTransform(ColorTransform colorTransform) { - mGlobal.requestColorTransform(mDisplayId, colorTransform.getId()); + @RequiresPermission(CONFIGURE_DISPLAY_COLOR_MODE) + public void requestColorMode(int colorMode) { + mGlobal.requestColorMode(mDisplayId, colorMode); } /** - * Returns the active color transform of this display + * Returns the active color mode of this display * @hide */ - public ColorTransform getColorTransform() { + public int getColorMode() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.getColorTransform(); - } - } - - /** - * Returns the default color transform of this display - * @hide - */ - public ColorTransform getDefaultColorTransform() { - synchronized (this) { - updateDisplayInfoLocked(); - return mDisplayInfo.getDefaultColorTransform(); + return mDisplayInfo.colorMode; } } @@ -728,14 +747,14 @@ public final class Display { } /** - * Gets the supported color transforms of this device. + * Gets the supported color modes of this device. * @hide */ - public ColorTransform[] getSupportedColorTransforms() { + public int[] getSupportedColorModes() { synchronized (this) { updateDisplayInfoLocked(); - ColorTransform[] transforms = mDisplayInfo.supportedColorTransforms; - return Arrays.copyOf(transforms, transforms.length); + int[] colorModes = mDisplayInfo.supportedColorModes; + return Arrays.copyOf(colorModes, colorModes.length); } } @@ -785,13 +804,16 @@ public final class Display { * were originally designed for smaller displays. *

    * 3. It can be different depending on the WindowManager to which the display belongs. - *

    +     * 

    * - If requested from non-Activity context (e.g. Application context via * {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}) - * metrics will report real size of the display based on current rotation. - * - If requested from activity resulting metrics will correspond to current window metrics. - * In this case the size can be smaller than physical size in multi-window mode. - *

    + * metrics will report the size of the entire display based on current rotation and with + * subtracted system decoration areas. + *

    + * - If requested from activity (either using {@code getWindowManager()} or + * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting metrics will + * correspond to current app window metrics. In this case the size can be smaller than physical + * size in multi-window mode. *

    * * @param outMetrics A {@link DisplayMetrics} object to receive the metrics. @@ -1205,6 +1227,33 @@ public final class Display { return mMinLuminance; } + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof HdrCapabilities)) { + return false; + } + HdrCapabilities that = (HdrCapabilities) other; + + return Arrays.equals(mSupportedHdrTypes, that.mSupportedHdrTypes) + && mMaxLuminance == that.mMaxLuminance + && mMaxAverageLuminance == that.mMaxAverageLuminance + && mMinLuminance == that.mMinLuminance; + } + + @Override + public int hashCode() { + int hash = 23; + hash = hash * 17 + Arrays.hashCode(mSupportedHdrTypes); + hash = hash * 17 + Float.floatToIntBits(mMaxLuminance); + hash = hash * 17 + Float.floatToIntBits(mMaxAverageLuminance); + hash = hash * 17 + Float.floatToIntBits(mMinLuminance); + return hash; + } + public static final Creator CREATOR = new Creator() { @Override public HdrCapabilities createFromParcel(Parcel source) { @@ -1251,89 +1300,4 @@ public final class Display { return 0; } } - - /** - * A color transform supported by a given display. - * - * @see Display#getSupportedColorTransforms() - * @hide - */ - public static final class ColorTransform implements Parcelable { - public static final ColorTransform[] EMPTY_ARRAY = new ColorTransform[0]; - - private final int mId; - private final int mColorTransform; - - public ColorTransform(int id, int colorTransform) { - mId = id; - mColorTransform = colorTransform; - } - - public int getId() { - return mId; - } - - public int getColorTransform() { - return mColorTransform; - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof ColorTransform)) { - return false; - } - ColorTransform that = (ColorTransform) other; - return mId == that.mId - && mColorTransform == that.mColorTransform; - } - - @Override - public int hashCode() { - int hash = 1; - hash = hash * 17 + mId; - hash = hash * 17 + mColorTransform; - return hash; - } - - @Override - public String toString() { - return new StringBuilder("{") - .append("id=").append(mId) - .append(", colorTransform=").append(mColorTransform) - .append("}") - .toString(); - } - - @Override - public int describeContents() { - return 0; - } - - private ColorTransform(Parcel in) { - this(in.readInt(), in.readInt()); - } - - @Override - public void writeToParcel(Parcel out, int parcelableFlags) { - out.writeInt(mId); - out.writeInt(mColorTransform); - } - - @SuppressWarnings("hiding") - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public ColorTransform createFromParcel(Parcel in) { - return new ColorTransform(in); - } - - @Override - public ColorTransform[] newArray(int size) { - return new ColorTransform[size]; - } - }; - } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 8aeeffd906439527c6f2ec2a0cb41bec6034760e..bc40849a47bc03157fffde1f7f185fb517e147f9 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -169,14 +169,11 @@ public final class DisplayInfo implements Parcelable { */ public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; - /** The active color transform. */ - public int colorTransformId; + /** The active color mode. */ + public int colorMode; - /** The default color transform. */ - public int defaultColorTransformId; - - /** The list of supported color transforms */ - public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY; + /** The list of supported color modes */ + public int[] supportedColorModes = { Display.COLOR_MODE_DEFAULT }; /** The display's HDR capabilities */ public Display.HdrCapabilities hdrCapabilities; @@ -291,8 +288,8 @@ public final class DisplayInfo implements Parcelable { && rotation == other.rotation && modeId == other.modeId && defaultModeId == other.defaultModeId - && colorTransformId == other.colorTransformId - && defaultColorTransformId == other.defaultColorTransformId + && colorMode == other.colorMode + && Arrays.equals(supportedColorModes, other.supportedColorModes) && Objects.equal(hdrCapabilities, other.hdrCapabilities) && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi @@ -332,10 +329,9 @@ public final class DisplayInfo implements Parcelable { modeId = other.modeId; defaultModeId = other.defaultModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); - colorTransformId = other.colorTransformId; - defaultColorTransformId = other.defaultColorTransformId; - supportedColorTransforms = Arrays.copyOf( - other.supportedColorTransforms, other.supportedColorTransforms.length); + colorMode = other.colorMode; + supportedColorModes = Arrays.copyOf( + other.supportedColorModes, other.supportedColorModes.length); hdrCapabilities = other.hdrCapabilities; logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; @@ -373,12 +369,11 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < nModes; i++) { supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source); } - colorTransformId = source.readInt(); - defaultColorTransformId = source.readInt(); - int nColorTransforms = source.readInt(); - supportedColorTransforms = new Display.ColorTransform[nColorTransforms]; - for (int i = 0; i < nColorTransforms; i++) { - supportedColorTransforms[i] = Display.ColorTransform.CREATOR.createFromParcel(source); + colorMode = source.readInt(); + int nColorModes = source.readInt(); + supportedColorModes = new int[nColorModes]; + for (int i = 0; i < nColorModes; i++) { + supportedColorModes[i] = source.readInt(); } hdrCapabilities = source.readParcelable(null); logicalDensityDpi = source.readInt(); @@ -418,11 +413,10 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < supportedModes.length; i++) { supportedModes[i].writeToParcel(dest, flags); } - dest.writeInt(colorTransformId); - dest.writeInt(defaultColorTransformId); - dest.writeInt(supportedColorTransforms.length); - for (int i = 0; i < supportedColorTransforms.length; i++) { - supportedColorTransforms[i].writeToParcel(dest, flags); + dest.writeInt(colorMode); + dest.writeInt(supportedColorModes.length); + for (int i = 0; i < supportedColorModes.length; i++) { + dest.writeInt(supportedColorModes[i]); } dest.writeParcelable(hdrCapabilities, flags); dest.writeInt(logicalDensityDpi); @@ -496,24 +490,6 @@ public final class DisplayInfo implements Parcelable { return result; } - public Display.ColorTransform getColorTransform() { - return findColorTransform(colorTransformId); - } - - public Display.ColorTransform getDefaultColorTransform() { - return findColorTransform(defaultColorTransformId); - } - - private Display.ColorTransform findColorTransform(int colorTransformId) { - for (int i = 0; i < supportedColorTransforms.length; i++) { - Display.ColorTransform colorTransform = supportedColorTransforms[i]; - if (colorTransform.getId() == colorTransformId) { - return colorTransform; - } - } - throw new IllegalStateException("Unable to locate color transform: " + colorTransformId); - } - public void getAppMetrics(DisplayMetrics outMetrics) { getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); } @@ -615,12 +591,10 @@ public final class DisplayInfo implements Parcelable { sb.append(defaultModeId); sb.append(", modes "); sb.append(Arrays.toString(supportedModes)); - sb.append(", colorTransformId "); - sb.append(colorTransformId); - sb.append(", defaultColorTransformId "); - sb.append(defaultColorTransformId); - sb.append(", supportedColorTransforms "); - sb.append(Arrays.toString(supportedColorTransforms)); + sb.append(", colorMode "); + sb.append(colorMode); + sb.append(", supportedColorModes "); + sb.append(Arrays.toString(supportedColorModes)); sb.append(", hdrCapabilities "); sb.append(hdrCapabilities); sb.append(", rotation "); diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index fb482b4e33ff5529c8f7c538e544961ad33b4fbf..b0f15b5f2329fceb11b5ac94e4592cce11afc760 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -102,8 +102,8 @@ import com.android.internal.view.IDragAndDropPermissions; * * * ACTION_DRAG_ENDED - * X - * X + *   + *   *   *   *   @@ -359,7 +359,7 @@ public class DragEvent implements Parcelable { * The drag handler or listener for a View can use the metadata in this object to decide if the * View can accept the dragged View object's data. *

    - * This method returns valid data for all event actions. + * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}. * @return The ClipDescription that was part of the ClipData sent to the system by startDrag(). */ public ClipDescription getClipDescription() { @@ -377,7 +377,7 @@ public class DragEvent implements Parcelable { * The object is intended to provide local information about the drag and drop operation. For * example, it can indicate whether the drag and drop operation is a copy or a move. *

    - * This method returns valid data for all event actions. + * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}. *

    * @return The local state object sent to the system by startDrag(). */ diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index 5c4450a4ed183b02477ded2b8217e2939a5be91b..2b938d0df7eb44104a03b1f07126bc36a0e9ed0e 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -198,7 +198,7 @@ public final class FrameMetrics { int SWAP_BUFFERS = 12; int FRAME_COMPLETED = 13; - int FRAME_STATS_COUNT = 14; // must always be last + int FRAME_STATS_COUNT = 16; // must always be last } /* diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 7f21901bf68cf6ed84b39368d0b6078e7af344a9..cdd34aff3bcbdb4fa8d8cc2547f7b07b79c6b6fc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -112,7 +112,7 @@ interface IWindowManager int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, in Rect taskBounds, in Configuration configuration, int taskResizeMode, - boolean alwaysFocusable, boolean homeTask, int targetSdkVersion); + boolean alwaysFocusable, boolean homeTask, int targetSdkVersion, int rotationAnimationHint); /** * * @param token The token we are adding to the input task Id. @@ -173,7 +173,8 @@ interface IWindowManager in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded); void setAppVisibility(IBinder token, boolean visible); - void notifyAppStopped(IBinder token, boolean stopped); + void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface); + void notifyAppStopped(IBinder token); void startAppFreezingScreen(IBinder token, int configChanges); void stopAppFreezingScreen(IBinder token, boolean force); void removeAppToken(IBinder token); @@ -305,6 +306,11 @@ interface IWindowManager */ boolean isRotationFrozen(); + /** + * Screenshot the current wallpaper layer, including the whole screen. + */ + Bitmap screenshotWallpaper(); + /** * Used only for assist -- request a screenshot of the current application. */ diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 05c7fbf166e5dc8cac70adcf3677d261ca8b0787..38635c6ced4cd8c86b6494f714f48f65a8cca91f 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -796,8 +796,16 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int KEYCODE_COPY = 278; /** Key code constant: Paste key. */ public static final int KEYCODE_PASTE = 279; + /** Key code constant: Consumed by the system for navigation up */ + public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; + /** Key code constant: Consumed by the system for navigation down */ + public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; + /** Key code constant: Consumed by the system for navigation left*/ + public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; + /** Key code constant: Consumed by the system for navigation right */ + public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; - private static final int LAST_KEYCODE = KEYCODE_PASTE; + private static final int LAST_KEYCODE = KEYCODE_SYSTEM_NAVIGATION_RIGHT; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -1844,6 +1852,10 @@ public class KeyEvent extends InputEvent implements Parcelable { case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: case KeyEvent.KEYCODE_BRIGHTNESS_UP: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: + case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP: + case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN: + case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT: + case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: return true; } @@ -2934,11 +2946,13 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public KeyEvent createFromParcel(Parcel in) { in.readInt(); // skip token, we already know this is a KeyEvent return KeyEvent.createFromParcelBody(in); } + @Override public KeyEvent[] newArray(int size) { return new KeyEvent[size]; } @@ -2962,6 +2976,7 @@ public class KeyEvent extends InputEvent implements Parcelable { mEventTime = in.readLong(); } + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_KEY_EVENT); diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index ab4cbcf21bed885bb06d45c1eef5abad73a8e504..0164fcd4f01673a1c3a347dab0fe28d7ba78359f 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -793,12 +793,12 @@ public class RenderNode { return mOwningView != null && mOwningView.mAttachInfo != null; } - public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) { + public void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) { if (mOwningView == null || mOwningView.mAttachInfo == null) { throw new IllegalStateException("Cannot start this animator on a detached view!"); } - nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr()); - mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); + mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet); } public void endAllAnimators() { diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..b77be8c00e697063609964029ed605d710559b8b --- /dev/null +++ b/core/java/android/view/RoundScrollbarRenderer.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Rect; + +/** + * Helper class for drawing round scroll bars on round Wear devices. + */ +class RoundScrollbarRenderer { + // The range of the scrollbar position represented as an angle in degrees. + private static final int SCROLLBAR_ANGLE_RANGE = 90; + private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16; + private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6; + private static final float WIDTH_PERCENTAGE = 0.02f; + private static final int DEFAULT_THUMB_COLOR = 0xFF757575; + private static final int DEFAULT_TRACK_COLOR = 0x21FFFFFF; + + private final Paint mThumbPaint = new Paint(); + private final Paint mTrackPaint = new Paint(); + private final RectF mRect = new RectF(); + private final View mParent; + + public RoundScrollbarRenderer(View parent) { + // Paints for the round scrollbar. + // Set up the thumb paint + mThumbPaint.setAntiAlias(true); + mThumbPaint.setStrokeCap(Paint.Cap.ROUND); + mThumbPaint.setStyle(Paint.Style.STROKE); + + // Set up the track paint + mTrackPaint.setAntiAlias(true); + mTrackPaint.setStrokeCap(Paint.Cap.ROUND); + mTrackPaint.setStyle(Paint.Style.STROKE); + + mParent = parent; + } + + public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) { + if (alpha == 0) { + return; + } + // Get information about the current scroll state of the parent view. + float maxScroll = mParent.computeVerticalScrollRange(); + float scrollExtent = mParent.computeVerticalScrollExtent(); + if (scrollExtent <= 0 || maxScroll <= scrollExtent) { + return; + } + float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset()); + float linearThumbLength = mParent.computeVerticalScrollExtent(); + float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE; + mThumbPaint.setStrokeWidth(thumbWidth); + mTrackPaint.setStrokeWidth(thumbWidth); + + setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha)); + setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha)); + + // Normalize the sweep angle for the scroll bar. + float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE; + sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE); + // Normalize the start angle so that it falls on the track. + float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle)) + / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2; + startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2, + SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle); + + // Draw the track and the scroll bar. + mRect.set( + bounds.left - thumbWidth / 2, + bounds.top, + bounds.right - thumbWidth / 2, + bounds.bottom); + + canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false, + mTrackPaint); + canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint); + } + + private static float clamp(float val, float min, float max) { + if (val < min) { + return min; + } else if (val > max) { + return max; + } else { + return val; + } + } + + private static int applyAlpha(int color, float alpha) { + int alphaByte = (int) (Color.alpha(color) * alpha); + return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color)); + } + + private void setThumbColor(int thumbColor) { + if (mThumbPaint.getColor() != thumbColor) { + mThumbPaint.setColor(thumbColor); + } + } + + private void setTrackColor(int trackColor) { + if (mTrackPaint.getColor() != trackColor) { + mTrackPaint.setColor(trackColor); + } + } +} diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 7da849a832e4d52d7b2ae8d52e43bef37d601cb2..286e0979d12b73b9ccdb3363fdbd6dea9d9787dc 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -58,6 +58,7 @@ public class Surface implements Parcelable { private static native long nativeGetNextFrameNumber(long nativeObject); private static native int nativeSetScalingMode(long nativeObject, int scalingMode); + private static native void nativeSetBuffersTransform(long nativeObject, long transform); public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 26d9135fee6c53fb2dec6b8dd1f5dd391d8242d7..6ace3049b5913d948761ec1ec07f030ad015803a 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -51,7 +51,7 @@ public class SurfaceControl { private static native void nativeSetLayer(long nativeObject, int zorder); private static native void nativeSetPosition(long nativeObject, float x, float y); - private static native void nativeSetPositionAppliesWithResize(long nativeObject); + private static native void nativeSetGeometryAppliesWithResize(long nativeObject); private static native void nativeSetSize(long nativeObject, int w, int h); private static native void nativeSetTransparentRegionHint(long nativeObject, Region region); private static native void nativeSetAlpha(long nativeObject, float alpha); @@ -82,6 +82,10 @@ public class SurfaceControl { IBinder displayToken); private static native int nativeGetActiveConfig(IBinder displayToken); private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); + private static native int[] nativeGetDisplayColorModes(IBinder displayToken); + private static native int nativeGetActiveColorMode(IBinder displayToken); + private static native boolean nativeSetActiveColorMode(IBinder displayToken, + int colorMode); private static native void nativeSetDisplayPowerMode( IBinder displayToken, int mode); private static native void nativeDeferTransactionUntil(long nativeObject, @@ -89,6 +93,8 @@ public class SurfaceControl { private static native void nativeSetOverrideScalingMode(long nativeObject, int scalingMode); private static native IBinder nativeGetHandle(long nativeObject); + private static native boolean nativeGetTransformToDisplayInverse(long nativeObject); + private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken); @@ -399,6 +405,10 @@ public class SurfaceControl { return nativeGetHandle(mNativeObject); } + public boolean getTransformToDisplayInverse() { + return nativeGetTransformToDisplayInverse(mNativeObject); + } + /** flag the transaction as an animation */ public static void setAnimationTransaction() { nativeSetAnimationTransaction(); @@ -415,13 +425,15 @@ public class SurfaceControl { } /** - * If the size changes in this transaction, position updates specified + * If the buffer size changes in this transaction, position and crop updates specified * in this transaction will not complete until a buffer of the new size - * arrives. + * arrives. As transform matrix and size are already frozen in this fashion, + * this enables totally freezing the surface until the resize has completed + * (at which point the geometry influencing aspects of this transaction will then occur) */ - public void setPositionAppliesWithResize() { + public void setGeometryAppliesWithResize() { checkNotReleased(); - nativeSetPositionAppliesWithResize(mNativeObject); + nativeSetGeometryAppliesWithResize(mNativeObject); } public void setSize(int w, int h) { @@ -545,7 +557,6 @@ public class SurfaceControl { public boolean secure; public long appVsyncOffsetNanos; public long presentationDeadlineNanos; - public int colorTransform; public PhysicalDisplayInfo() { } @@ -569,8 +580,7 @@ public class SurfaceControl { && yDpi == other.yDpi && secure == other.secure && appVsyncOffsetNanos == other.appVsyncOffsetNanos - && presentationDeadlineNanos == other.presentationDeadlineNanos - && colorTransform == other.colorTransform; + && presentationDeadlineNanos == other.presentationDeadlineNanos; } @Override @@ -588,7 +598,6 @@ public class SurfaceControl { secure = other.secure; appVsyncOffsetNanos = other.appVsyncOffsetNanos; presentationDeadlineNanos = other.presentationDeadlineNanos; - colorTransform = other.colorTransform; } // For debugging purposes @@ -597,8 +606,7 @@ public class SurfaceControl { return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure + ", appVsyncOffset " + appVsyncOffsetNanos - + ", bufferDeadline " + presentationDeadlineNanos - + ", colorTransform " + colorTransform + "}"; + + ", bufferDeadline " + presentationDeadlineNanos + "}"; } } @@ -630,6 +638,27 @@ public class SurfaceControl { return nativeSetActiveConfig(displayToken, id); } + public static int[] getDisplayColorModes(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + return nativeGetDisplayColorModes(displayToken); + } + + public static int getActiveColorMode(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + return nativeGetActiveColorMode(displayToken); + } + + public static boolean setActiveColorMode(IBinder displayToken, int colorMode) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + return nativeSetActiveColorMode(displayToken, colorMode); + } + public static void setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) { if (displayToken == null) { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 203b82563ef86d3d308cf769e195763a57bc2be1..48189106f2c4a3b7d9621db6f6ab27dc6833d115 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -499,7 +499,7 @@ public class SurfaceView extends View { | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ; - if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) { + if (!creating && !force && !sizeChanged) { mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; } else { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index e650d956915ac52afe53997b87d996bbecd73a85..2e0729be8b6368d075e8e5aaa29fb7ca24fa13fb 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -16,6 +16,7 @@ package android.view; +import android.app.ActivityManagerNative; import android.annotation.IntDef; import android.annotation.NonNull; import android.content.Context; @@ -23,6 +24,7 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -517,16 +519,6 @@ public final class ThreadedRenderer { } } - /** - * This method should be invoked whenever the current hardware renderer - * context should be reset. - * - * @param surface The surface to hardware accelerate - */ - void invalidate(Surface surface) { - updateSurface(surface); - } - /** * Detaches the layer's surface texture from the GL context and releases * the texture id @@ -881,6 +873,12 @@ public final class ThreadedRenderer { nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); } + void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode, + animator.getAnimatorNativePtr()); + } + public void serializeDisplayListTree() { nSerializeDisplayListTree(mNativeProxy); } @@ -910,10 +908,20 @@ public final class ThreadedRenderer { synchronized void init(Context context, long renderProxy) { if (mInitialized) return; mInitialized = true; + initSched(context, renderProxy); initGraphicsStats(context, renderProxy); initAssetAtlas(context, renderProxy); } + private static void initSched(Context context, long renderProxy) { + try { + int tid = nGetRenderThreadTid(renderProxy); + ActivityManagerNative.getDefault().setRenderThread(tid); + } catch (Throwable t) { + Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t); + } + } + private static void initGraphicsStats(Context context, long renderProxy) { try { IBinder binder = ServiceManager.getService("graphicsstats"); @@ -972,6 +980,7 @@ public final class ThreadedRenderer { private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); private static native void nSetProcessStatsBuffer(long nativeProxy, int fd); + private static native int nGetRenderThreadTid(long nativeProxy); private static native long nCreateRootRenderNode(); private static native long nCreateProxy(boolean translucent, long rootRenderNode); @@ -992,6 +1001,7 @@ public final class ThreadedRenderer { private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); private static native void nDestroy(long nativeProxy, long rootRenderNode); private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); + private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator); private static native void nInvokeFunctor(long functor, boolean waitForCompletion); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c9bb41288414ba00a329383957e6781ece631c0e..e757db7c22c31ae8e7f1fd9c5315fe55a42a60d8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,6 +40,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; @@ -819,6 +820,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static boolean sTextureViewIgnoresDrawableSetters = false; + /** + * Prior to N, some ViewGroups would not convert LayoutParams properly even though both extend + * MarginLayoutParams. For instance, converting LinearLayout.LayoutParams to + * RelativeLayout.LayoutParams would lose margin information. This is fixed on N but target API + * check is implemented for backwards compatibility. + * + * {@hide} + */ + protected static boolean sPreserveMarginParamsInLayoutParamConversion; + /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. @@ -2435,6 +2446,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE * 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED * 1 PFLAG3_TEMPORARY_DETACH + * 1 PFLAG3_NO_REVEAL_ON_FOCUS * |-------|-------|-------|-------| */ @@ -2676,6 +2688,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG3_TEMPORARY_DETACH = 0x2000000; + /** + * Flag indicating that the view does not wish to be revealed within its parent + * hierarchy when it gains focus. Expressed in the negative since the historical + * default behavior is to reveal on focus; this flag suppresses that behavior. + * + * @see #setRevealOnFocusHint(boolean) + * @see #getRevealOnFocusHint() + */ + private static final int PFLAG3_NO_REVEAL_ON_FOCUS = 0x4000000; + /* End of masks for mPrivateFlags3 */ /** @@ -3744,12 +3766,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Flag indicating that a drag can cross window boundaries. When * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called - * with this flag set, all visible applications will be able to participate + * with this flag set, all visible applications with targetSdkVersion >= + * {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate * in the drag operation and receive the dragged content. * - * If this is the only flag set, then the drag recipient will only have access to text data + *

    If this is the only flag set, then the drag recipient will only have access to text data * and intents contained in the {@link ClipData} object. Access to URIs contained in the - * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags. + * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags

    */ public static final int DRAG_FLAG_GLOBAL = 1 << 8; // 256 @@ -3979,6 +4002,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ String mStartActivityRequestWho; + @Nullable + private RoundScrollbarRenderer mRoundScrollbarRenderer; + /** * Simple constructor to use when creating a view from code. * @@ -4036,6 +4062,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // On N+, we throw, but that breaks compatibility with apps that use these methods. sTextureViewIgnoresDrawableSetters = targetSdkVersion <= M; + // Prior to N, we would drop margins in LayoutParam conversions. The fix triggers bugs + // in apps so we target check it to avoid breaking existing apps. + sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >= N; + sCompatibilityDone = true; } } @@ -4214,25 +4244,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setAlpha(a.getFloat(attr, 1f)); break; case com.android.internal.R.styleable.View_transformPivotX: - setPivotX(a.getDimensionPixelOffset(attr, 0)); + setPivotX(a.getDimension(attr, 0)); break; case com.android.internal.R.styleable.View_transformPivotY: - setPivotY(a.getDimensionPixelOffset(attr, 0)); + setPivotY(a.getDimension(attr, 0)); break; case com.android.internal.R.styleable.View_translationX: - tx = a.getDimensionPixelOffset(attr, 0); + tx = a.getDimension(attr, 0); transformSet = true; break; case com.android.internal.R.styleable.View_translationY: - ty = a.getDimensionPixelOffset(attr, 0); + ty = a.getDimension(attr, 0); transformSet = true; break; case com.android.internal.R.styleable.View_translationZ: - tz = a.getDimensionPixelOffset(attr, 0); + tz = a.getDimension(attr, 0); transformSet = true; break; case com.android.internal.R.styleable.View_elevation: - elevation = a.getDimensionPixelOffset(attr, 0); + elevation = a.getDimension(attr, 0); transformSet = true; break; case com.android.internal.R.styleable.View_rotation: @@ -5939,6 +5969,47 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + /** + * Sets this view's preference for reveal behavior when it gains focus. + * + *

    When set to true, this is a signal to ancestor views in the hierarchy that + * this view would prefer to be brought fully into view when it gains focus. + * For example, a text field that a user is meant to type into. Other views such + * as scrolling containers may prefer to opt-out of this behavior.

    + * + *

    The default value for views is true, though subclasses may change this + * based on their preferred behavior.

    + * + * @param revealOnFocus true to request reveal on focus in ancestors, false otherwise + * + * @see #getRevealOnFocusHint() + */ + public final void setRevealOnFocusHint(boolean revealOnFocus) { + if (revealOnFocus) { + mPrivateFlags3 &= ~PFLAG3_NO_REVEAL_ON_FOCUS; + } else { + mPrivateFlags3 |= PFLAG3_NO_REVEAL_ON_FOCUS; + } + } + + /** + * Returns this view's preference for reveal behavior when it gains focus. + * + *

    When this method returns true for a child view requesting focus, ancestor + * views responding to a focus change in {@link ViewParent#requestChildFocus(View, View)} + * should make a best effort to make the newly focused child fully visible to the user. + * When it returns false, ancestor views should preferably not disrupt scroll positioning or + * other properties affecting visibility to the user as part of the focus change.

    + * + * @return true if this view would prefer to become fully visible when it gains focus, + * false if it would prefer not to disrupt scroll positioning + * + * @see #setRevealOnFocusHint(boolean) + */ + public final boolean getRevealOnFocusHint() { + return (mPrivateFlags3 & PFLAG3_NO_REVEAL_ON_FOCUS) == 0; + } + /** * Populates outRect with the hotspot bounds. By default, * the hotspot bounds are identical to the screen bounds. @@ -9780,6 +9851,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Tells whether the {@link View} is in the state between {@link #onStartTemporaryDetach()} + * and {@link #onFinishTemporaryDetach()}. + * + *

    This method always returns {@code true} when called directly or indirectly from + * {@link #onStartTemporaryDetach()}. The return value when called directly or indirectly from + * {@link #onFinishTemporaryDetach()}, however, depends on the OS version. + *

      + *
    • {@code true} on {@link android.os.Build.VERSION_CODES#N API 24}
    • + *
    • {@code false} on {@link android.os.Build.VERSION_CODES#N_MR1 API 25}} and later
    • + *
    + *

    + * * @return {@code true} when the View is in the state between {@link #onStartTemporaryDetach()} * and {@link #onFinishTemporaryDetach()}. */ @@ -9814,8 +9897,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @CallSuper public void dispatchFinishTemporaryDetach() { - onFinishTemporaryDetach(); mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH; + onFinishTemporaryDetach(); if (hasWindowFocus() && hasFocus()) { InputMethodManager.getInstance().focusIn(this); } @@ -10302,7 +10385,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * ancestors or by window visibility * @return true if this view is visible to the user, not counting clipping or overlapping */ - @Visibility boolean dispatchVisibilityAggregated(boolean isVisible) { + boolean dispatchVisibilityAggregated(boolean isVisible) { final boolean thisVisible = getVisibility() == VISIBLE; // If we're not visible but something is telling us we are, ignore it. if (thisVisible || !isVisible) { @@ -12309,6 +12392,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { + // Report visibility changes, which can affect children, to accessibility + if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) { + notifySubtreeAccessibilityStateChangedIfNeeded(); + } mTransformationInfo.mAlpha = alpha; if (onSetAlpha((int) (alpha * 255))) { mPrivateFlags |= PFLAG_ALPHA_SET; @@ -12319,8 +12406,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); mRenderNode.setAlpha(getFinalAlpha()); - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -14720,6 +14805,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private void getVerticalScrollBarBounds(Rect bounds) { + if (mRoundScrollbarRenderer == null) { + getStraightVerticalScrollBarBounds(bounds); + } else { + getRoundVerticalScrollBarBounds(bounds); + } + } + + private void getRoundVerticalScrollBarBounds(Rect bounds) { + final int width = mRight - mLeft; + final int height = mBottom - mTop; + // Do not take padding into account as we always want the scrollbars + // to hug the screen for round wearable devices. + bounds.left = mScrollX; + bounds.top = mScrollY; + bounds.right = bounds.left + width; + bounds.bottom = mScrollY + height; + } + + private void getStraightVerticalScrollBarBounds(Rect bounds) { final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; final int size = getVerticalScrollbarWidth(); int verticalScrollbarPosition = mVerticalScrollbarPosition; @@ -14754,6 +14858,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected final void onDrawScrollBars(Canvas canvas) { // scrollbars are drawn only when the animation is running final ScrollabilityCache cache = mScrollCache; + if (cache != null) { int state = cache.state; @@ -14794,13 +14899,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden(); - if (drawVerticalScrollBar || drawHorizontalScrollBar) { + // Fork out the scroll bar drawing for round wearable devices. + if (mRoundScrollbarRenderer != null) { + if (drawVerticalScrollBar) { + final Rect bounds = cache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + mRoundScrollbarRenderer.drawRoundScrollbars( + canvas, (float) cache.scrollBar.getAlpha() / 255f, bounds); + if (invalidate) { + invalidate(); + } + } + // Do not draw horizontal scroll bars for round wearable devices. + } else if (drawVerticalScrollBar || drawHorizontalScrollBar) { final ScrollBarDrawable scrollBar = cache.scrollBar; if (drawHorizontalScrollBar) { scrollBar.setParameters(computeHorizontalScrollRange(), - computeHorizontalScrollOffset(), - computeHorizontalScrollExtent(), false); + computeHorizontalScrollOffset(), + computeHorizontalScrollExtent(), false); final Rect bounds = cache.mScrollBarBounds; getHorizontalScrollBarBounds(bounds); onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top, @@ -14812,8 +14929,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (drawVerticalScrollBar) { scrollBar.setParameters(computeVerticalScrollRange(), - computeVerticalScrollOffset(), - computeVerticalScrollExtent(), true); + computeVerticalScrollOffset(), + computeVerticalScrollExtent(), true); final Rect bounds = cache.mScrollBarBounds; getVerticalScrollBarBounds(bounds); onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top, @@ -15414,7 +15531,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (vis != GONE) { onWindowVisibilityChanged(vis); if (isShown()) { - // Calling onVisibilityChanged directly here since the subtree will also + // Calling onVisibilityAggregated directly here since the subtree will also // receive dispatchAttachedToWindow and this same call onVisibilityAggregated(vis == VISIBLE); } @@ -17524,6 +17641,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); + + if (shouldDrawRoundScrollbar()) { + if(mRoundScrollbarRenderer == null) { + mRoundScrollbarRenderer = new RoundScrollbarRenderer(this); + } + } else { + mRoundScrollbarRenderer = null; + } + mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo; @@ -20540,7 +20666,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, *
  • {@link #DRAG_FLAG_GLOBAL}
  • *
  • {@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}
  • *
  • {@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}
  • - *
  • {@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}
  • *
  • {@link #DRAG_FLAG_GLOBAL_URI_READ}
  • *
  • {@link #DRAG_FLAG_GLOBAL_URI_WRITE}
  • *
  • {@link #DRAG_FLAG_OPAQUE}
  • @@ -22857,7 +22982,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Last global system UI visibility reported by the window manager. */ - int mGlobalSystemUiVisibility; + int mGlobalSystemUiVisibility = -1; /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener @@ -22904,7 +23029,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int[] mInvalidateChildLocation = new int[2]; /** - * Global to the view hierarchy used as a temporary for dealng with + * Global to the view hierarchy used as a temporary for dealing with * computing absolute on-screen location. */ final int[] mTmpLocation = new int[2]; @@ -23742,4 +23867,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, stream.addProperty("accessibility:labelFor", getLabelFor()); stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); } + + /** + * Determine if this view is rendered on a round wearable device and is the main view + * on the screen. + */ + private boolean shouldDrawRoundScrollbar() { + if (!mResources.getConfiguration().isScreenRound()) { + return false; + } + + final View rootView = getRootView(); + final WindowInsets insets = getRootWindowInsets(); + + int height = getHeight(); + int width = getWidth(); + int displayHeight = rootView.getHeight(); + int displayWidth = rootView.getWidth(); + + if (height != displayHeight || width != displayWidth) { + return false; + } + + getLocationOnScreen(mAttachInfo.mTmpLocation); + return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft() + && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop(); + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 14eeadf662213c20fae86cf11ec8913eb96e1b4d..e7c613a0ca406e72d445e304854d6ae19a9adace 100755 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -44,6 +44,7 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -822,6 +823,13 @@ public final class ViewRootImpl implements ViewParent, } } + public void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.registerVectorDrawableAnimator(animator); + } + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; @@ -1459,6 +1467,8 @@ public final class ViewRootImpl implements ViewParent, final int viewVisibility = getHostVisibility(); final boolean viewVisibilityChanged = !mFirst && (mViewVisibility != viewVisibility || mNewSurfaceNeeded); + final boolean viewUserVisibilityChanged = !mFirst && + ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { @@ -1532,7 +1542,9 @@ public final class ViewRootImpl implements ViewParent, if (viewVisibilityChanged) { mAttachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); - host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); + if (viewUserVisibilityChanged) { + host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); + } if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { endDragResizing(); destroyHardwareResources(); @@ -1723,7 +1735,7 @@ public final class ViewRootImpl implements ViewParent, } boolean hwInitialized = false; - boolean framesChanged = false; + boolean contentInsetsChanged = false; boolean hadSurface = mSurface.isValid(); try { @@ -1763,7 +1775,7 @@ public final class ViewRootImpl implements ViewParent, final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals( mAttachInfo.mOverscanInsets); - boolean contentInsetsChanged = !mPendingContentInsets.equals( + contentInsetsChanged = !mPendingContentInsets.equals( mAttachInfo.mContentInsets); final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals( mAttachInfo.mVisibleInsets); @@ -1813,19 +1825,6 @@ public final class ViewRootImpl implements ViewParent, + mAttachInfo.mVisibleInsets); } - // If any of the insets changed, do a forceLayout on the view so that the - // measure cache is cleared. We might have a pending MSG_RESIZED_REPORT - // that is supposed to take care of it, but since pending insets are - // already modified here, it won't detect the frame change after this. - framesChanged = overscanInsetsChanged - || contentInsetsChanged - || stableInsetsChanged - || visibleInsetsChanged - || outsetsChanged; - if (mAdded && mView != null && framesChanged) { - forceLayout(mView); - } - if (!hadSurface) { if (mSurface.isValid()) { // If we are creating a new surface, then we need to @@ -2009,7 +2008,7 @@ public final class ViewRootImpl implements ViewParent, boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() - || mHeight != host.getMeasuredHeight() || framesChanged || + || mHeight != host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); @@ -2018,7 +2017,7 @@ public final class ViewRootImpl implements ViewParent, + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight + " measuredHeight=" + host.getMeasuredHeight() - + " framesChanged=" + framesChanged); + + " coveredInsetsChanged=" + contentInsetsChanged); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); @@ -3178,7 +3177,7 @@ public final class ViewRootImpl implements ViewParent, } focusNode.recycle(); } - if (mAccessibilityFocusedHost != null) { + if ((mAccessibilityFocusedHost != null) && (mAccessibilityFocusedHost != view)) { // Clear accessibility focus in the view. mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks( AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); @@ -5513,6 +5512,15 @@ public final class ViewRootImpl implements ViewParent, if (mView != null && mAdded) { final int what = event.mAction; + // Cache the drag description when the operation starts, then fill it in + // on subsequent calls as a convenience + if (what == DragEvent.ACTION_DRAG_STARTED) { + mCurrentDragView = null; // Start the current-recipient tracking + mDragDescription = event.mClipDescription; + } else { + event.mClipDescription = mDragDescription; + } + if (what == DragEvent.ACTION_DRAG_EXITED) { // A direct EXITED event means that the window manager knows we've just crossed // a window boundary, so the current drag target within this one must have @@ -5520,15 +5528,6 @@ public final class ViewRootImpl implements ViewParent, // for now. mView.dispatchDragEvent(event); } else { - // Cache the drag description when the operation starts, then fill it in - // on subsequent calls as a convenience - if (what == DragEvent.ACTION_DRAG_STARTED) { - mCurrentDragView = null; // Start the current-recipient tracking - mDragDescription = event.mClipDescription; - } else { - event.mClipDescription = mDragDescription; - } - // For events with a [screen] location, translate into window coordinates if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { mDragPoint.set(event.mX, event.mY); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 26803b8ce89e76c69f7c0e5d3794e5a9ca850a09..14f9fcc3c2e62a5d572685e361b0addb186159af 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -221,6 +221,7 @@ public interface WindowManager extends ViewManager { * @see #TYPE_BASE_APPLICATION * @see #TYPE_APPLICATION * @see #TYPE_APPLICATION_STARTING + * @see #TYPE_DRAWN_APPLICATION * @see #TYPE_APPLICATION_PANEL * @see #TYPE_APPLICATION_MEDIA * @see #TYPE_APPLICATION_SUB_PANEL @@ -245,6 +246,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_BASE_APPLICATION, to = "TYPE_BASE_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION, to = "TYPE_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING, to = "TYPE_APPLICATION_STARTING"), + @ViewDebug.IntToString(from = TYPE_DRAWN_APPLICATION, to = "TYPE_DRAWN_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"), @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"), @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"), @@ -316,6 +318,13 @@ public interface WindowManager extends ViewManager { */ public static final int TYPE_APPLICATION_STARTING = 3; + /** + * Window type: a variation on TYPE_APPLICATION that ensures the window + * manager will wait for this window to be drawn before the app is shown. + * In multiuser systems shows only on the owning user's window. + */ + public static final int TYPE_DRAWN_APPLICATION = 4; + /** * End of types of application windows. */ @@ -638,7 +647,7 @@ public interface WindowManager extends ViewManager { /** * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is - * reserved for screenshot region selection. + * reserved for screenshot region selection. These windows must not take input focus. * @hide */ public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36; @@ -1612,6 +1621,15 @@ public interface WindowManager extends ViewManager { */ public static final int ROTATION_ANIMATION_JUMPCUT = 2; + /** + * Value for {@link #rotationAnimation} to specify seamless rotation mode. + * This works like JUMPCUT but will fall back to CROSSFADE if rotation + * can't be applied without pausing the screen. + * + * @hide + */ + public static final int ROTATION_ANIMATION_SEAMLESS = 3; + /** * Define the exit and entry animations used on this window when the device is rotated. * This only has an affect if the incoming and outgoing topmost diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 11d4f93f7cdb51f28ee5e1362f25deae856215a9..8269be29dcc323caee66037998cedd8233a43b2e 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -135,6 +135,12 @@ public interface WindowManagerPolicy { void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) throws RemoteException; + /** + * @return true if windows with FLAG_DISMISS_KEYGUARD should be allowed to show even if + * the keyguard is locked. + */ + boolean canShowDismissingWindowWhileLockedLw(); + /** * Interface to the Window Manager state associated with a particular * window. You can hold on to an instance of this interface from the call @@ -416,6 +422,8 @@ public interface WindowManagerPolicy { * screen with other application windows. */ public boolean isInMultiWindowMode(); + + public int getRotationAnimationHint(); } /** @@ -476,6 +484,7 @@ public interface WindowManagerPolicy { public void switchInputMethod(boolean forwardDirection); public void shutdown(boolean confirm); + public void reboot(boolean confirm); public void rebootSafeMode(boolean confirm); /** @@ -1417,4 +1426,6 @@ public interface WindowManagerPolicy { * Called when the configuration has changed, and it's safe to load new values from resources. */ public void onConfigurationChanged(); + + public boolean shouldRotateSeamlessly(int oldRotation, int newRotation); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 81ec3306c4c740fad4f78958803d89ef08537c9f..2dfa8cdd3db9e6850286607f6aa67f1307575df3 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -305,7 +305,18 @@ public final class AccessibilityManager { return; } if (!mIsEnabled) { - throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + Looper myLooper = Looper.myLooper(); + if (myLooper == Looper.getMainLooper()) { + throw new IllegalStateException( + "Accessibility off. Did you forget to check that?"); + } else { + // If we're not running on the thread with the main looper, it's possible for + // the state of accessibility to change between checking isEnabled and + // calling this method. So just log the error rather than throwing the + // exception. + Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); + return; + } } userId = mUserId; } diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index a54d94c5c2e322fe9c156745083549b01b2792a1..351b6dbd66162ad595ddef1a1dc4a7c9676e1b4a 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -44,6 +44,31 @@ public class AnimationUtils { private static final int TOGETHER = 0; private static final int SEQUENTIALLY = 1; + private static class AnimationState { + boolean animationClockLocked; + long currentVsyncTimeMillis; + long lastReportedTimeMillis; + }; + + private static ThreadLocal sAnimationState + = new ThreadLocal() { + @Override + protected AnimationState initialValue() { + return new AnimationState(); + } + }; + + /** @hide */ + public static void lockAnimationClock(long vsyncMillis) { + AnimationState state = sAnimationState.get(); + state.animationClockLocked = true; + state.currentVsyncTimeMillis = vsyncMillis; + } + + /** @hide */ + public static void unlockAnimationClock() { + sAnimationState.get().animationClockLocked = false; + } /** * Returns the current animation time in milliseconds. This time should be used when invoking @@ -56,7 +81,14 @@ public class AnimationUtils { * @see android.os.SystemClock */ public static long currentAnimationTimeMillis() { - return SystemClock.uptimeMillis(); + AnimationState state = sAnimationState.get(); + if (state.animationClockLocked) { + // It's important that time never rewinds + return Math.max(state.currentVsyncTimeMillis, + state.lastReportedTimeMillis); + } + state.lastReportedTimeMillis = SystemClock.uptimeMillis(); + return state.lastReportedTimeMillis; } /** diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 89dec2d22d7a74abf37d0833d72643ed0ef1ac6e..38962a36dd701155d2d39241dd873d4758adbb8f 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -851,4 +851,11 @@ public class BaseInputConnection implements InputConnection { endBatchEdit(); } + + /** + * The default implementation does nothing. + */ + public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + return false; + } } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 7b7ccae62dede0f5b6ab07b82d80e6d6687b249b..80380897fd398a6057ea1c960d27fa5278d14e3f 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -25,6 +25,8 @@ import android.text.InputType; import android.text.TextUtils; import android.util.Printer; +import java.util.Arrays; + /** * An EditorInfo describes several attributes of a text editing object * that an input method is communicating with (typically an EditText), most @@ -363,6 +365,18 @@ public class EditorInfo implements InputType, Parcelable { @Nullable public LocaleList hintLocales = null; + + /** + * List of acceptable MIME types for + * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}. + * + *

    {@code null} or an empty array means that + * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is not supported in this + * editor.

    + */ + @Nullable + public String[] contentMimeTypes = null; + /** * Ensure that the data in this EditorInfo is compatible with an application * that was developed against the given target API version. This can @@ -418,6 +432,7 @@ public class EditorInfo implements InputType, Parcelable { + " fieldName=" + fieldName); pw.println(prefix + "extras=" + extras); pw.println(prefix + "hintLocales=" + hintLocales); + pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes)); } /** @@ -446,6 +461,7 @@ public class EditorInfo implements InputType, Parcelable { } else { LocaleList.getEmptyLocaleList().writeToParcel(dest, flags); } + dest.writeStringArray(contentMimeTypes); } /** @@ -471,6 +487,7 @@ public class EditorInfo implements InputType, Parcelable { res.extras = source.readBundle(); LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source); res.hintLocales = hintLocales.isEmpty() ? null : hintLocales; + res.contentMimeTypes = source.readStringArray(); return res; } diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 9f664293476835b2eb8f6f54d0340deb1b3d092b..07910b60df3fd0b872149783057a36264de56348 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -16,6 +16,8 @@ package android.view.inputmethod; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Bundle; import android.os.Handler; import android.view.KeyCharacterMap; @@ -836,4 +838,53 @@ public interface InputConnection { *

    Note: This does nothing when called from input methods.

    */ public void closeConnection(); + + /** + * When this flag is used, the editor will be able to request read access to the content URI + * contained in the {@link InputContentInfo} object. + * + *

    Make sure that the content provider owning the Uri sets the + * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions + * grantUriPermissions} attribute in its manifest or included the + * {@link android.R.styleable#AndroidManifestGrantUriPermission + * <grant-uri-permissions>} tag. Otherwise {@link InputContentInfo#requestPermission()} + * can fail.

    + * + *

    Although calling this API is allowed only for the IME that is currently selected, the + * client is able to request a temporary read-only access even after the current IME is switched + * to any other IME as long as the client keeps {@link InputContentInfo} object.

    + **/ + public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = + android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; // 0x00000001 + + /** + * Called by the input method to commit a content such as PNG image to the editor. + * + *

    In order to avoid variety of compatibility issues, this focuses on a simple use case, + * where we expect editors and IMEs work cooperatively as follows:

    + *
      + *
    • Editor must keep {@link EditorInfo#contentMimeTypes} to be {@code null} if it does + * not support this method at all.
    • + *
    • Editor can ignore this request when the MIME type specified in + * {@code inputContentInfo} does not match to any of {@link EditorInfo#contentMimeTypes}. + *
    • + *
    • Editor can ignore the cursor position when inserting the provided context.
    • + *
    • Editor can return {@code true} asynchronously, even before it starts loading the + * content.
    • + *
    • Editor should provide a way to delete the content inserted by this method, or revert + * the effect caused by this method.
    • + *
    • IME should not call this method when there is any composing text, in case calling + * this method causes focus change.
    • + *
    • IME should grant a permission for the editor to read the content. See + * {@link EditorInfo#packageName} about how to obtain the package name of the editor.
    • + *
    + * + * @param inputContentInfo Content to be inserted. + * @param flags {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}. + * @param opts optional bundle data. This can be {@code null}. + * @return {@code true} if this request is accepted by the application, no matter if the request + * is already handled or still being handled in background. + */ + public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags, + @Nullable Bundle opts); } diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java index 118a61f8d571c3d447b694971144bffd5dcd6d85..2b292bbca84993c269c455379208365224b0bbbf 100644 --- a/core/java/android/view/inputmethod/InputConnectionInspector.java +++ b/core/java/android/view/inputmethod/InputConnectionInspector.java @@ -19,6 +19,7 @@ package android.view.inputmethod; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import java.lang.annotation.Retention; import java.lang.reflect.Method; @@ -41,6 +42,8 @@ public final class InputConnectionInspector { MissingMethodFlags.REQUEST_CURSOR_UPDATES, MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS, MissingMethodFlags.GET_HANDLER, + MissingMethodFlags.CLOSE_CONNECTION, + MissingMethodFlags.COMMIT_CONTENT, }) public @interface MissingMethodFlags { /** @@ -78,6 +81,11 @@ public final class InputConnectionInspector { * {@link android.os.Build.VERSION_CODES#N} and later. */ int CLOSE_CONNECTION = 1 << 6; + /** + * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in + * {@link android.os.Build.VERSION_CODES#N} MR-1 and later. + */ + int COMMIT_CONTENT = 1 << 7; } private static final Map sMissingMethodsMap = Collections.synchronizedMap( @@ -127,6 +135,9 @@ public final class InputConnectionInspector { if (!hasCloseConnection(clazz)) { flags |= MissingMethodFlags.CLOSE_CONNECTION; } + if (!hasCommitContent(clazz)) { + flags |= MissingMethodFlags.COMMIT_CONTENT; + } sMissingMethodsMap.put(clazz, flags); return flags; } @@ -195,6 +206,16 @@ public final class InputConnectionInspector { } } + private static boolean hasCommitContent(@NonNull final Class clazz) { + try { + final Method method = clazz.getMethod("commitContent", InputContentInfo.class, + int.class, Bundle.class); + return !Modifier.isAbstract(method.getModifiers()); + } catch (NoSuchMethodException e) { + return false; + } + } + public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) { final StringBuilder sb = new StringBuilder(); boolean isEmpty = true; @@ -242,6 +263,12 @@ public final class InputConnectionInspector { } sb.append("closeConnection()"); } + if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) { + if (!isEmpty) { + sb.append(","); + } + sb.append("commitContent(InputContentInfo, Bundle)"); + } return sb.toString(); } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index e743f62a5a0a6df97288951d4acdc5e6e1d7f51b..317730ca092c6752bef971b15e2f97d52b0cd4b8 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -269,4 +269,12 @@ public class InputConnectionWrapper implements InputConnection { public void closeConnection() { mTarget.closeConnection(); } + + /** + * {@inheritDoc} + * @throws NullPointerException if the target is {@code null}. + */ + public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + return mTarget.commitContent(inputContentInfo, flags, opts); + } } diff --git a/core/java/android/view/inputmethod/InputContentInfo.aidl b/core/java/android/view/inputmethod/InputContentInfo.aidl new file mode 100644 index 0000000000000000000000000000000000000000..1afeee3b4b245692e760e38229966456dddae54d --- /dev/null +++ b/core/java/android/view/inputmethod/InputContentInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +parcelable InputContentInfo; diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b39705e0b1fa686b1d6ceabc3ee9a1e10a5e27df --- /dev/null +++ b/core/java/android/view/inputmethod/InputContentInfo.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.inputmethod; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ClipDescription; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +import com.android.internal.inputmethod.IInputContentUriToken; + +import java.security.InvalidParameterException; + +/** + * A container object with which input methods can send content files to the target application. + */ +public final class InputContentInfo implements Parcelable { + + @NonNull + private final Uri mContentUri; + @NonNull + private final ClipDescription mDescription; + @Nullable + private final Uri mLinkUri; + @NonNull + private IInputContentUriToken mUriToken; + + /** + * Constructs {@link InputContentInfo} object only with mandatory data. + * + * @param contentUri Content URI to be exported from the input method. + * This cannot be {@code null}. + * @param description A {@link ClipDescription} object that contains the metadata of + * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also + * {@link ClipDescription#getLabel()} should be describing the content specified by + * {@code contentUri} for accessibility reasons. + */ + public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) { + this(contentUri, description, null /* link Uri */); + } + + /** + * Constructs {@link InputContentInfo} object with additional link URI. + * + * @param contentUri Content URI to be exported from the input method. + * This cannot be {@code null}. + * @param description A {@link ClipDescription} object that contains the metadata of + * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also + * {@link ClipDescription#getLabel()} should be describing the content specified by + * {@code contentUri} for accessibility reasons. + * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide + * a way to navigate the user to the specified web page if this is not {@code null}. + * @throws InvalidParameterException if any invalid parameter is specified. + */ + public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description, + @Nullable Uri linkUri) { + validateInternal(contentUri, description, linkUri, true /* throwException */); + mContentUri = contentUri; + mDescription = description; + mLinkUri = linkUri; + } + + /** + * @return {@code true} if all the fields are valid. + * @hide + */ + public boolean validate() { + return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */); + } + + /** + * Constructs {@link InputContentInfo} object with additional link URI. + * + * @param contentUri Content URI to be exported from the input method. + * This cannot be {@code null}. + * @param description A {@link ClipDescription} object that contains the metadata of + * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also + * {@link ClipDescription#getLabel()} should be describing the content specified by + * {@code contentUri} for accessibility reasons. + * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide + * a way to navigate the user to the specified web page if this is not {@code null}. + * @param throwException {@code true} if this method should throw an + * {@link InvalidParameterException}. + * @throws InvalidParameterException if any invalid parameter is specified. + */ + private static boolean validateInternal(@NonNull Uri contentUri, + @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) { + if (contentUri == null) { + if (throwException) { + throw new NullPointerException("contentUri"); + } + return false; + } + if (description == null) { + if (throwException) { + throw new NullPointerException("description"); + } + return false; + } + final String contentUriScheme = contentUri.getScheme(); + if (!"content".equals(contentUriScheme)) { + if (throwException) { + throw new InvalidParameterException("contentUri must have content scheme"); + } + return false; + } + if (linkUri != null) { + final String scheme = linkUri.getScheme(); + if (scheme == null || + (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) { + if (throwException) { + throw new InvalidParameterException( + "linkUri must have either http or https scheme"); + } + return false; + } + } + return true; + } + + /** + * @return Content URI with which the content can be obtained. + */ + @NonNull + public Uri getContentUri() { return mContentUri; } + + /** + * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()} + * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility + * purpose. + */ + @NonNull + public ClipDescription getDescription() { return mDescription; } + + /** + * @return An optional {@code http} or {@code https} URI that is related to this content. + */ + @Nullable + public Uri getLinkUri() { return mLinkUri; } + + void setUriToken(IInputContentUriToken token) { + if (mUriToken != null) { + throw new IllegalStateException("URI token is already set"); + } + mUriToken = token; + } + + /** + * Requests a temporary read-only access permission for content URI associated with this object. + * + *

    Does nothing if the temporary permission is already granted.

    + */ + public void requestPermission() { + if (mUriToken == null) { + return; + } + try { + mUriToken.take(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Releases a temporary read-only access permission for content URI associated with this object. + * + *

    Does nothing if the temporary permission is not granted.

    + */ + public void releasePermission() { + if (mUriToken == null) { + return; + } + try { + mUriToken.release(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + Uri.writeToParcel(dest, mContentUri); + mDescription.writeToParcel(dest, flags); + Uri.writeToParcel(dest, mLinkUri); + if (mUriToken != null) { + dest.writeInt(1); + dest.writeStrongBinder(mUriToken.asBinder()); + } else { + dest.writeInt(0); + } + } + + private InputContentInfo(@NonNull Parcel source) { + mContentUri = Uri.CREATOR.createFromParcel(source); + mDescription = ClipDescription.CREATOR.createFromParcel(source); + mLinkUri = Uri.CREATOR.createFromParcel(source); + if (source.readInt() == 1) { + mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder()); + } else { + mUriToken = null; + } + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public InputContentInfo createFromParcel(Parcel source) { + return new InputContentInfo(source); + } + + @Override + public InputContentInfo[] newArray(int size) { + return new InputContentInfo[size]; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 4013b30fce06f4498ed675470cb88199fe93d96e..c0c8e64aacc8bdfee29ee40feca670cd48e717ab 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; @@ -30,6 +31,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.Context; import android.graphics.Rect; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -56,6 +58,7 @@ import android.view.ViewRootImpl; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -2288,6 +2291,40 @@ public final class InputMethodManager { } } + /** + * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access + * permission to the content. + * + *

    See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, EditorInfo)} + * for details.

    + * + * @param token Supplies the identifying token given to an input method when it was started, + * which allows it to perform this operation on itself. + * @param inputContentInfo Content to be temporarily exposed from the input method to the + * application. + * This cannot be {@code null}. + * @param editorInfo The editor that receives {@link InputContentInfo}. + * @hide + */ + public void exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo, + @NonNull EditorInfo editorInfo) { + final IInputContentUriToken uriToken; + final Uri contentUri = inputContentInfo.getContentUri(); + try { + uriToken = mService.createInputContentUriToken(token, contentUri, + editorInfo.packageName); + if (uriToken == null) { + return; + } + } catch (RemoteException e) { + Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString() + + " packageName=" + editorInfo.packageName, e); + return; + } + inputContentInfo.setUriToken(uriToken); + return; + } + void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { final Printer p = new PrintWriterPrinter(fout); p.println("Input method client state for " + this + ":"); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index bdbbe8e5704ab36ed27066faf42ccfd4ddd2982d..46da9d3c28b7f4b6f652c8bef9f0f5208a53f8db 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -75,6 +75,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputMethodManager; import android.widget.RemoteViews.OnClickHandler; @@ -1545,7 +1546,7 @@ public abstract class AbsListView extends AdapterView implements Te switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: case R.id.accessibilityActionScrollDown: { - if (isEnabled() && getLastVisiblePosition() < getCount() - 1) { + if (isEnabled() && canScrollDown()) { final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom; smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION); return true; @@ -1553,7 +1554,7 @@ public abstract class AbsListView extends AdapterView implements Te } return false; case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: case R.id.accessibilityActionScrollUp: { - if (isEnabled() && mFirstPosition > 0) { + if (isEnabled() && canScrollUp()) { final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom; smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION); return true; @@ -5983,6 +5984,11 @@ public abstract class AbsListView extends AdapterView implements Te public void closeConnection() { getTarget().closeConnection(); } + + @Override + public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + return getTarget().commitContent(inputContentInfo, flags, opts); + } } /** diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 2fd52b5f1230a360a5c218502a34360649de7a08..af22ec7bc755f37942833acb62f8202ee52f69d3 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -65,6 +65,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { private SimpleDateFormat mYearFormat; private SimpleDateFormat mMonthDayFormat; + private SimpleDateFormat mAccessibilityEventFormat; + // Top-level container. private ViewGroup mContainer; @@ -307,6 +309,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mMonthDayFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE); mYearFormat = new SimpleDateFormat("y", locale); + // Clear out the lazily-initialized accessibility event formatter. + mAccessibilityEventFormat = null; + // Update the header text. onCurrentDateChanged(false); } @@ -586,7 +591,12 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { @Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - event.getText().add(mCurrentDate.getTime().toString()); + if (mAccessibilityEventFormat == null) { + final String pattern = DateFormat.getBestDateTimePattern(mCurrentLocale, "EMMMMdy"); + mAccessibilityEventFormat = new SimpleDateFormat(pattern); + } + final CharSequence text = mAccessibilityEventFormat.format(mCurrentDate.getTime()); + event.getText().add(text); } public CharSequence getAccessibilityClassName() { diff --git a/core/java/android/widget/ForwardingListener.java b/core/java/android/widget/ForwardingListener.java index b383e1c55aada780f40b6d9d45de0b5801d0b599..a5fcbc7934122abff7b76148c88f31f35199747a 100644 --- a/core/java/android/widget/ForwardingListener.java +++ b/core/java/android/widget/ForwardingListener.java @@ -58,13 +58,14 @@ public abstract class ForwardingListener public ForwardingListener(View src) { mSrc = src; + src.setLongClickable(true); + src.addOnAttachStateChangeListener(this); + mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop(); mTapTimeout = ViewConfiguration.getTapTimeout(); // Use a medium-press timeout. Halfway between tap and long-press. mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2; - - src.addOnAttachStateChangeListener(this); } /** diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 029313c539389a878364a70f90d3508684ebc583..b8c74d88b58079a14d990b3bd97e0b594c37eb9f 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -382,13 +382,14 @@ public class FrameLayout extends ViewGroup { @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - if (lp instanceof LayoutParams) { - return new LayoutParams((LayoutParams) lp); - } else if (lp instanceof MarginLayoutParams) { - return new LayoutParams((MarginLayoutParams) lp); - } else { - return new LayoutParams(lp); + if (sPreserveMarginParamsInLayoutParamConversion) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } } + return new LayoutParams(lp); } @Override diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 726586ee8d5f305fcbc6610ff2958048dfd8298a..af2852cde22a124e87d3dea28a60325072717908 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -868,13 +868,14 @@ public class GridLayout extends ViewGroup { @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - if (lp instanceof LayoutParams) { - return new LayoutParams((LayoutParams) lp); - } else if (lp instanceof MarginLayoutParams) { - return new LayoutParams((MarginLayoutParams) lp); - } else { - return new LayoutParams(lp); + if (sPreserveMarginParamsInLayoutParamConversion) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } } + return new LayoutParams(lp); } // Draw grid diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 222a040d2b3caf2cddbc888aa584370af70004ba..aa67c8215b0481198d90f937f0bcb3535a3ab326 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -115,11 +114,17 @@ public class ImageView extends View { private int mBaseline = -1; private boolean mBaselineAlignBottom = false; - // AdjustViewBounds behavior will be in compatibility mode for older apps. - private boolean mAdjustViewBoundsCompat = false; + /** Compatibility modes dependent on targetSdkVersion of the app. */ + private static boolean sCompatDone; + + /** AdjustViewBounds behavior will be in compatibility mode for older apps. */ + private static boolean sCompatAdjustViewBounds; /** Whether to pass Resources when creating the source from a stream. */ - private boolean mUseCorrectStreamDensity; + private static boolean sCompatUseCorrectStreamDensity; + + /** Whether to use pre-Nougat drawable visibility dispatching conditions. */ + private static boolean sCompatDrawableVisibilityDispatch; private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, @@ -206,9 +211,13 @@ public class ImageView extends View { mMatrix = new Matrix(); mScaleType = ScaleType.FIT_CENTER; - final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; - mAdjustViewBoundsCompat = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; - mUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M; + if (!sCompatDone) { + final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; + sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M; + sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N; + sCompatDone = true; + } } @Override @@ -545,6 +554,13 @@ public class ImageView extends View { * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically * mutate the drawable and apply the specified tint and tint mode using * {@link Drawable#setTintList(ColorStateList)}. + *

    + * Note: The default tint mode used by this setter is NOT + * consistent with the default tint mode used by the + * {@link android.R.styleable#ImageView_tint android:tint} + * attribute. If the {@code android:tint} attribute is specified, the + * default tint mode will be set to {@link PorterDuff.Mode#SRC_ATOP} to + * ensure consistency with earlier versions of the platform. * * @param tint the tint to apply, may be {@code null} to clear tint * @@ -874,8 +890,8 @@ public class ImageView extends View { InputStream stream = null; try { stream = mContext.getContentResolver().openInputStream(uri); - return Drawable.createFromResourceStream( - mUseCorrectStreamDensity ? getResources() : null, null, stream, null); + return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity + ? getResources() : null, null, stream, null); } catch (Exception e) { Log.w(LOG_TAG, "Unable to open content: " + uri, e); } finally { @@ -910,10 +926,13 @@ public class ImageView extends View { mRecycleableBitmapDrawable.setBitmap(null); } + boolean sameDrawable = false; + if (mDrawable != null) { + sameDrawable = mDrawable == d; mDrawable.setCallback(null); unscheduleDrawable(mDrawable); - if (isAttachedToWindow()) { + if (!sCompatDrawableVisibilityDispatch && !sameDrawable && isAttachedToWindow()) { mDrawable.setVisible(false, false); } } @@ -926,8 +945,11 @@ public class ImageView extends View { if (d.isStateful()) { d.setState(getDrawableState()); } - if (isAttachedToWindow()) { - d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true); + if (!sameDrawable || sCompatDrawableVisibilityDispatch) { + final boolean visible = sCompatDrawableVisibilityDispatch + ? getVisibility() == VISIBLE + : isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown(); + d.setVisible(visible, true); } d.setLevel(mLevel); mDrawableWidth = d.getIntrinsicWidth(); @@ -1051,7 +1073,7 @@ public class ImageView extends View { pleft + pright; // Allow the width to outgrow its original estimate if height is fixed. - if (!resizeHeight && !mAdjustViewBoundsCompat) { + if (!resizeHeight && !sCompatAdjustViewBounds) { widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); } @@ -1067,7 +1089,7 @@ public class ImageView extends View { ptop + pbottom; // Allow the height to outgrow its original estimate if width is fixed. - if (!resizeWidth && !mAdjustViewBoundsCompat) { + if (!resizeWidth && !sCompatAdjustViewBounds) { heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightMeasureSpec); } @@ -1506,11 +1528,40 @@ public class ImageView extends View { @Override public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); - if (mDrawable != null) { + // Only do this for new apps post-Nougat + if (mDrawable != null && !sCompatDrawableVisibilityDispatch) { mDrawable.setVisible(isVisible, false); } } + @RemotableViewMethod + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated + if (mDrawable != null && sCompatDrawableVisibilityDispatch) { + mDrawable.setVisible(visibility == VISIBLE, false); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated + if (mDrawable != null && sCompatDrawableVisibilityDispatch) { + mDrawable.setVisible(getVisibility() == VISIBLE, false); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Only do this for old apps pre-Nougat; new apps use onVisibilityAggregated + if (mDrawable != null && sCompatDrawableVisibilityDispatch) { + mDrawable.setVisible(false, false); + } + } + @Override public CharSequence getAccessibilityClassName() { return ImageView.class.getName(); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 38d7cd4acaf66e56bb53922f263b20427de62a17..f897372c1f01918288c37b6c9bb986e21d2ce71c 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1844,13 +1844,14 @@ public class LinearLayout extends ViewGroup { @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - if (lp instanceof LayoutParams) { - return new LayoutParams((LayoutParams) lp); - } else if (lp instanceof MarginLayoutParams) { - return new LayoutParams((MarginLayoutParams) lp); - } else { - return new LayoutParams(lp); + if (sPreserveMarginParamsInLayoutParamConversion) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } } + return new LayoutParams(lp); } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index b8b7c553a1db3dda2a08cf5b205b7bdb19a9ceb3..b0f19d7a0b49fce927f8818d397b45f6158c60fd 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -112,8 +112,8 @@ public class ListView extends AbsListView { public boolean isSelectable; } - private ArrayList mHeaderViewInfos = Lists.newArrayList(); - private ArrayList mFooterViewInfos = Lists.newArrayList(); + ArrayList mHeaderViewInfos = Lists.newArrayList(); + ArrayList mFooterViewInfos = Lists.newArrayList(); Drawable mDivider; int mDividerHeight; @@ -279,7 +279,7 @@ public class ListView extends AbsListView { // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); + wrapHeaderListAdapterInternal(); } // In the case of re-adding a header view, or adding one later on, @@ -373,7 +373,7 @@ public class ListView extends AbsListView { // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); + wrapHeaderListAdapterInternal(); } // In the case of re-adding a footer view, or adding one later on, @@ -476,7 +476,7 @@ public class ListView extends AbsListView { mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); + mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } @@ -2050,6 +2050,8 @@ public class ListView extends AbsListView { p.recycledHeaderFooter = true; } addViewInLayout(child, flowDown ? -1 : 0, p, true); + // add view in layout will reset the RTL properties. We have to re-resolve them + child.resolveRtlPropertiesIfNeeded(); } if (needToMeasure) { @@ -2226,7 +2228,7 @@ public class ListView extends AbsListView { * after the header views. */ public void setSelectionAfterHeaderView() { - final int count = mHeaderViewInfos.size(); + final int count = getHeaderViewsCount(); if (count > 0) { mNextSelectedPosition = 0; return; @@ -3354,7 +3356,7 @@ public class ListView extends AbsListView { bounds.right = mRight - mLeft - mPaddingRight; final int count = getChildCount(); - final int headerCount = mHeaderViewInfos.size(); + final int headerCount = getHeaderViewsCount(); final int itemCount = mItemCount; final int footerLimit = (itemCount - mFooterViewInfos.size()); final boolean headerDividers = mHeaderDividersEnabled; @@ -3938,7 +3940,7 @@ public class ListView extends AbsListView { if (drawDividers) { final boolean fillForMissingDividers = isOpaque() && !super.isOpaque(); final int itemCount = mItemCount; - final int headerCount = mHeaderViewInfos.size(); + final int headerCount = getHeaderViewsCount(); final int footerLimit = (itemCount - mFooterViewInfos.size()); final boolean isHeader = (itemIndex < headerCount); final boolean isFooter = (itemIndex >= footerLimit); @@ -4050,4 +4052,24 @@ public class ListView extends AbsListView { encoder.addProperty("recycleOnMeasure", recycleOnMeasure()); } + + /** @hide */ + protected HeaderViewListAdapter wrapHeaderListAdapterInternal( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter); + } + + /** @hide */ + protected void wrapHeaderListAdapterInternal() { + mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter); + } + + /** @hide */ + protected void dispatchDataSetObserverOnChangedInternal() { + if (mDataSetObserver != null) { + mDataSetObserver.onChanged(); + } + } } diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index 02ee2df18aafd2ecfc0fddc93d5d43df0ff5ed3d..6f198e78df8a1c5b41023a64ca2cca4ffb7b068a 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -16,7 +16,11 @@ package android.widget; +import com.android.internal.R; +import com.android.internal.widget.ExploreByTouchHelper; + import android.animation.ObjectAnimator; +import android.annotation.IntDef; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -43,9 +47,8 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import com.android.internal.R; -import com.android.internal.widget.ExploreByTouchHelper; - +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Calendar; import java.util.Locale; @@ -55,11 +58,16 @@ import java.util.Locale; * @hide */ public class RadialTimePickerView extends View { - private static final String TAG = "RadialTimePickerView"; public static final int HOURS = 0; public static final int MINUTES = 1; + + /** @hide */ + @IntDef({HOURS, MINUTES}) + @Retention(RetentionPolicy.SOURCE) + @interface PickerType {} + private static final int HOURS_INNER = 2; private static final int SELECTOR_CIRCLE = 0; @@ -185,8 +193,24 @@ public class RadialTimePickerView extends View { private boolean mInputEnabled = true; - public interface OnValueSelectedListener { - void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance); + interface OnValueSelectedListener { + /** + * Called when the selected value at a given picker index has changed. + * + * @param pickerType the type of value that has changed, one of: + *

      + *
    • {@link #MINUTES} + *
    • {@link #HOURS} + *
    + * @param newValue the new value as minute in hour (0-59) or hour in + * day (0-23) + * @param autoAdvance when the picker type is {@link #HOURS}, + * {@code true} to switch to the {@link #MINUTES} + * picker or {@code false} to stay on the current + * picker. No effect when picker type is + * {@link #MINUTES}. + */ + void onValueSelected(@PickerType int pickerType, int newValue, boolean autoAdvance); } /** @@ -977,7 +1001,7 @@ public class RadialTimePickerView extends View { // Ensure we're showing the correct picker. animatePicker(mShowHours, ANIM_DURATION_TOUCH); - final int type; + final @PickerType int type; final int newValue; final boolean valueChanged; diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java index 223096116adbac74368812c846b550ffb5ceebb4..3ad05b5e7c01f9c428671470a4a74bf589e7eba1 100644 --- a/core/java/android/widget/RatingBar.java +++ b/core/java/android/widget/RatingBar.java @@ -110,8 +110,8 @@ public class RatingBar extends AbsSeekBar { } // A touch inside a star fill up to that fractional area (slightly more - // than 1 so boundaries round up). - mTouchProgressOffset = 1.1f; + // than 0.5 so boundaries round up). + mTouchProgressOffset = 0.6f; } public RatingBar(Context context, AttributeSet attrs) { diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 0136542ef4ac867d34ec62ec1dace4a44161551c..a189d3c0cc9126bc2c2c5304c22b1a3dbda0d1e3 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -1104,13 +1104,14 @@ public class RelativeLayout extends ViewGroup { @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - if (lp instanceof LayoutParams) { - return new LayoutParams((LayoutParams) lp); - } else if (lp instanceof MarginLayoutParams) { - return new LayoutParams((MarginLayoutParams) lp); - } else { - return new LayoutParams(lp); + if (sPreserveMarginParamsInLayoutParamConversion) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } } + return new LayoutParams(lp); } /** @hide */ diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 1f745185d01b16823dc43575a33bb194733106bc..d0d233ebe975e06bcd263873b41de12950856960 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2787,6 +2787,18 @@ public class RemoteViews implements Parcelable, Filter { setInt(viewId, "setTextColor", color); } + /** + * @hide + * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}. + * + * @param viewId The id of the view whose text color should change + * @param colors the text colors to set + */ + public void setTextColor(int viewId, @ColorInt ColorStateList colors) { + addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST, + colors)); + } + /** * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. * @@ -3195,7 +3207,9 @@ public class RemoteViews implements Parcelable, Filter { // we don't add a filter to the static version returned by getSystemService. inflater = inflater.cloneInContext(inflationContext); inflater.setFilter(this); - return inflater.inflate(rv.getLayoutId(), parent, false); + View v = inflater.inflate(rv.getLayoutId(), parent, false); + v.setTagInternal(R.id.widget_frame, rv.getLayoutId()); + return v; } private static void loadTransitionOverride(Context context, @@ -3373,7 +3387,7 @@ public class RemoteViews implements Parcelable, Filter { // across orientation change, and has the RemoteViews re-applied in the new orientation, // we throw an exception, since the layouts may be completely unrelated. if (hasLandscapeAndPortraitLayouts()) { - if (v.getId() != rvToApply.getLayoutId()) { + if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } @@ -3409,7 +3423,7 @@ public class RemoteViews implements Parcelable, Filter { // across orientation change, and has the RemoteViews re-applied in the new orientation, // we throw an exception, since the layouts may be completely unrelated. if (hasLandscapeAndPortraitLayouts()) { - if (v.getId() != rvToApply.getLayoutId()) { + if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5cbd2841b57a30993d10847c3c737af7fefcdaf4..d3cb7425fe3584c17cd8746e60ef2fadd13b4648 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3255,6 +3255,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @attr ref android.R.styleable#TextView_textColor */ + @android.view.RemotableViewMethod public void setTextColor(ColorStateList colors) { if (colors == null) { throw new NullPointerException(); diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index c21f1dfe0ed13c386c1719e51c124351f5ca6c0c..aa0b93d76a15ad07abe25edbdc42558bbdced3d7 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -61,9 +61,6 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { private static final int HOUR_INDEX = RadialTimePickerView.HOURS; private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES; - // NOT a real index for the purpose of what's showing. - private static final int AMPM_INDEX = 2; - private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor}; private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha}; @@ -701,22 +698,21 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { /** Listener for RadialTimePickerView interaction. */ private final OnValueSelectedListener mOnValueSelectedListener = new OnValueSelectedListener() { @Override - public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { - switch (pickerIndex) { - case HOUR_INDEX: + public void onValueSelected(int pickerType, int newValue, boolean autoAdvance) { + switch (pickerType) { + case RadialTimePickerView.HOURS: final boolean isTransition = mAllowAutoAdvance && autoAdvance; setHourInternal(newValue, true, !isTransition); if (isTransition) { setCurrentItemShowing(MINUTE_INDEX, true, false); - mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes); + + final int localizedHour = getLocalizedHour(newValue); + mDelegator.announceForAccessibility(localizedHour + ". " + mSelectMinutes); } break; - case MINUTE_INDEX: + case RadialTimePickerView.MINUTES: setMinuteInternal(newValue, true); break; - case AMPM_INDEX: - updateAmPmLabelStates(newValue); - break; } if (mOnTimeChangedListener != null) { diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 9cdb73ae51ce88c494862e4e9ef05310b09a0b9c..0988c928cb2633b624f15d0402b271a2833afaf3 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -161,7 +161,7 @@ public class Toolbar extends ViewGroup { private int mTitleMarginTop; private int mTitleMarginBottom; - private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper(); + private RtlSpacingHelper mContentInsets; private int mContentInsetStartWithNavigation; private int mContentInsetEndWithActions; @@ -270,6 +270,7 @@ public class Toolbar extends ViewGroup { final int contentInsetRight = a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0); + ensureContentInsets(); mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); if (contentInsetStart != RtlSpacingHelper.UNDEFINED || @@ -463,13 +464,13 @@ public class Toolbar extends ViewGroup { */ public void setTitleMarginBottom(int margin) { mTitleMarginBottom = margin; - requestLayout(); } @Override public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); + ensureContentInsets(); mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL); } @@ -1092,6 +1093,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetStart */ public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) { + ensureContentInsets(); mContentInsets.setRelative(contentInsetStart, contentInsetEnd); } @@ -1112,7 +1114,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetStart */ public int getContentInsetStart() { - return mContentInsets.getStart(); + return mContentInsets != null ? mContentInsets.getStart() : 0; } /** @@ -1132,7 +1134,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetEnd */ public int getContentInsetEnd() { - return mContentInsets.getEnd(); + return mContentInsets != null ? mContentInsets.getEnd() : 0; } /** @@ -1154,6 +1156,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetRight */ public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) { + ensureContentInsets(); mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); } @@ -1174,7 +1177,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetLeft */ public int getContentInsetLeft() { - return mContentInsets.getLeft(); + return mContentInsets != null ? mContentInsets.getLeft() : 0; } /** @@ -1194,7 +1197,7 @@ public class Toolbar extends ViewGroup { * @attr ref android.R.styleable#Toolbar_contentInsetRight */ public int getContentInsetRight() { - return mContentInsets.getRight(); + return mContentInsets != null ? mContentInsets.getRight() : 0; } /** @@ -2128,6 +2131,12 @@ public class Toolbar extends ViewGroup { } } + private void ensureContentInsets() { + if (mContentInsets == null) { + mContentInsets = new RtlSpacingHelper(); + } + } + /** * Accessor to enable LayoutLib to get ActionMenuPresenter directly. */ diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java index ed48b0d1610c5e25f0fa428c69be12ce51bd2b28..35ffa71de56f87aa64144d56c2725468c810ec80 100644 --- a/core/java/com/android/internal/app/AlertActivity.java +++ b/core/java/com/android/internal/app/AlertActivity.java @@ -49,7 +49,7 @@ public abstract class AlertActivity extends Activity implements DialogInterface protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAlert = new AlertController(this, this, getWindow()); + mAlert = AlertController.create(this, this, getWindow()); mAlertParams = new AlertController.AlertParams(this); } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index b7ac6008a1fdc04bf0d32eb2e793869afd28b8ad..5aeb7f92eeda3e2609db0dfd7d350864842e495e 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; @@ -61,14 +62,15 @@ import android.widget.TextView; import java.lang.ref.WeakReference; public class AlertController { + public static final int MICRO = 1; private final Context mContext; private final DialogInterface mDialogInterface; - private final Window mWindow; + protected final Window mWindow; private CharSequence mTitle; - private CharSequence mMessage; - private ListView mListView; + protected CharSequence mMessage; + protected ListView mListView; private View mView; private int mViewLayoutResId; @@ -91,14 +93,14 @@ public class AlertController { private CharSequence mButtonNeutralText; private Message mButtonNeutralMessage; - private ScrollView mScrollView; + protected ScrollView mScrollView; private int mIconId = 0; private Drawable mIcon; private ImageView mIconView; private TextView mTitleView; - private TextView mMessageView; + protected TextView mMessageView; private View mCustomTitleView; private boolean mForceInverseBackground; @@ -176,7 +178,21 @@ public class AlertController { return outValue.data != 0; } - public AlertController(Context context, DialogInterface di, Window window) { + public static final AlertController create(Context context, DialogInterface di, Window window) { + final TypedArray a = context.obtainStyledAttributes( + null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); + int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0); + a.recycle(); + + switch (controllerType) { + case MICRO: + return new MicroAlertController(context, di, window); + default: + return new AlertController(context, di, window); + } + } + + protected AlertController(Context context, DialogInterface di, Window window) { mContext = context; mDialogInterface = di; mWindow = window; @@ -597,7 +613,7 @@ public class AlertController { } } - private void setupTitle(ViewGroup topPanel) { + protected void setupTitle(ViewGroup topPanel) { if (mCustomTitleView != null && mShowTitle) { // Add the custom title view directly to the topPanel layout final LayoutParams lp = new LayoutParams( @@ -643,7 +659,7 @@ public class AlertController { } } - private void setupContent(ViewGroup contentPanel) { + protected void setupContent(ViewGroup contentPanel) { mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView); mScrollView.setFocusable(false); @@ -680,7 +696,7 @@ public class AlertController { } } - private void setupButtons(ViewGroup buttonPanel) { + protected void setupButtons(ViewGroup buttonPanel) { int BIT_BUTTON_POSITIVE = 1; int BIT_BUTTON_NEGATIVE = 2; int BIT_BUTTON_NEUTRAL = 4; @@ -1072,7 +1088,8 @@ public class AlertController { public void bindView(View view, Context context, Cursor cursor) { CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1); text.setText(cursor.getString(mLabelIndex)); - listView.setItemChecked(cursor.getPosition(), + listView.setItemChecked( + cursor.getPosition(), cursor.getInt(mIsCheckedIndex) == 1); } diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index d552e542e5274ad7dfcff9f74dbe0d26156d36b1..294007946c773c4134cb171dfc96f0e159aaa317 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -16,10 +16,13 @@ package com.android.internal.app; +import com.android.internal.R; + import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; @@ -132,6 +135,16 @@ public class AssistUtils { } } + public void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener) { + try { + if (mVoiceInteractionManagerService != null) { + mVoiceInteractionManagerService.registerVoiceInteractionSessionListener(listener); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed to register voice interaction listener", e); + } + } + public ComponentName getAssistComponentForUser(int userId) { final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); @@ -156,4 +169,41 @@ public class AssistUtils { return null; } + public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) { + if (assistant == null) { + return false; + } + ApplicationInfo applicationInfo; + try { + applicationInfo = context.getPackageManager().getApplicationInfo( + assistant.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp(); + } + + private static boolean isDisclosureEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0; + } + + /** + * @return if the disclosure animation should trigger for the given assistant. + * + * Third-party assistants will always need to disclose, while the user can configure this for + * pre-installed assistants. + */ + public static boolean shouldDisclose(Context context, ComponentName assistant) { + if (!allowDisablingAssistDisclosure(context)) { + return true; + } + + return isDisclosureEnabled(context) || !isPreinstalledAssistant(context, assistant); + } + + public static boolean allowDisablingAssistDisclosure(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_allowDisablingAssistDisclosure); + } } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 216a4f1c9a4486f70a6e42aecd52d0c110a38465..5623a2cb430c15e0bf132a1ecc7ec903de6b2e22 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -79,6 +79,8 @@ interface IBatteryStats { String newHistoryName, int newType, boolean newUnimportantForLogging); void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, String historyName, int type); + void noteLongPartialWakelockStart(String name, String historyName, int uid); + void noteLongPartialWakelockFinish(String name, String historyName, int uid); void noteVibratorOn(int uid, long durationMillis); void noteVibratorOff(int uid); @@ -118,7 +120,7 @@ interface IBatteryStats { void noteWifiBatchedScanStoppedFromSource(in WorkSource ws); void noteWifiMulticastEnabledFromSource(in WorkSource ws); void noteWifiMulticastDisabledFromSource(in WorkSource ws); - void noteWifiRadioPowerState(int powerState, long timestampNs); + void noteWifiRadioPowerState(int powerState, long timestampNs, int uid); void noteNetworkInterfaceType(String iface, int type); void noteNetworkStatsEnabled(); void noteDeviceIdleMode(int mode, String activeReason, int activeUid); diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 1a963f30ca075e81ba70e93fa008387c800cde5d..033dd13046eab1a56c8b38008c1209c2ee0638a4 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -22,6 +22,7 @@ import android.os.Bundle; import com.android.internal.app.IVoiceInteractionSessionShowCallback; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.app.IVoiceInteractionSessionListener; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; import android.service.voice.IVoiceInteractionService; @@ -136,4 +137,9 @@ interface IVoiceInteractionManagerService { * Called when the lockscreen got shown. */ void onLockscreenShown(); + + /** + * Register a voice interaction listener. + */ + void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl new file mode 100644 index 0000000000000000000000000000000000000000..87749d26e4a0c09c2c275983388a4ca07486e104 --- /dev/null +++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package com.android.internal.app; + + oneway interface IVoiceInteractionSessionListener { + /** + * Called when a voice session is shown. + */ + void onVoiceSessionShown(); + + /** + * Called when a voice session is hidden. + */ + void onVoiceSessionHidden(); + } \ No newline at end of file diff --git a/core/java/com/android/internal/app/MicroAlertController.java b/core/java/com/android/internal/app/MicroAlertController.java new file mode 100644 index 0000000000000000000000000000000000000000..4431f3c6a50f682c0817d39b3deb7238246c7a46 --- /dev/null +++ b/core/java/com/android/internal/app/MicroAlertController.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.content.Context; +import android.content.DialogInterface; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.android.internal.app.AlertController; +import com.android.internal.R; + +public class MicroAlertController extends AlertController { + public MicroAlertController(Context context, DialogInterface di, Window window) { + super(context, di, window); + } + + @Override + protected void setupContent(ViewGroup contentPanel) { + // Special case for small screen - the scroll view is higher in hierarchy + mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView); + + // Special case for users that only want to display a String + mMessageView = (TextView) contentPanel.findViewById(R.id.message); + if (mMessageView == null) { + return; + } + + if (mMessage != null) { + mMessageView.setText(mMessage); + } else { + // no message, remove associated views + mMessageView.setVisibility(View.GONE); + contentPanel.removeView(mMessageView); + + if (mListView != null) { + // has ListView, swap scrollView with ListView + + // move topPanel into top of scrollParent + View topPanel = mScrollView.findViewById(R.id.topPanel); + ((ViewGroup) topPanel.getParent()).removeView(topPanel); + FrameLayout.LayoutParams topParams = + new FrameLayout.LayoutParams(topPanel.getLayoutParams()); + topParams.gravity = Gravity.TOP; + topPanel.setLayoutParams(topParams); + + // move buttonPanel into bottom of scrollParent + View buttonPanel = mScrollView.findViewById(R.id.buttonPanel); + ((ViewGroup) buttonPanel.getParent()).removeView(buttonPanel); + FrameLayout.LayoutParams buttonParams = + new FrameLayout.LayoutParams(buttonPanel.getLayoutParams()); + buttonParams.gravity = Gravity.BOTTOM; + buttonPanel.setLayoutParams(buttonParams); + + // remove scrollview + final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent(); + final int childIndex = scrollParent.indexOfChild(mScrollView); + scrollParent.removeViewAt(childIndex); + + // add list view + scrollParent.addView(mListView, + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // add top and button panel + scrollParent.addView(topPanel); + scrollParent.addView(buttonPanel); + } else { + // no content, just hide everything + contentPanel.setVisibility(View.GONE); + } + } + } + + @Override + protected void setupTitle(ViewGroup topPanel) { + super.setupTitle(topPanel); + if (topPanel.getVisibility() == View.GONE) { + topPanel.setVisibility(View.INVISIBLE); + } + } + + @Override + protected void setupButtons(ViewGroup buttonPanel) { + super.setupButtons(buttonPanel); + if (buttonPanel.getVisibility() == View.GONE) { + buttonPanel.setVisibility(View.INVISIBLE); + } + } +} diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java new file mode 100644 index 0000000000000000000000000000000000000000..03cd729db62ba0554d0db0cd46b7735b733575c0 --- /dev/null +++ b/core/java/com/android/internal/app/NightDisplayController.java @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings.Secure; +import android.util.Slog; + +import com.android.internal.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Calendar; +import java.util.Locale; + +/** + * Controller for managing Night display settings. + *

    + * Night display tints your screen red at night. This makes it easier to look at your screen in + * dim light and may help you fall asleep more easily. + */ +public final class NightDisplayController { + + private static final String TAG = "NightDisplayController"; + private static final boolean DEBUG = false; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT }) + public @interface AutoMode {} + + /** + * Auto mode value to prevent Night display from being automatically activated. It can still + * be activated manually via {@link #setActivated(boolean)}. + * + * @see #setAutoMode(int) + */ + public static final int AUTO_MODE_DISABLED = 0; + /** + * Auto mode value to automatically activate Night display at a specific start and end time. + * + * @see #setAutoMode(int) + * @see #setCustomStartTime(LocalTime) + * @see #setCustomEndTime(LocalTime) + */ + public static final int AUTO_MODE_CUSTOM = 1; + /** + * Auto mode value to automatically activate Night display from sunset to sunrise. + * + * @see #setAutoMode(int) + */ + public static final int AUTO_MODE_TWILIGHT = 2; + + private final Context mContext; + private final int mUserId; + + private final ContentObserver mContentObserver; + + private Callback mCallback; + + public NightDisplayController(@NonNull Context context) { + this(context, UserHandle.myUserId()); + } + + public NightDisplayController(@NonNull Context context, int userId) { + mContext = context.getApplicationContext(); + mUserId = userId; + + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + + final String setting = uri == null ? null : uri.getLastPathSegment(); + if (setting != null) { + onSettingChanged(setting); + } + } + }; + } + + /** + * Returns {@code true} when Night display is activated (the display is tinted red). + */ + public boolean isActivated() { + return Secure.getIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1; + } + + /** + * Sets whether Night display should be activated. + * + * @param activated {@code true} if Night display should be activated + * @return {@code true} if the activated value was set successfully + */ + public boolean setActivated(boolean activated) { + return Secure.putIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId); + } + + /** + * Returns the current auto mode value controlling when Night display will be automatically + * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or + * {@link #AUTO_MODE_TWILIGHT}. + */ + public @AutoMode int getAutoMode() { + int autoMode = Secure.getIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId); + if (autoMode == -1) { + if (DEBUG) { + Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE); + } + autoMode = mContext.getResources().getInteger( + R.integer.config_defaultNightDisplayAutoMode); + } + + if (autoMode != AUTO_MODE_DISABLED + && autoMode != AUTO_MODE_CUSTOM + && autoMode != AUTO_MODE_TWILIGHT) { + Slog.e(TAG, "Invalid autoMode: " + autoMode); + autoMode = AUTO_MODE_DISABLED; + } + + return autoMode; + } + + /** + * Sets the current auto mode value controlling when Night display will be automatically + * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or + * {@link #AUTO_MODE_TWILIGHT}. + * + * @param autoMode the new auto mode to use + * @return {@code true} if new auto mode was set successfully + */ + public boolean setAutoMode(@AutoMode int autoMode) { + if (autoMode != AUTO_MODE_DISABLED + && autoMode != AUTO_MODE_CUSTOM + && autoMode != AUTO_MODE_TWILIGHT) { + throw new IllegalArgumentException("Invalid autoMode: " + autoMode); + } + + return Secure.putIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId); + } + + /** + * Returns the local time when Night display will be automatically activated when using + * {@link #AUTO_MODE_CUSTOM}. + */ + public @NonNull LocalTime getCustomStartTime() { + int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId); + if (startTimeValue == -1) { + if (DEBUG) { + Slog.d(TAG, "Using default value for setting: " + + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME); + } + startTimeValue = mContext.getResources().getInteger( + R.integer.config_defaultNightDisplayCustomStartTime); + } + + return LocalTime.valueOf(startTimeValue); + } + + /** + * Sets the local time when Night display will be automatically activated when using + * {@link #AUTO_MODE_CUSTOM}. + * + * @param startTime the local time to automatically activate Night display + * @return {@code true} if the new custom start time was set successfully + */ + public boolean setCustomStartTime(@NonNull LocalTime startTime) { + if (startTime == null) { + throw new IllegalArgumentException("startTime cannot be null"); + } + return Secure.putIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId); + } + + /** + * Returns the local time when Night display will be automatically deactivated when using + * {@link #AUTO_MODE_CUSTOM}. + */ + public @NonNull LocalTime getCustomEndTime() { + int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId); + if (endTimeValue == -1) { + if (DEBUG) { + Slog.d(TAG, "Using default value for setting: " + + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME); + } + endTimeValue = mContext.getResources().getInteger( + R.integer.config_defaultNightDisplayCustomEndTime); + } + + return LocalTime.valueOf(endTimeValue); + } + + /** + * Sets the local time when Night display will be automatically deactivated when using + * {@link #AUTO_MODE_CUSTOM}. + * + * @param endTime the local time to automatically deactivate Night display + * @return {@code true} if the new custom end time was set successfully + */ + public boolean setCustomEndTime(@NonNull LocalTime endTime) { + if (endTime == null) { + throw new IllegalArgumentException("endTime cannot be null"); + } + return Secure.putIntForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId); + } + + private void onSettingChanged(@NonNull String setting) { + if (DEBUG) { + Slog.d(TAG, "onSettingChanged: " + setting); + } + + if (mCallback != null) { + switch (setting) { + case Secure.NIGHT_DISPLAY_ACTIVATED: + mCallback.onActivated(isActivated()); + break; + case Secure.NIGHT_DISPLAY_AUTO_MODE: + mCallback.onAutoModeChanged(getAutoMode()); + break; + case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME: + mCallback.onCustomStartTimeChanged(getCustomStartTime()); + break; + case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME: + mCallback.onCustomEndTimeChanged(getCustomEndTime()); + break; + } + } + } + + /** + * Register a callback to be invoked whenever the Night display settings are changed. + */ + public void setListener(Callback callback) { + final Callback oldCallback = mCallback; + if (oldCallback != callback) { + mCallback = callback; + + if (callback == null) { + // Stop listening for changes now that there IS NOT a listener. + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } else if (oldCallback == null) { + // Start listening for changes now that there IS a listener. + final ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME), + false /* notifyForDescendants */, mContentObserver, mUserId); + cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME), + false /* notifyForDescendants */, mContentObserver, mUserId); + } + } + } + + /** + * Returns {@code true} if Night display is supported by the device. + */ + public static boolean isAvailable(Context context) { + return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable); + } + + /** + * A time without a time-zone or date. + */ + public static class LocalTime { + + /** + * The hour of the day from 0 - 23. + */ + public final int hourOfDay; + /** + * The minute within the hour from 0 - 59. + */ + public final int minute; + + public LocalTime(int hourOfDay, int minute) { + if (hourOfDay < 0 || hourOfDay > 23) { + throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay); + } else if (minute < 0 || minute > 59) { + throw new IllegalArgumentException("Invalid minute: " + minute); + } + + this.hourOfDay = hourOfDay; + this.minute = minute; + } + + /** + * Returns the first date time corresponding to this local time that occurs before the + * provided date time. + * + * @param time the date time to compare against + * @return the prior date time corresponding to this local time + */ + public Calendar getDateTimeBefore(Calendar time) { + final Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, time.get(Calendar.YEAR)); + c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR)); + + c.set(Calendar.HOUR_OF_DAY, hourOfDay); + c.set(Calendar.MINUTE, minute); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + // Check if the local time has past, if so return the same time tomorrow. + if (c.after(time)) { + c.add(Calendar.DATE, -1); + } + + return c; + } + + /** + * Returns the first date time corresponding to this local time that occurs after the + * provided date time. + * + * @param time the date time to compare against + * @return the next date time corresponding to this local time + */ + public Calendar getDateTimeAfter(Calendar time) { + final Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, time.get(Calendar.YEAR)); + c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR)); + + c.set(Calendar.HOUR_OF_DAY, hourOfDay); + c.set(Calendar.MINUTE, minute); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + // Check if the local time has past, if so return the same time tomorrow. + if (c.before(time)) { + c.add(Calendar.DATE, 1); + } + + return c; + } + + /** + * Returns a local time corresponding the given number of milliseconds from midnight. + * + * @param millis the number of milliseconds from midnight + * @return the corresponding local time + */ + private static LocalTime valueOf(int millis) { + final int hourOfDay = (millis / 3600000) % 24; + final int minutes = (millis / 60000) % 60; + return new LocalTime(hourOfDay, minutes); + } + + /** + * Returns the local time represented as milliseconds from midnight. + */ + private int toMillis() { + return hourOfDay * 3600000 + minute * 60000; + } + + @Override + public String toString() { + return String.format(Locale.US, "%02d:%02d", hourOfDay, minute); + } + } + + /** + * Callback invoked whenever the Night display settings are changed. + */ + public interface Callback { + /** + * Callback invoked when the activated state changes. + * + * @param activated {@code true} if Night display is activated + */ + default void onActivated(boolean activated) {} + /** + * Callback invoked when the auto mode changes. + * + * @param autoMode the auto mode to use + */ + default void onAutoModeChanged(int autoMode) {} + /** + * Callback invoked when the time to automatically activate Night display changes. + * + * @param startTime the local time to automatically activate Night display + */ + default void onCustomStartTimeChanged(LocalTime startTime) {} + /** + * Callback invoked when the time to automatically deactivate Night display changes. + * + * @param endTime the local time to automatically deactivate Night display + */ + default void onCustomEndTimeChanged(LocalTime endTime) {} + } +} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 085e159c8b21765c8bb322a9a7d6253f0ea50a8f..0a4ac0d44db4b83e94dd42aa9aa945612d5131c1 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -69,9 +69,13 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto; import com.android.internal.widget.ResolverDrawerLayout; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -359,6 +363,12 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { onSetupVoiceInteraction(); } + final Set categories = intent.getCategories(); + MetricsLogger.action(this, mAdapter.hasFilteredItem() + ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED + : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED, + intent.getAction() + ":" + intent.getType() + ":" + + (categories != null ? Arrays.toString(categories.toArray()) : "")); } public final void setFilteredComponents(ComponentName[] components) { @@ -649,6 +659,19 @@ public class ResolverActivity extends Activity { TargetInfo target = mAdapter.targetInfoForPosition(which, filtered); if (onTargetSelected(target, always)) { + if (always && filtered) { + MetricsLogger.action( + this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS); + } else if (filtered) { + MetricsLogger.action( + this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE); + } else { + MetricsLogger.action( + this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP); + } + MetricsLogger.action(this, mAdapter.hasFilteredItem() + ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED + : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED); finish(); } } diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java index d24cefeecf077239d378426375c795c03433338d..0a539f19c48aa416158be63f64bfd59cdc3aaf14 100644 --- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java +++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java @@ -37,6 +37,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.Window; import android.widget.TextView; import com.android.internal.R; @@ -59,6 +60,9 @@ public class UnlaunchableAppActivity extends Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // As this activity has nothing to show, we should hide the title bar also + // TODO: Use AlertActivity so we don't need to hide title bar and create a dialog + requestWindowFeature(Window.FEATURE_NO_TITLE); Intent intent = getIntent(); mReason = intent.getIntExtra(EXTRA_UNLAUNCHABLE_REASON, -1); mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index 4260e50a31f26120cf34b33409424d810e0af968..951a45a7b4376a42de8a624c916e1acac6f4cb94 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -34,7 +34,7 @@ interface IAppWidgetService { // for AppWidgetHost // ParceledListSlice startListening(IAppWidgetHost host, String callingPackage, int hostId, - in int[] appWidgetIds, out int[] updatedIds); + in int[] appWidgetIds); void stopListening(String callingPackage, int hostId); int allocateAppWidgetId(String callingPackage, int hostId); void deleteAppWidgetId(String callingPackage, int appWidgetId); diff --git a/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl new file mode 100644 index 0000000000000000000000000000000000000000..8abc8074b5acc376deff9976b0fdcc15c8d032e4 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl @@ -0,0 +1,27 @@ +/* +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.inputmethod; + +import android.os.IBinder; + +/** + * {@hide} + */ +interface IInputContentUriToken { + void take(); + void release(); +} diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 5c92f3c68c774b0c276fc81b7d11da850504df35..9a5543afcd78962850e48db93bb26cf11e10843e 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -176,6 +176,11 @@ public class VpnProfile implements Cloneable, Parcelable { * connection. */ public boolean isValidLockdownProfile() { + // b/7064069: lockdown firewall blocks ports that would be used for PPTP + if (type == TYPE_PPTP) { + return false; + } + try { InetAddress.parseNumericAddress(server); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index a1df8c186982dbd2324d0a4889b44536355f842d..2538d60f1924836ed10604744075ed53350a27d8 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -108,7 +108,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 147 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 150 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -1415,22 +1415,6 @@ public class BatteryStatsImpl extends BatteryStats { mUnpluggedReportedCount = 0; return true; } - - @Override - public void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) { - super.writeSummaryFromParcelLocked(out, batteryRealtime); - out.writeLong(mCurrentReportedTotalTime); - out.writeInt(mCurrentReportedCount); - out.writeInt(mTrackingReportedValues ? 1 : 0); - } - - @Override - public void readSummaryFromParcelLocked(Parcel in) { - super.readSummaryFromParcelLocked(in); - mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = in.readLong(); - mUnpluggedReportedCount = mCurrentReportedCount = in.readInt(); - mTrackingReportedValues = in.readInt() == 1; - } } /** @@ -1566,6 +1550,186 @@ public class BatteryStatsImpl extends BatteryStats { } } + + /** + * A StopwatchTimer that also tracks the total and max individual + * time spent active according to the given timebase. Whereas + * StopwatchTimer apportions the time amongst all in the pool, + * the total and max durations are not apportioned. + */ + public static class DurationTimer extends StopwatchTimer { + /** + * The time (in ms) that the timer was last acquired or the time base + * last (re-)started. Increasing the nesting depth does not reset this time. + * + * -1 if the timer is currently not running or the time base is not running. + * + * If written to a parcel, the start time is reset, as is mNesting in the base class + * StopwatchTimer. + */ + long mStartTimeMs = -1; + + /** + * The longest time period (in ms) that the timer has been active. + */ + long mMaxDurationMs; + + /** + * The total time (in ms) that that the timer has been active since reset(). + */ + long mCurrentDurationMs; + + public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList timerPool, + TimeBase timeBase, Parcel in) { + super(clocks, uid, type, timerPool, timeBase, in); + mMaxDurationMs = in.readLong(); + } + + public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList timerPool, + TimeBase timeBase) { + super(clocks, uid, type, timerPool, timeBase); + } + + @Override + public void writeToParcel(Parcel out, long elapsedRealtimeUs) { + super.writeToParcel(out, elapsedRealtimeUs); + out.writeLong(mMaxDurationMs); + } + + /** + * Write the summary to the parcel. + * + * Since the time base is probably meaningless after we come back, reading + * from this will have the effect of stopping the timer. So here all we write + * is the max duration. + */ + @Override + public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { + super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + out.writeLong(mMaxDurationMs); + } + + /** + * Read the summary parcel. + * + * Has the side effect of stopping the timer. + */ + @Override + public void readSummaryFromParcelLocked(Parcel in) { + super.readSummaryFromParcelLocked(in); + mMaxDurationMs = in.readLong(); + mStartTimeMs = -1; + mCurrentDurationMs = 0; + } + + /** + * The TimeBase time started (again). + * + * If the timer is also running, store the start time. + */ + public void onTimeStarted(long elapsedRealtimeUs, long baseUptime, long baseRealtime) { + super.onTimeStarted(elapsedRealtimeUs, baseUptime, baseRealtime); + if (mNesting > 0) { + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } + } + + /** + * The TimeBase stopped running. + * + * If the timer is running, add the duration into mCurrentDurationMs. + */ + @Override + public void onTimeStopped(long elapsedRealtimeUs, long baseUptime, long baseRealtime) { + super.onTimeStopped(elapsedRealtimeUs, baseUptime, baseRealtime); + if (mNesting > 0) { + mCurrentDurationMs += (elapsedRealtimeUs / 1000) - mStartTimeMs; + } + mStartTimeMs = -1; + } + + @Override + public void logState(Printer pw, String prefix) { + super.logState(pw, prefix); + } + + @Override + public void startRunningLocked(long elapsedRealtimeMs) { + super.startRunningLocked(elapsedRealtimeMs); + if (mNesting == 1 && mTimeBase.isRunning()) { + // Just started + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } + } + + /** + * Decrements the mNesting ref-count on this timer. + * + * If it actually stopped (mNesting went to 0), then possibly update + * mMaxDuration if the current duration was the longest ever. + */ + @Override + public void stopRunningLocked(long elapsedRealtimeMs) { + super.stopRunningLocked(elapsedRealtimeMs); + if (mNesting == 0) { + final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs); + if (durationMs > mMaxDurationMs) { + mMaxDurationMs = durationMs; + } + mStartTimeMs = -1; + mCurrentDurationMs = 0; + } + } + + @Override + public boolean reset(boolean detachIfReset) { + boolean result = super.reset(detachIfReset); + mMaxDurationMs = 0; + mCurrentDurationMs = 0; + if (mNesting > 0) { + mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000; + } else { + mStartTimeMs = -1; + } + return result; + } + + /** + * Returns the max duration that this timer has ever seen. + * + * Note that this time is NOT split between the timers in the timer group that + * this timer is attached to. It is the TOTAL time. + */ + @Override + public long getMaxDurationMsLocked(long elapsedRealtimeMs) { + if (mNesting > 0) { + final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs); + if (durationMs > mMaxDurationMs) { + return durationMs; + } + } + return mMaxDurationMs; + } + + /** + * Returns the time since the timer was started. + * + * Note that this time is NOT split between the timers in the timer group that + * this timer is attached to. It is the TOTAL time. + */ + @Override + public long getCurrentDurationMsLocked(long elapsedRealtimeMs) { + long durationMs = mCurrentDurationMs; + if (mNesting > 0) { + if (mTimeBase.isRunning()) { + durationMs += (mTimeBase.getRealtime(elapsedRealtimeMs*1000)/1000) + - mStartTimeMs; + } + } + return durationMs; + } + } + /** * State for keeping track of timing information. */ @@ -1791,11 +1955,17 @@ public class BatteryStatsImpl extends BatteryStats { public abstract class OverflowArrayMap { private static final String OVERFLOW_NAME = "*overflow*"; + final int mUid; final ArrayMap mMap = new ArrayMap<>(); T mCurOverflow; ArrayMap mActiveOverflow; + long mLastOverflowTime; + long mLastOverflowFinishTime; + long mLastClearTime; + long mLastCleanupTime; - public OverflowArrayMap() { + public OverflowArrayMap(int uid) { + mUid = uid; } public ArrayMap getMap() { @@ -1803,6 +1973,7 @@ public class BatteryStatsImpl extends BatteryStats { } public void clear() { + mLastClearTime = SystemClock.elapsedRealtime(); mMap.clear(); mCurOverflow = null; mActiveOverflow = null; @@ -1819,6 +1990,7 @@ public class BatteryStatsImpl extends BatteryStats { } public void cleanup() { + mLastCleanupTime = SystemClock.elapsedRealtime(); if (mActiveOverflow != null) { if (mActiveOverflow.size() == 0) { mActiveOverflow = null; @@ -1885,6 +2057,7 @@ public class BatteryStatsImpl extends BatteryStats { mActiveOverflow = new ArrayMap<>(); } mActiveOverflow.put(name, new MutableInt(1)); + mLastOverflowTime = SystemClock.elapsedRealtime(); return obj; } @@ -1914,6 +2087,7 @@ public class BatteryStatsImpl extends BatteryStats { over.value--; if (over.value <= 0) { mActiveOverflow.remove(name); + mLastOverflowFinishTime = SystemClock.elapsedRealtime(); } return obj; } @@ -1922,9 +2096,35 @@ public class BatteryStatsImpl extends BatteryStats { // Huh, they are stopping an active operation but we can't find one! // That's not good. - Slog.wtf(TAG, "Unable to find object for " + name + " mapsize=" - + mMap.size() + " activeoverflow=" + mActiveOverflow - + " curoverflow=" + mCurOverflow); + StringBuilder sb = new StringBuilder(); + sb.append("Unable to find object for "); + sb.append(name); + sb.append(" in uid "); + sb.append(mUid); + sb.append(" mapsize="); + sb.append(mMap.size()); + sb.append(" activeoverflow="); + sb.append(mActiveOverflow); + sb.append(" curoverflow="); + sb.append(mCurOverflow); + long now = SystemClock.elapsedRealtime(); + if (mLastOverflowTime != 0) { + sb.append(" lastOverflowTime="); + TimeUtils.formatDuration(mLastOverflowTime-now, sb); + } + if (mLastOverflowFinishTime != 0) { + sb.append(" lastOverflowFinishTime="); + TimeUtils.formatDuration(mLastOverflowFinishTime-now, sb); + } + if (mLastClearTime != 0) { + sb.append(" lastClearTime="); + TimeUtils.formatDuration(mLastClearTime-now, sb); + } + if (mLastCleanupTime != 0) { + sb.append(" lastCleanupTime="); + TimeUtils.formatDuration(mLastCleanupTime-now, sb); + } + Slog.wtf(TAG, sb.toString()); return null; } @@ -3285,6 +3485,36 @@ public class BatteryStatsImpl extends BatteryStats { } } + public void noteLongPartialWakelockStart(String name, String historyName, int uid) { + uid = mapUid(uid); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + if (historyName == null) { + historyName = name; + } + if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName, uid, + 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_START, + historyName, uid); + } + + public void noteLongPartialWakelockFinish(String name, String historyName, int uid) { + uid = mapUid(uid); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + if (historyName == null) { + historyName = name; + } + if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName, uid, + 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, + historyName, uid); + } + void aggregateLastWakeupUptimeLocked(long uptimeMs) { if (mLastWakeupReason != null) { long deltaUptime = uptimeMs - mLastWakeupUptimeMs; @@ -3554,6 +3784,14 @@ public class BatteryStatsImpl extends BatteryStats { mNumConnectivityChange++; } + private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis, + final long uptimeMillis, int uid) { + uid = mapUid(uid); + addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "", + uid); + getUidStatsLocked(uid).noteMobileRadioApWakeupLocked(); + } + public void noteMobileRadioPowerState(int powerState, long timestampNs, int uid) { final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); @@ -3563,6 +3801,10 @@ public class BatteryStatsImpl extends BatteryStats { powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH; if (active) { + if (uid > 0) { + noteMobileRadioApWakeupLocked(elapsedRealtime, uptime, uid); + } + mMobileRadioActiveStartTime = realElapsedRealtimeMs = timestampNs / (1000 * 1000); mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG; } else { @@ -4239,7 +4481,15 @@ public class BatteryStatsImpl extends BatteryStats { } } - public void noteWifiRadioPowerState(int powerState, long timestampNs) { + private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis, + final long uptimeMillis, int uid) { + uid = mapUid(uid); + addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "", + uid); + getUidStatsLocked(uid).noteWifiRadioApWakeupLocked(); + } + + public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) { final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); if (mWifiRadioPowerState != powerState) { @@ -4247,6 +4497,9 @@ public class BatteryStatsImpl extends BatteryStats { powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH; if (active) { + if (uid > 0) { + noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid); + } mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG; } else { mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG; @@ -4874,6 +5127,33 @@ public class BatteryStatsImpl extends BatteryStats { return mUidStats; } + private static void detachTimerIfNotNull(BatteryStatsImpl.Timer timer) { + if (timer != null) { + timer.detach(); + } + } + + private static boolean resetTimerIfNotNull(BatteryStatsImpl.Timer timer, + boolean detachIfReset) { + if (timer != null) { + return timer.reset(detachIfReset); + } + return true; + } + + private static void detachLongCounterIfNotNull(LongSamplingCounter counter) { + if (counter != null) { + counter.detach(); + } + } + + private static void resetLongCounterIfNotNull(LongSamplingCounter counter, + boolean detachIfReset) { + if (counter != null) { + counter.reset(detachIfReset); + } + } + /** * The statistics associated with a particular uid. */ @@ -4920,6 +5200,16 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mMobileRadioActiveTime; LongSamplingCounter mMobileRadioActiveCount; + /** + * How many times this UID woke up the Application Processor due to a Mobile radio packet. + */ + private LongSamplingCounter mMobileRadioApWakeupCount; + + /** + * How many times this UID woke up the Application Processor due to a Wifi packet. + */ + private LongSamplingCounter mWifiRadioApWakeupCount; + /** * The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode. * Can be null if the UID has had no such activity. @@ -4994,18 +5284,18 @@ public class BatteryStatsImpl extends BatteryStats { mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); mCpuPower = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); - mWakelockStats = mBsi.new OverflowArrayMap() { + mWakelockStats = mBsi.new OverflowArrayMap(uid) { @Override public Wakelock instantiateObject() { return new Wakelock(mBsi, Uid.this); } }; - mSyncStats = mBsi.new OverflowArrayMap() { + mSyncStats = mBsi.new OverflowArrayMap(uid) { @Override public StopwatchTimer instantiateObject() { return new StopwatchTimer(mBsi.mClocks, Uid.this, SYNC, null, mBsi.mOnBatteryTimeBase); } }; - mJobStats = mBsi.new OverflowArrayMap() { + mJobStats = mBsi.new OverflowArrayMap(uid) { @Override public StopwatchTimer instantiateObject() { return new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null, mBsi.mOnBatteryTimeBase); @@ -5629,6 +5919,36 @@ public class BatteryStatsImpl extends BatteryStats { return 0; } + public void noteMobileRadioApWakeupLocked() { + if (mMobileRadioApWakeupCount == null) { + mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + } + mMobileRadioApWakeupCount.addCountLocked(1); + } + + @Override + public long getMobileRadioApWakeupCount(int which) { + if (mMobileRadioApWakeupCount != null) { + return mMobileRadioApWakeupCount.getCountLocked(which); + } + return 0; + } + + public void noteWifiRadioApWakeupLocked() { + if (mWifiRadioApWakeupCount == null) { + mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + } + mWifiRadioApWakeupCount.addCountLocked(1); + } + + @Override + public long getWifiRadioApWakeupCount(int which) { + if (mWifiRadioApWakeupCount != null) { + return mWifiRadioApWakeupCount.getCountLocked(which); + } + return 0; + } + void initNetworkActivityLocked() { mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; @@ -5671,24 +5991,14 @@ public class BatteryStatsImpl extends BatteryStats { active |= !mWifiMulticastTimer.reset(false); active |= mWifiMulticastEnabled; } - if (mAudioTurnedOnTimer != null) { - active |= !mAudioTurnedOnTimer.reset(false); - } - if (mVideoTurnedOnTimer != null) { - active |= !mVideoTurnedOnTimer.reset(false); - } - if (mFlashlightTurnedOnTimer != null) { - active |= !mFlashlightTurnedOnTimer.reset(false); - } - if (mCameraTurnedOnTimer != null) { - active |= !mCameraTurnedOnTimer.reset(false); - } - if (mForegroundActivityTimer != null) { - active |= !mForegroundActivityTimer.reset(false); - } - if (mBluetoothScanTimer != null) { - active |= !mBluetoothScanTimer.reset(false); - } + + active |= !resetTimerIfNotNull(mAudioTurnedOnTimer, false); + active |= !resetTimerIfNotNull(mVideoTurnedOnTimer, false); + active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false); + active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false); + active |= !resetTimerIfNotNull(mForegroundActivityTimer, false); + active |= !resetTimerIfNotNull(mBluetoothScanTimer, false); + if (mProcessStateTimer != null) { for (int i = 0; i < NUM_PROCESS_STATE; i++) { if (mProcessStateTimer[i] != null) { @@ -5749,6 +6059,9 @@ public class BatteryStatsImpl extends BatteryStats { } } + resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false); + resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false); + final ArrayMap wakeStats = mWakelockStats.getMap(); for (int iw=wakeStats.size()-1; iw>=0; iw--) { Wakelock wl = wakeStats.valueAt(iw); @@ -5908,6 +6221,9 @@ public class BatteryStatsImpl extends BatteryStats { } } } + + detachLongCounterIfNotNull(mMobileRadioApWakeupCount); + detachLongCounterIfNotNull(mWifiRadioApWakeupCount); } return !active; @@ -6114,6 +6430,20 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + + if (mMobileRadioApWakeupCount != null) { + out.writeInt(1); + mMobileRadioApWakeupCount.writeToParcel(out); + } else { + out.writeInt(0); + } + + if (mWifiRadioApWakeupCount != null) { + out.writeInt(1); + mWifiRadioApWakeupCount.writeToParcel(out); + } else { + out.writeInt(0); + } } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { @@ -6338,6 +6668,18 @@ public class BatteryStatsImpl extends BatteryStats { } else { mCpuClusterSpeed = null; } + + if (in.readInt() != 0) { + mMobileRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); + } else { + mMobileRadioApWakeupCount = null; + } + + if (in.readInt() != 0) { + mWifiRadioApWakeupCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); + } else { + mWifiRadioApWakeupCount = null; + } } /** @@ -6357,7 +6699,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * How long (in ms) this uid has been keeping the device partially awake. */ - StopwatchTimer mTimerPartial; + DurationTimer mTimerPartial; /** * How long (in ms) this uid has been keeping the device fully awake. @@ -6386,8 +6728,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param in the Parcel to be read from. * return a new Timer, or null. */ - private StopwatchTimer readTimerFromParcel(int type, ArrayList pool, - TimeBase timeBase, Parcel in) { + private StopwatchTimer readStopwatchTimerFromParcel(int type, + ArrayList pool, TimeBase timeBase, Parcel in) { if (in.readInt() == 0) { return null; } @@ -6395,6 +6737,22 @@ public class BatteryStatsImpl extends BatteryStats { return new StopwatchTimer(mBsi.mClocks, mUid, type, pool, timeBase, in); } + /** + * Reads a possibly null Timer from a Parcel. The timer is associated with the + * proper timer pool from the given BatteryStatsImpl object. + * + * @param in the Parcel to be read from. + * return a new Timer, or null. + */ + private DurationTimer readDurationTimerFromParcel(int type, + ArrayList pool, TimeBase timeBase, Parcel in) { + if (in.readInt() == 0) { + return null; + } + + return new DurationTimer(mBsi.mClocks, mUid, type, pool, timeBase, in); + } + boolean reset() { boolean wlactive = false; if (mTimerFull != null) { @@ -6431,11 +6789,14 @@ public class BatteryStatsImpl extends BatteryStats { } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { - mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL, + mTimerPartial = readDurationTimerFromParcel(WAKE_TYPE_PARTIAL, mBsi.mPartialTimers, screenOffTimeBase, in); - mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL, mBsi.mFullTimers, timeBase, in); - mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW, mBsi.mWindowTimers, timeBase, in); - mTimerDraw = readTimerFromParcel(WAKE_TYPE_DRAW, mBsi.mDrawTimers, timeBase, in); + mTimerFull = readStopwatchTimerFromParcel(WAKE_TYPE_FULL, + mBsi.mFullTimers, timeBase, in); + mTimerWindow = readStopwatchTimerFromParcel(WAKE_TYPE_WINDOW, + mBsi.mWindowTimers, timeBase, in); + mTimerDraw = readStopwatchTimerFromParcel(WAKE_TYPE_DRAW, + mBsi.mDrawTimers, timeBase, in); } void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { @@ -6457,40 +6818,43 @@ public class BatteryStatsImpl extends BatteryStats { } public StopwatchTimer getStopwatchTimer(int type) { - StopwatchTimer t; switch (type) { - case WAKE_TYPE_PARTIAL: - t = mTimerPartial; + case WAKE_TYPE_PARTIAL: { + DurationTimer t = mTimerPartial; if (t == null) { - t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL, + t = new DurationTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL, mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase); mTimerPartial = t; } return t; - case WAKE_TYPE_FULL: - t = mTimerFull; + } + case WAKE_TYPE_FULL: { + StopwatchTimer t = mTimerFull; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_FULL, mBsi.mFullTimers, mBsi.mOnBatteryTimeBase); mTimerFull = t; } return t; - case WAKE_TYPE_WINDOW: - t = mTimerWindow; + } + case WAKE_TYPE_WINDOW: { + StopwatchTimer t = mTimerWindow; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_WINDOW, mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase); mTimerWindow = t; } return t; - case WAKE_TYPE_DRAW: - t = mTimerDraw; + } + case WAKE_TYPE_DRAW: { + StopwatchTimer t = mTimerDraw; if (t == null) { t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_DRAW, mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase); mTimerDraw = t; } return t; + } default: throw new IllegalArgumentException("type=" + type); } @@ -10348,6 +10712,20 @@ public class BatteryStatsImpl extends BatteryStats { u.mCpuClusterSpeed = null; } + if (in.readInt() != 0) { + u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase); + u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in); + } else { + u.mMobileRadioApWakeupCount = null; + } + + if (in.readInt() != 0) { + u.mWifiRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase); + u.mWifiRadioApWakeupCount.readSummaryFromParcelLocked(in); + } else { + u.mWifiRadioApWakeupCount = null; + } + int NW = in.readInt(); if (NW > 100) { throw new ParcelFormatException("File corrupt: too many wake locks " + NW); @@ -10708,6 +11086,20 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } + if (u.mMobileRadioApWakeupCount != null) { + out.writeInt(1); + u.mMobileRadioApWakeupCount.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + + if (u.mWifiRadioApWakeupCount != null) { + out.writeInt(1); + u.mWifiRadioApWakeupCount.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + final ArrayMap wakeStats = u.mWakelockStats.getMap(); int NW = wakeStats.size(); out.writeInt(NW); diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java index c828d11fd3d20e7bbdc3d1817441b86a6cdc3924..e8919ede82b52bd5553972d75c290bf896755682 100644 --- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -120,7 +120,7 @@ public class KernelUidCpuTimeReader { sb.append(" s="); TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb); sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms"); - Slog.wtf(TAG, sb.toString()); + Slog.e(TAG, sb.toString()); userTimeDeltaUs = 0; systemTimeDeltaUs = 0; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9c960c04d4914e016142e3ece5f86fb25ddb1361..68299615b5b9f9051b9e67a96cc53e6b873c9060 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -586,21 +586,42 @@ public class ZygoteInit { // System server is fully AOTed and never profiled // for profile guided compilation. // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING? - final int dexoptNeeded = DexFile.getDexOptNeeded( + + int dexoptNeeded; + try { + dexoptNeeded = DexFile.getDexOptNeeded( classPathElement, instructionSet, "speed", false /* newProfile */); + } catch (FileNotFoundException ignored) { + // Do not add to the classpath. + Log.w(TAG, "Missing classpath element for system server: " + classPathElement); + continue; + } catch (IOException e) { + // Not fully clear what to do here as we don't know the cause of the + // IO exception. Add to the classpath to be conservative, but don't + // attempt to compile it. + Log.w(TAG, "Error checking classpath element for system server: " + + classPathElement, e); + dexoptNeeded = DexFile.NO_DEXOPT_NEEDED; + } + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { - installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet, - dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/, - sharedLibraries); + try { + installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet, + dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/, + sharedLibraries); + } catch (InstallerException e) { + // Ignore (but log), we need this on the classpath for fallback mode. + Log.w(TAG, "Failed compiling classpath element for system server: " + + classPathElement, e); + } } + if (!sharedLibraries.isEmpty()) { sharedLibraries += ":"; } sharedLibraries += classPathElement; } - } catch (IOException | InstallerException e) { - throw new RuntimeException("Error starting system_server", e); } finally { installer.disconnect(); } @@ -753,7 +774,7 @@ public class ZygoteInit { closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); - } catch (RuntimeException ex) { + } catch (Throwable ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 488f769cdf3182188d95d33e444033a922eb8ec0..619303f34c32e732c2a4d3de284542cbdd8cd819 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -371,6 +371,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame systemInsets.bottom); final int rightInset = DecorView.getColorViewRightInset(stableInsets.right, systemInsets.right); + final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left, + systemInsets.left); if (mStatusBarColor != null) { mStatusBarColor.setBounds(0, 0, left + width, topInset); mStatusBarColor.draw(canvas); @@ -380,9 +382,11 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame // don't want the navigation bar background be moving around when resizing in docked mode. // However, we need it for the transitions into/out of docked mode. if (mNavigationBarColor != null && fullscreen) { - final int size = DecorView.getNavBarSize(bottomInset, rightInset); + final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset); if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) { mNavigationBarColor.setBounds(width - size, 0, width, height); + } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) { + mNavigationBarColor.setBounds(0, 0, size, height); } else { mNavigationBarColor.setBounds(0, height - size, width, height); } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 3cf7a4e6b9ecc75f52493fd0203e2531f9cfdea8..92ab3246e354b3e427369f5701ddd836fe4332e6 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -97,11 +97,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; /** @hide */ @@ -162,13 +162,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private final ColorViewState mStatusColorViewState = new ColorViewState( SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, - Gravity.TOP, Gravity.LEFT, + Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, com.android.internal.R.id.statusBarBackground, FLAG_FULLSCREEN); private final ColorViewState mNavigationColorViewState = new ColorViewState( SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, - Gravity.BOTTOM, Gravity.RIGHT, + Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, com.android.internal.R.id.navigationBarBackground, 0 /* hideWindowFlag */); @@ -184,9 +184,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private int mLastTopInset = 0; private int mLastBottomInset = 0; private int mLastRightInset = 0; + private int mLastLeftInset = 0; private boolean mLastHasTopStableInset = false; private boolean mLastHasBottomStableInset = false; private boolean mLastHasRightStableInset = false; + private boolean mLastHasLeftStableInset = false; private int mLastWindowFlags = 0; private boolean mLastShouldAlwaysConsumeNavBar = false; @@ -991,12 +993,21 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return Math.min(stableRight, systemRight); } + static int getColorViewLeftInset(int stableLeft, int systemLeft) { + return Math.min(stableLeft, systemLeft); + } + static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { return bottomInset == 0 && rightInset > 0; } - static int getNavBarSize(int bottomInset, int rightInset) { - return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset; + static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { + return bottomInset == 0 && leftInset > 0; + } + + static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { + return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset + : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset; } WindowInsets updateColorViews(WindowInsets insets, boolean animate) { @@ -1016,6 +1027,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind insets.getSystemWindowInsetBottom()); mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(), insets.getSystemWindowInsetRight()); + mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(), + insets.getSystemWindowInsetLeft()); // Don't animate if the presence of stable insets has changed, because that // indicates that the window was either just added and received them for the @@ -1031,21 +1044,32 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind boolean hasRightStableInset = insets.getStableInsetRight() != 0; disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset); mLastHasRightStableInset = hasRightStableInset; + + boolean hasLeftStableInset = insets.getStableInsetLeft() != 0; + disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset); + mLastHasLeftStableInset = hasLeftStableInset; + mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar(); } boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset); - int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset); + boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset); + int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset); updateColorViewInt(mNavigationColorViewState, sysUiVisibility, - mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge, - 0 /* rightInset */, animate && !disallowAnimate, false /* force */); + mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge, + navBarToLeftEdge, + 0 /* sideInset */, animate && !disallowAnimate, false /* force */); boolean statusBarNeedsRightInset = navBarToRightEdge && mNavigationColorViewState.present; - int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0; + boolean statusBarNeedsLeftInset = navBarToLeftEdge + && mNavigationColorViewState.present; + int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset + : statusBarNeedsLeftInset ? mLastLeftInset : 0; updateColorViewInt(mStatusColorViewState, sysUiVisibility, calculateStatusBarColor(), mLastTopInset, - false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate, + false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, + animate && !disallowAnimate, mForceWindowDrawsStatusBarBackground); } @@ -1070,15 +1094,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind int consumedTop = consumingStatusBar ? mLastTopInset : 0; int consumedRight = consumingNavBar ? mLastRightInset : 0; int consumedBottom = consumingNavBar ? mLastBottomInset : 0; + int consumedLeft = consumingNavBar ? mLastLeftInset : 0; if (mContentRoot != null && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams(); if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight - || lp.bottomMargin != consumedBottom) { + || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) { lp.topMargin = consumedTop; lp.rightMargin = consumedRight; lp.bottomMargin = consumedBottom; + lp.leftMargin = consumedLeft; mContentRoot.setLayoutParams(lp); if (insets == null) { @@ -1089,7 +1115,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } if (insets != null) { insets = insets.replaceSystemWindowInsets( - insets.getSystemWindowInsetLeft(), + insets.getSystemWindowInsetLeft() - consumedLeft, insets.getSystemWindowInsetTop() - consumedTop, insets.getSystemWindowInsetRight() - consumedRight, insets.getSystemWindowInsetBottom() - consumedBottom); @@ -1126,11 +1152,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind * @param size the current size in the non-parent-matching dimension. * @param verticalBar if true the view is attached to a vertical edge, otherwise to a * horizontal edge, - * @param rightMargin rightMargin for the color view. + * @param sideMargin sideMargin for the color view. * @param animate if true, the change will be animated. */ private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, - int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) { + int size, boolean verticalBar, boolean seascape, int sideMargin, + boolean animate, boolean force) { state.present = (sysUiVis & state.systemUiHideFlag) == 0 && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0 && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 @@ -1145,7 +1172,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size; int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT; - int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity; + int resolvedGravity = verticalBar + ? (seascape ? state.seascapeGravity : state.horizontalGravity) + : state.verticalGravity; if (view == null) { if (showView) { @@ -1159,7 +1188,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight, resolvedGravity); - lp.rightMargin = rightMargin; + if (seascape) { + lp.leftMargin = sideMargin; + } else { + lp.rightMargin = sideMargin; + } addView(view, lp); updateColorViewTranslations(); } @@ -1168,12 +1201,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind visibilityChanged = state.targetVisibility != vis; state.targetVisibility = vis; LayoutParams lp = (LayoutParams) view.getLayoutParams(); + int rightMargin = seascape ? 0 : sideMargin; + int leftMargin = seascape ? sideMargin : 0; if (lp.height != resolvedHeight || lp.width != resolvedWidth - || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { + || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin + || lp.leftMargin != leftMargin) { lp.height = resolvedHeight; lp.width = resolvedWidth; lp.gravity = resolvedGravity; lp.rightMargin = rightMargin; + lp.leftMargin = leftMargin; view.setLayoutParams(lp); } if (showView) { @@ -1824,7 +1861,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } final WindowManager.LayoutParams attrs = mWindow.getAttributes(); final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION || - attrs.type == TYPE_APPLICATION; + attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION; // Only a non floating application window on one of the allowed workspaces can get a caption if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) { // Dependent on the brightness of the used title we either use the @@ -2217,17 +2254,19 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final int translucentFlag; final int verticalGravity; final int horizontalGravity; + final int seascapeGravity; final String transitionName; final int hideWindowFlag; ColorViewState(int systemUiHideFlag, int translucentFlag, int verticalGravity, int horizontalGravity, - String transitionName, int id, int hideWindowFlag) { + int seascapeGravity, String transitionName, int id, int hideWindowFlag) { this.id = id; this.systemUiHideFlag = systemUiHideFlag; this.translucentFlag = translucentFlag; this.verticalGravity = verticalGravity; this.horizontalGravity = horizontalGravity; + this.seascapeGravity = seascapeGravity; this.transitionName = transitionName; this.hideWindowFlag = hideWindowFlag; } diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index 171a264eea8b2c4bc210b3e8ed4930c2290e04fd..83d75fba80f9ade4f40b9eab79f75800f585986e 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -34,7 +34,7 @@ oneway interface IKeyguardService { void addStateMonitorCallback(IKeyguardStateCallback callback); void verifyUnlock(IKeyguardExitCallback callback); void keyguardDone(boolean authenticated, boolean wakeup); - void dismiss(); + void dismiss(boolean allowWhileOccluded); void onDreamingStarted(); void onDreamingStopped(); diff --git a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl index db3b40b29903e62822a5179490e63e9a69207ce6..8e454db4cb040146664e9e58f5c0829680ead6eb 100644 --- a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl +++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl @@ -19,4 +19,6 @@ interface IKeyguardStateCallback { void onShowingStateChanged(boolean showing); void onSimSecureStateChanged(boolean simSecure); void onInputRestrictedStateChanged(boolean inputRestricted); + void onTrustedChanged(boolean trusted); + void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper); } \ No newline at end of file diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 9ad750d3a59945c280f753872f4fa17ac6f64bb9..878f3a69a504bd3f329c01f53fcf2c98108c54ea 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -92,8 +92,6 @@ import android.util.EventLog; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeProvider; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; @@ -2057,9 +2055,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } static private final String FOCUSED_ID_TAG = "android:focusedViewId"; - static private final String ACCESSIBILITY_FOCUSED_ID_TAG = "android:accessibilityFocusedViewId"; - static private final String ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG = - "android:accessibilityFocusedVirtualViewId"; static private final String VIEWS_TAG = "android:views"; static private final String PANELS_TAG = "android:Panels"; static private final String ACTION_BAR_TAG = "android:ActionBar"; @@ -2082,26 +2077,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { outState.putInt(FOCUSED_ID_TAG, focusedView.getId()); } - // Save the accessibility focused view ID. - if (mDecor != null) { - final ViewRootImpl viewRootImpl = mDecor.getViewRootImpl(); - if (viewRootImpl != null) { - final View accessFocusHost = viewRootImpl.getAccessibilityFocusedHost(); - if (accessFocusHost != null && accessFocusHost.getId() != View.NO_ID) { - outState.putInt(ACCESSIBILITY_FOCUSED_ID_TAG, accessFocusHost.getId()); - - // If we have a focused virtual node ID, save that too. - final AccessibilityNodeInfo accessFocusedNode = - viewRootImpl.getAccessibilityFocusedVirtualView(); - if (accessFocusedNode != null) { - final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( - accessFocusedNode.getSourceNodeId()); - outState.putInt(ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG, virtualNodeId); - } - } - } - } - // save the panels SparseArray panelStates = new SparseArray(); savePanelState(panelStates); @@ -2144,13 +2119,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - // Restore the accessibility focused view. - final int accessFocusHostViewId = savedInstanceState.getInt( - ACCESSIBILITY_FOCUSED_ID_TAG, View.NO_ID); - final int accessFocusVirtualViewId = savedInstanceState.getInt( - ACCESSIBILITY_FOCUSED_VIRTUAL_ID_TAG, AccessibilityNodeInfo.UNDEFINED_ITEM_ID); - tryRestoreAccessibilityFocus(accessFocusHostViewId, accessFocusVirtualViewId); - // Restore the panels. SparseArray panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG); if (panelStates != null) { @@ -2170,33 +2138,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - private void tryRestoreAccessibilityFocus(int hostViewId, int virtualViewId) { - if (hostViewId != View.NO_ID && mDecor != null) { - final View needsAccessFocus = mDecor.findViewById(hostViewId); - if (needsAccessFocus != null) { - if (!tryFocusingVirtualView(needsAccessFocus, virtualViewId) - && !needsAccessFocus.requestAccessibilityFocus()) { - Log.w(TAG, "Failed to restore focus to previously accessibility" - + " focused view with id " + hostViewId); - } - } else { - Log.w(TAG, "Previously accessibility focused view reported id " + hostViewId - + " during save, but can't be found during restore."); - } - } - } - - private boolean tryFocusingVirtualView(View host, int virtualViewId) { - if (virtualViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - final AccessibilityNodeProvider nodeProvider = host.getAccessibilityNodeProvider(); - if (nodeProvider != null) { - return nodeProvider.performAction(virtualViewId, - AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } - } - return false; - } - /** * Invoked when the panels should freeze their state. * diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 7706ff7f173ffe4e48d5727b7af48f35dc89dc2d..20f10b33343b197ec22a523507e4a39b6ee5f497 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -112,4 +112,5 @@ oneway interface IStatusBar void addQsTile(in ComponentName tile); void remQsTile(in ComponentName tile); void clickQsTile(in ComponentName tile); + void handleSystemNavigationKey(in int key); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 3d054225d2342b0ec4480683f949044cc0c1525b..698e387175fbc719a4cdff2378c52edb75d206f7 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -66,4 +66,5 @@ interface IStatusBarService void addTile(in ComponentName tile); void remTile(in ComponentName tile); void clickTile(in ComponentName tile); + void handleSystemNavigationKey(in int key); } diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..841ec7ca994ceca849f47b29be8291a392cf4559 --- /dev/null +++ b/core/java/com/android/internal/util/MimeIconUtils.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.provider.DocumentsContract; + +import com.android.internal.R; + +import java.util.HashMap; + +public class MimeIconUtils { + + private static HashMap sMimeIcons = new HashMap<>(); + + private static void add(String mimeType, int resId) { + if (sMimeIcons.put(mimeType, resId) != null) { + throw new RuntimeException(mimeType + " already registered!"); + } + } + + static { + int icon; + + // Package + icon = R.drawable.ic_doc_apk; + add("application/vnd.android.package-archive", icon); + + // Audio + icon = R.drawable.ic_doc_audio; + add("application/ogg", icon); + add("application/x-flac", icon); + + // Certificate + icon = R.drawable.ic_doc_certificate; + add("application/pgp-keys", icon); + add("application/pgp-signature", icon); + add("application/x-pkcs12", icon); + add("application/x-pkcs7-certreqresp", icon); + add("application/x-pkcs7-crl", icon); + add("application/x-x509-ca-cert", icon); + add("application/x-x509-user-cert", icon); + add("application/x-pkcs7-certificates", icon); + add("application/x-pkcs7-mime", icon); + add("application/x-pkcs7-signature", icon); + + // Source code + icon = R.drawable.ic_doc_codes; + add("application/rdf+xml", icon); + add("application/rss+xml", icon); + add("application/x-object", icon); + add("application/xhtml+xml", icon); + add("text/css", icon); + add("text/html", icon); + add("text/xml", icon); + add("text/x-c++hdr", icon); + add("text/x-c++src", icon); + add("text/x-chdr", icon); + add("text/x-csrc", icon); + add("text/x-dsrc", icon); + add("text/x-csh", icon); + add("text/x-haskell", icon); + add("text/x-java", icon); + add("text/x-literate-haskell", icon); + add("text/x-pascal", icon); + add("text/x-tcl", icon); + add("text/x-tex", icon); + add("application/x-latex", icon); + add("application/x-texinfo", icon); + add("application/atom+xml", icon); + add("application/ecmascript", icon); + add("application/json", icon); + add("application/javascript", icon); + add("application/xml", icon); + add("text/javascript", icon); + add("application/x-javascript", icon); + + // Compressed + icon = R.drawable.ic_doc_compressed; + add("application/mac-binhex40", icon); + add("application/rar", icon); + add("application/zip", icon); + add("application/x-apple-diskimage", icon); + add("application/x-debian-package", icon); + add("application/x-gtar", icon); + add("application/x-iso9660-image", icon); + add("application/x-lha", icon); + add("application/x-lzh", icon); + add("application/x-lzx", icon); + add("application/x-stuffit", icon); + add("application/x-tar", icon); + add("application/x-webarchive", icon); + add("application/x-webarchive-xml", icon); + add("application/gzip", icon); + add("application/x-7z-compressed", icon); + add("application/x-deb", icon); + add("application/x-rar-compressed", icon); + + // Contact + icon = R.drawable.ic_doc_contact; + add("text/x-vcard", icon); + add("text/vcard", icon); + + // Event + icon = R.drawable.ic_doc_event; + add("text/calendar", icon); + add("text/x-vcalendar", icon); + + // Font + icon = R.drawable.ic_doc_font; + add("application/x-font", icon); + add("application/font-woff", icon); + add("application/x-font-woff", icon); + add("application/x-font-ttf", icon); + + // Image + icon = R.drawable.ic_doc_image; + add("application/vnd.oasis.opendocument.graphics", icon); + add("application/vnd.oasis.opendocument.graphics-template", icon); + add("application/vnd.oasis.opendocument.image", icon); + add("application/vnd.stardivision.draw", icon); + add("application/vnd.sun.xml.draw", icon); + add("application/vnd.sun.xml.draw.template", icon); + + // PDF + icon = R.drawable.ic_doc_pdf; + add("application/pdf", icon); + + // Presentation + icon = R.drawable.ic_doc_presentation; + add("application/vnd.stardivision.impress", icon); + add("application/vnd.sun.xml.impress", icon); + add("application/vnd.sun.xml.impress.template", icon); + add("application/x-kpresenter", icon); + add("application/vnd.oasis.opendocument.presentation", icon); + + // Spreadsheet + icon = R.drawable.ic_doc_spreadsheet; + add("application/vnd.oasis.opendocument.spreadsheet", icon); + add("application/vnd.oasis.opendocument.spreadsheet-template", icon); + add("application/vnd.stardivision.calc", icon); + add("application/vnd.sun.xml.calc", icon); + add("application/vnd.sun.xml.calc.template", icon); + add("application/x-kspread", icon); + + // Document + icon = R.drawable.ic_doc_document; + add("application/vnd.oasis.opendocument.text", icon); + add("application/vnd.oasis.opendocument.text-master", icon); + add("application/vnd.oasis.opendocument.text-template", icon); + add("application/vnd.oasis.opendocument.text-web", icon); + add("application/vnd.stardivision.writer", icon); + add("application/vnd.stardivision.writer-global", icon); + add("application/vnd.sun.xml.writer", icon); + add("application/vnd.sun.xml.writer.global", icon); + add("application/vnd.sun.xml.writer.template", icon); + add("application/x-abiword", icon); + add("application/x-kword", icon); + + // Video + icon = R.drawable.ic_doc_video; + add("application/x-quicktimeplayer", icon); + add("application/x-shockwave-flash", icon); + + // Word + icon = R.drawable.ic_doc_word; + add("application/msword", icon); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon); + + // Excel + icon = R.drawable.ic_doc_excel; + add("application/vnd.ms-excel", icon); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon); + + // Powerpoint + icon = R.drawable.ic_doc_powerpoint; + add("application/vnd.ms-powerpoint", icon); + add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon); + add("application/vnd.openxmlformats-officedocument.presentationml.template", icon); + add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon); + } + + public static Drawable loadMimeIcon(Context context, String mimeType) { + if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) { + return context.getDrawable(R.drawable.ic_doc_folder); + } + + // Look for exact match first + Integer resId = sMimeIcons.get(mimeType); + if (resId != null) { + return context.getDrawable(resId); + } + + if (mimeType == null) { + // TODO: generic icon? + return null; + } + + // Otherwise look for partial match + final String typeOnly = mimeType.split("/")[0]; + if ("audio".equals(typeOnly)) { + return context.getDrawable(R.drawable.ic_doc_audio); + } else if ("image".equals(typeOnly)) { + return context.getDrawable(R.drawable.ic_doc_image); + } else if ("text".equals(typeOnly)) { + return context.getDrawable(R.drawable.ic_doc_text); + } else if ("video".equals(typeOnly)) { + return context.getDrawable(R.drawable.ic_doc_video); + } else { + return context.getDrawable(R.drawable.ic_doc_generic); + } + } +} diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 48bcc09301648ab2f9ef85b99a40050d1a3bf06e..4748e6fb19ff0f9d21d4c6de42ebebb429aaa386 100644 --- a/core/java/com/android/internal/util/NotificationColorUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -271,7 +271,7 @@ public class NotificationColorUtil { * Finds a text color with sufficient contrast over bg that has the same hue as the original * color, assuming it is for large text. */ - private static int ensureLargeTextContrast(int color, int bg) { + public static int ensureLargeTextContrast(int color, int bg) { return findContrastColor(color, bg, true, 3); } @@ -340,6 +340,20 @@ public class NotificationColorUtil { return color; } + /** + * Lighten a color by a specified value + * @param baseColor the base color to lighten + * @param amount the amount to lighten the color from 0 to 100. This corresponds to the L + * increase in the LAB color space. + * @return the lightened color + */ + public static int lightenColor(int baseColor, int amount) { + final double[] result = ColorUtilsFromCompat.getTempDouble3Array(); + ColorUtilsFromCompat.colorToLAB(baseColor, result); + result[0] = Math.min(100, result[0] + amount); + return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]); + } + /** * Framework copy of functions needed from android.support.v4.graphics.ColorUtils. */ @@ -434,7 +448,7 @@ public class NotificationColorUtil { * Convert RGB components to its CIE Lab representative components. * *

      - *
    • outLab[0] is L [0 ...1)
    • + *
    • outLab[0] is L [0 ...100)
    • *
    • outLab[1] is a [-128...127)
    • *
    • outLab[2] is b [-128...127)
    • *
    @@ -516,7 +530,7 @@ public class NotificationColorUtil { * 2° Standard Observer (1931).

    * *
      - *
    • outLab[0] is L [0 ...1)
    • + *
    • outLab[0] is L [0 ...100)
    • *
    • outLab[1] is a [-128...127)
    • *
    • outLab[2] is b [-128...127)
    • *
    @@ -634,7 +648,7 @@ public class NotificationColorUtil { : (XYZ_KAPPA * component + 16) / 116; } - private static double[] getTempDouble3Array() { + public static double[] getTempDouble3Array() { double[] result = TEMP_ARRAY.get(); if (result == null) { result = new double[3]; diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index e93d911af93a60c2e8de457243f3e12fcd3d2d0e..f804002df2d09dff5d1f06ce59915625b5b5cd26 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -29,8 +29,8 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; -import java.util.Iterator; import java.util.HashMap; +import java.util.Iterator; import java.util.Vector; /** @@ -1642,7 +1642,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(int what) { + public void sendMessage(int what) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1655,7 +1655,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(int what, Object obj) { + public void sendMessage(int what, Object obj) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1668,7 +1668,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(int what, int arg1) { + public void sendMessage(int what, int arg1) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1681,7 +1681,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(int what, int arg1, int arg2) { + public void sendMessage(int what, int arg1, int arg2) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1694,7 +1694,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(int what, int arg1, int arg2, Object obj) { + public void sendMessage(int what, int arg1, int arg2, Object obj) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1707,7 +1707,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessage(Message msg) { + public void sendMessage(Message msg) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1720,7 +1720,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(int what, long delayMillis) { + public void sendMessageDelayed(int what, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1733,7 +1733,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(int what, Object obj, long delayMillis) { + public void sendMessageDelayed(int what, Object obj, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1746,7 +1746,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(int what, int arg1, long delayMillis) { + public void sendMessageDelayed(int what, int arg1, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1759,7 +1759,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) { + public void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; @@ -1772,7 +1772,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj, + public void sendMessageDelayed(int what, int arg1, int arg2, Object obj, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; @@ -1786,7 +1786,7 @@ public class StateMachine { * * Message is ignored if state machine has quit. */ - public final void sendMessageDelayed(Message msg, long delayMillis) { + public void sendMessageDelayed(Message msg, long delayMillis) { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java index 26537451992c609750aa2ab16e89f5f9acc796e4..7d222c74ac3beb246d1c3eb6a8b689ace04a8c30 100644 --- a/core/java/com/android/internal/util/WakeupMessage.java +++ b/core/java/com/android/internal/util/WakeupMessage.java @@ -45,24 +45,32 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { protected final String mCmdName; @VisibleForTesting protected final int mCmd, mArg1, mArg2; + @VisibleForTesting + protected final Object mObj; private boolean mScheduled; public WakeupMessage(Context context, Handler handler, - String cmdName, int cmd, int arg1, int arg2) { + String cmdName, int cmd, int arg1, int arg2, Object obj) { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mHandler = handler; mCmdName = cmdName; mCmd = cmd; mArg1 = arg1; mArg2 = arg2; + mObj = obj; } public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) { - this(context, handler, cmdName, cmd, arg1, 0); + this(context, handler, cmdName, cmd, arg1, 0, null); + } + + public WakeupMessage(Context context, Handler handler, + String cmdName, int cmd, int arg1, int arg2) { + this(context, handler, cmdName, cmd, arg1, arg2, null); } public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) { - this(context, handler, cmdName, cmd, 0, 0); + this(context, handler, cmdName, cmd, 0, 0, null); } /** @@ -99,7 +107,7 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { mScheduled = false; } if (stillScheduled) { - Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2); + Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj); mHandler.handleMessage(msg); msg.recycle(); } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 59b4b35fa24ef16a5f113ef65ff2d03e375893fb..644c7e90f8b024a0ddf41d36ca28db926b7ce988 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -33,6 +33,7 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionInspector; import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; +import android.view.inputmethod.InputContentInfo; public abstract class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; @@ -61,6 +62,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_CLEAR_META_KEY_STATES = 130; private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; private static final int DO_CLOSE_CONNECTION = 150; + private static final int DO_COMMIT_CONTENT = 160; @GuardedBy("mLock") @Nullable @@ -241,6 +243,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION)); } + public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, + int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIOOSC(DO_COMMIT_CONTENT, flags, inputContentInfo, opts, seq, + callback)); + } + void dispatchMessage(Message msg) { // If we are calling this from the main thread, then we can call // right through. Otherwise, we need to send the message to the @@ -552,6 +560,41 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } + case DO_COMMIT_CONTENT: { + final int flags = msg.arg1; + final boolean grantUriPermission = + (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0; + SomeArgs args = (SomeArgs) msg.obj; + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitContent on inactive InputConnection"); + args.callback.setCommitContentResult(false, args.seq); + return; + } + final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1; + if (inputContentInfo == null || !inputContentInfo.validate()) { + Log.w(TAG, "commitContent with invalid inputContentInfo=" + + inputContentInfo); + args.callback.setCommitContentResult(false, args.seq); + return; + } + if (grantUriPermission) { + inputContentInfo.requestPermission(); + } + final boolean result = + ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); + // If this request is not handled, then there is no reason to keep the URI + // permission. + if (grantUriPermission && !result) { + inputContentInfo.releasePermission(); + } + args.callback.setCommitContentResult(result, args.seq); + } catch (RemoteException e) { + Log.w(TAG, "Got RemoteException calling commitContent", e); + } + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -582,12 +625,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { return mH.obtainMessage(what, arg1, arg2, args); } - Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) { + Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int seq, + IInputContextCallback callback) { SomeArgs args = new SomeArgs(); - args.arg1 = arg1; + args.arg1 = objArg1; + args.arg2 = objArg2; args.callback = callback; args.seq = seq; - return mH.obtainMessage(what, 0, 0, args); + return mH.obtainMessage(what, arg1, 0, args); } Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index f7ec420ba6769041fc09e54cf92d2e5c9bef0282..728c55786c49c352ed4640d46f01113f1f8b9e3d 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -21,6 +21,7 @@ import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputContentInfo; import com.android.internal.view.IInputContextCallback; @@ -74,6 +75,9 @@ import com.android.internal.view.IInputContextCallback; void getSelectedText(int flags, int seq, IInputContextCallback callback); - void requestUpdateCursorAnchorInfo(in int cursorUpdateMode, int seq, + void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq, + IInputContextCallback callback); + + void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts, int sec, IInputContextCallback callback); } diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl index 54ea3064bd7e68058c5b307485ac7939ec770524..0f40a83d7ee4b4968f138a86ebfb010d30f74ee5 100644 --- a/core/java/com/android/internal/view/IInputContextCallback.aidl +++ b/core/java/com/android/internal/view/IInputContextCallback.aidl @@ -28,4 +28,5 @@ oneway interface IInputContextCallback { void setExtractedText(in ExtractedText extractedText, int seq); void setSelectedText(CharSequence selectedText, int seq); void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq); + void setCommitContentResult(boolean result, int seq); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index cb7c3bfecc89bacea0498fcb482346b2a9673312..9e4b43b6c007dcf8077f8f84a67b4c4ac499a5aa 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -16,11 +16,13 @@ package com.android.internal.view; +import android.net.Uri; import android.os.ResultReceiver; import android.text.style.SuggestionSpan; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.EditorInfo; +import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.view.InputBindResult; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -81,5 +83,8 @@ interface IInputMethodManager { int getInputMethodWindowVisibleHeight(); void clearLastInputMethodWindowForTransition(in IBinder token); + IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri, + in String packageName); + oneway void notifyUserAction(int sequenceNumber); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index f9884d85d52cb7337e7cf1633545ce3efa6e735b..9a09dcccd1a7b672f06a64cfea249097d8415ff2 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -16,6 +16,8 @@ package com.android.internal.view; +import android.annotation.NonNull; +import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; @@ -29,10 +31,16 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionInspector; import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; +import android.view.inputmethod.InputContentInfo; + +import java.lang.ref.WeakReference; public class InputConnectionWrapper implements InputConnection { private static final int MAX_WAIT_TIME_MILLIS = 2000; private final IInputContext mIInputContext; + @NonNull + private final WeakReference mInputMethodService; + @MissingMethodFlags private final int mMissingMethods; @@ -46,7 +54,8 @@ public class InputConnectionWrapper implements InputConnection { public ExtractedText mExtractedText; public int mCursorCapsMode; public boolean mRequestUpdateCursorAnchorInfoResult; - + public boolean mCommitContentResult; + // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain // exclusive access to this object. private static InputContextCallback sInstance = new InputContextCallback(); @@ -172,6 +181,19 @@ public class InputConnectionWrapper implements InputConnection { } } + public void setCommitContentResult(boolean result, int seq) { + synchronized (this) { + if (seq == mSeq) { + mCommitContentResult = result; + mHaveValue = true; + notifyAll(); + } else { + Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq + + ") in setCommitContentResult, ignoring."); + } + } + } + /** * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds. * @@ -195,8 +217,10 @@ public class InputConnectionWrapper implements InputConnection { } } - public InputConnectionWrapper(IInputContext inputContext, - @MissingMethodFlags final int missingMethods) { + public InputConnectionWrapper( + @NonNull WeakReference inputMethodService, + IInputContext inputContext, @MissingMethodFlags final int missingMethods) { + mInputMethodService = inputMethodService; mIInputContext = inputContext; mMissingMethods = missingMethods; } @@ -491,6 +515,37 @@ public class InputConnectionWrapper implements InputConnection { // Nothing should happen when called from input method. } + public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + boolean result = false; + if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) { + // This method is not implemented. + return false; + } + try { + if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService == null) { + // This basically should not happen, because it's the the caller of this method. + return false; + } + inputMethodService.exposeContent(inputContentInfo, this); + } + + InputContextCallback callback = InputContextCallback.getInstance(); + mIInputContext.commitContent(inputContentInfo, flags, opts, callback.mSeq, callback); + synchronized (callback) { + callback.waitForResultLocked(); + if (callback.mHaveValue) { + result = callback.mCommitContentResult; + } + } + callback.dispose(); + } catch (RemoteException e) { + return false; + } + return result; + } + private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) { return (mMissingMethods & methodFlag) == methodFlag; } diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java index 9e2cbdba44d475f2c634049af50d953e8c912351..d28ab078d74a612c663f027961687c2ce2b7589e 100644 --- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java +++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java @@ -30,6 +30,8 @@ import android.view.Choreographer; @HasNativeInterpolator public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator { + // If the duration of an animation is more than 300 frames, we cap the sample size to 300. + private static final int MAX_SAMPLE_POINTS = 300; private TimeInterpolator mSourceInterpolator; private final float mLut[]; @@ -47,6 +49,7 @@ public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeI int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS); // We need 2 frame values as the minimal. int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs)); + numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS); float values[] = new float[numAnimFrames]; float lastFrame = numAnimFrames - 1; for (int i = 0; i < numAnimFrames; i++) { diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index ddf3a764661297eb8320079bdaf05acd25e66e1d..69e974c672d063518a9d7d805fc5ed15e29b0638 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -366,7 +366,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth); final MenuPopupWindow popupWindow = createPopupWindow(); popupWindow.setAdapter(adapter); - popupWindow.setWidth(menuWidth); + popupWindow.setContentWidth(menuWidth); popupWindow.setDropDownGravity(mDropDownGravity); final CascadingMenuInfo parentInfo; diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java new file mode 100644 index 0000000000000000000000000000000000000000..293b77b91d3795d5b51a0d3ff0f9249f1490ce47 --- /dev/null +++ b/core/java/com/android/internal/widget/CachingIconView.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.internal.widget; + +import android.annotation.DrawableRes; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.RemotableViewMethod; +import android.widget.ImageView; +import android.widget.RemoteViews; + +import libcore.util.Objects; + +/** + * An ImageView for displaying an Icon. Avoids reloading the Icon when possible. + */ +@RemoteViews.RemoteView +public class CachingIconView extends ImageView { + + private String mLastPackage; + private int mLastResId; + private boolean mInternalSetDrawable; + + public CachingIconView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageIconAsync") + public void setImageIcon(@Nullable Icon icon) { + if (!testAndSetCache(icon)) { + mInternalSetDrawable = true; + // This calls back to setImageDrawable, make sure we don't clear the cache there. + super.setImageIcon(icon); + mInternalSetDrawable = false; + } + } + + @Override + public Runnable setImageIconAsync(@Nullable Icon icon) { + resetCache(); + return super.setImageIconAsync(icon); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageResourceAsync") + public void setImageResource(@DrawableRes int resId) { + if (!testAndSetCache(resId)) { + mInternalSetDrawable = true; + // This calls back to setImageDrawable, make sure we don't clear the cache there. + super.setImageResource(resId); + mInternalSetDrawable = false; + } + } + + @Override + public Runnable setImageResourceAsync(@DrawableRes int resId) { + resetCache(); + return super.setImageResourceAsync(resId); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageURIAsync") + public void setImageURI(@Nullable Uri uri) { + resetCache(); + super.setImageURI(uri); + } + + @Override + public Runnable setImageURIAsync(@Nullable Uri uri) { + resetCache(); + return super.setImageURIAsync(uri); + } + + @Override + public void setImageDrawable(@Nullable Drawable drawable) { + if (!mInternalSetDrawable) { + // Only clear the cache if we were externally called. + resetCache(); + } + super.setImageDrawable(drawable); + } + + @Override + @RemotableViewMethod + public void setImageBitmap(Bitmap bm) { + resetCache(); + super.setImageBitmap(bm); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + resetCache(); + } + + /** + * @return true if the currently set image is the same as {@param icon} + */ + private synchronized boolean testAndSetCache(Icon icon) { + if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { + String iconPackage = normalizeIconPackage(icon); + + boolean isCached = mLastResId != 0 + && icon.getResId() == mLastResId + && Objects.equal(iconPackage, mLastPackage); + + mLastPackage = iconPackage; + mLastResId = icon.getResId(); + + return isCached; + } else { + resetCache(); + return false; + } + } + + /** + * @return true if the currently set image is the same as {@param resId} + */ + private synchronized boolean testAndSetCache(int resId) { + boolean isCached; + if (resId == 0 || mLastResId == 0) { + isCached = false; + } else { + isCached = resId == mLastResId && null == mLastPackage; + } + mLastPackage = null; + mLastResId = resId; + return isCached; + } + + /** + * Returns the normalized package name of {@param icon}. + * @return null if icon is null or if the icons package is null, empty or matches the current + * context. Otherwise returns the icon's package context. + */ + private String normalizeIconPackage(Icon icon) { + if (icon == null) { + return null; + } + + String pkg = icon.getResPackage(); + if (TextUtils.isEmpty(pkg)) { + return null; + } + if (pkg.equals(mContext.getPackageName())) { + return null; + } + return pkg; + } + + private synchronized void resetCache() { + mLastResId = 0; + mLastPackage = null; + } +} diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index e59d7ba57ea1d9e7c52eb14494990e8aec7d385f..fc68b849ed2080f06a1c6e410faaa4711ea55f2a 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -244,6 +244,11 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, return mTouchDispatchList; } + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + private boolean passedSlop(int x, int y) { return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop; } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 594581af8837f1922fda9d23c9f09de775cd3ff8..66042086e9257d16943e5c49ba2e21bb6008282b 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -737,7 +737,7 @@ public final class FloatingToolbar { protected void applyTransformation(float interpolatedTime, Transformation t) { int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth)); setWidth(mContentContainer, startWidth + deltaWidth); - if (isRTL()) { + if (isInRTLMode()) { mContentContainer.setX(left); // Lock the panels in place. @@ -766,7 +766,7 @@ public final class FloatingToolbar { } }; final float overflowButtonStartX = mOverflowButton.getX(); - final float overflowButtonTargetX = isRTL() ? + final float overflowButtonTargetX = isInRTLMode() ? overflowButtonStartX + targetWidth - mOverflowButton.getWidth() : overflowButtonStartX - targetWidth + mOverflowButton.getWidth(); Animation overflowButtonAnimation = new Animation() { @@ -774,7 +774,7 @@ public final class FloatingToolbar { protected void applyTransformation(float interpolatedTime, Transformation t) { float overflowButtonX = overflowButtonStartX + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX); - float deltaContainerWidth = isRTL() ? + float deltaContainerWidth = isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth; float actualOverflowButtonX = overflowButtonX + deltaContainerWidth; @@ -812,7 +812,7 @@ public final class FloatingToolbar { protected void applyTransformation(float interpolatedTime, Transformation t) { int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth)); setWidth(mContentContainer, startWidth + deltaWidth); - if (isRTL()) { + if (isInRTLMode()) { mContentContainer.setX(left); // Lock the panels in place. @@ -843,7 +843,7 @@ public final class FloatingToolbar { } }; final float overflowButtonStartX = mOverflowButton.getX(); - final float overflowButtonTargetX = isRTL() ? + final float overflowButtonTargetX = isInRTLMode() ? overflowButtonStartX - startWidth + mOverflowButton.getWidth() : overflowButtonStartX + startWidth - mOverflowButton.getWidth(); Animation overflowButtonAnimation = new Animation() { @@ -851,7 +851,7 @@ public final class FloatingToolbar { protected void applyTransformation(float interpolatedTime, Transformation t) { float overflowButtonX = overflowButtonStartX + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX); - float deltaContainerWidth = isRTL() ? + float deltaContainerWidth = isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth; float actualOverflowButtonX = overflowButtonX + deltaContainerWidth; @@ -903,7 +903,7 @@ public final class FloatingToolbar { R.string.floating_toolbar_close_overflow_description)); // Update x-coordinates depending on RTL state. - if (isRTL()) { + if (isInRTLMode()) { mContentContainer.setX(mMarginHorizontal); // align left mMainPanel.setX(0); // align left mOverflowButton.setX( // align right @@ -947,7 +947,7 @@ public final class FloatingToolbar { if (hasOverflow()) { // Update x-coordinates depending on RTL state. - if (isRTL()) { + if (isInRTLMode()) { mContentContainer.setX(mMarginHorizontal); // align left mMainPanel.setX(0); // align left mOverflowButton.setX(0); // align left @@ -1087,9 +1087,10 @@ public final class FloatingToolbar { viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer); } - private boolean isRTL() { - return mContext.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_RTL; + private boolean isInRTLMode() { + return mContext.getApplicationInfo().hasRtlSupport() + && mContext.getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_RTL; } private boolean hasOverflow() { @@ -1203,7 +1204,7 @@ public final class FloatingToolbar { // The positioning of contents in RTL is wrong when the view is first rendered. // Hide the view and post a runnable to recalculate positions and render the view. // TODO: Investigate why this happens and fix. - if (isRTL()) { + if (isInRTLMode()) { mContentContainer.setAlpha(0); mContentContainer.post(mPreparePopupContentRTLHelper); } diff --git a/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl new file mode 100644 index 0000000000000000000000000000000000000000..79717cf2b0c9f254daa4ba2119df25491142d842 --- /dev/null +++ b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** {@hide} */ +oneway interface ICheckCredentialProgressCallback { + void onCredentialVerified(); +} diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index b352894a9f2f1ebc79d2d8231fe4cf8d2b99e000..3d27cf7c3d8292c44ce9080ffcf56d9c60c44efa 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.app.trust.IStrongAuthTracker; +import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.VerifyCredentialResponse; /** {@hide} */ @@ -29,10 +30,12 @@ interface ILockSettings { String getString(in String key, in String defaultValue, in int userId); void setLockPattern(in String pattern, in String savedPattern, int userId); void resetKeyStore(int userId); - VerifyCredentialResponse checkPattern(in String pattern, int userId); + VerifyCredentialResponse checkPattern(in String pattern, int userId, + in ICheckCredentialProgressCallback progressCallback); VerifyCredentialResponse verifyPattern(in String pattern, long challenge, int userId); void setLockPassword(in String password, in String savedPassword, int userId); - VerifyCredentialResponse checkPassword(in String password, int userId); + VerifyCredentialResponse checkPassword(in String password, int userId, + in ICheckCredentialProgressCallback progressCallback); VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId); VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, int userId); byte getLockPatternSize(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java index 713f56f46266c3a35c00122ce4b513102bc25da2..df9b0ddc180473c114e5be41120ed27da783e9f1 100644 --- a/core/java/com/android/internal/widget/LockPatternChecker.java +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -14,6 +14,13 @@ public final class LockPatternChecker { * Interface for a callback to be invoked after security check. */ public interface OnCheckCallback { + + /** + * Invoked as soon as possible we know that the credentials match. This will be called + * earlier than {@link #onChecked} but only if the credentials match. + */ + default void onEarlyMatched() {} + /** * Invoked when a security check is finished. * @@ -92,7 +99,7 @@ public final class LockPatternChecker { @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPattern(pattern, userId); + return utils.checkPattern(pattern, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; @@ -199,7 +206,7 @@ public final class LockPatternChecker { @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPassword(password, userId); + return utils.checkPassword(password, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 94b4679a5970e7049da051e90ea4005f62f2684e..916365bbb047f5ca9a0cbd570e69c7d2fb30bd9c 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; @@ -32,7 +33,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IMountService; @@ -154,6 +154,7 @@ public class LockPatternUtils { private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; private UserManager mUserManager; + private final Handler mHandler; /** * Use {@link TrustManager#isTrustUsuallyManaged(int)}. @@ -235,6 +236,9 @@ public class LockPatternUtils { public LockPatternUtils(Context context) { mContext = context; mContentResolver = context.getContentResolver(); + + Looper looper = Looper.myLooper(); + mHandler = looper != null ? new Handler(looper) : null; } private ILockSettings getLockSettings() { @@ -289,7 +293,6 @@ public class LockPatternUtils { public void reportFailedPasswordAttempt(int userId) { getDevicePolicyManager().reportFailedPasswordAttempt(userId); getTrustManager().reportUnlockAttempt(false /* authenticated */, userId); - requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL, userId); } public void reportSuccessfulPasswordAttempt(int userId) { @@ -346,10 +349,23 @@ public class LockPatternUtils { */ public boolean checkPattern(List pattern, int userId) throws RequestThrottledException { + return checkPattern(pattern, userId, null /* progressCallback */); + } + + /** + * Check to see if a pattern matches the saved pattern. If no pattern exists, + * always returns true. + * @param pattern The pattern to check. + * @return Whether the pattern matches the stored one. + */ + public boolean checkPattern(List pattern, int userId, + @Nullable CheckCredentialProgressCallback progressCallback) + throws RequestThrottledException { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().checkPattern(patternToString(pattern, userId), userId); + getLockSettings().checkPattern(patternToString(pattern), userId, + wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; @@ -428,10 +444,22 @@ public class LockPatternUtils { * @return Whether the password matches the stored one. */ public boolean checkPassword(String password, int userId) throws RequestThrottledException { + return checkPassword(password, userId, null /* progressCallback */); + } + + /** + * Check to see if a password matches the saved password. If no password exists, + * always returns true. + * @param password The password to check. + * @return Whether the password matches the stored one. + */ + public boolean checkPassword(String password, int userId, + @Nullable CheckCredentialProgressCallback progressCallback) + throws RequestThrottledException { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().checkPassword(password, userId); + getLockSettings().checkPassword(password, userId, wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { @@ -1535,6 +1563,37 @@ public class LockPatternUtils { return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0; } + private ICheckCredentialProgressCallback wrapCallback( + final CheckCredentialProgressCallback callback) { + if (callback == null) { + return null; + } else { + if (mHandler == null) { + throw new IllegalStateException("Must construct LockPatternUtils on a looper thread" + + " to use progress callbacks."); + } + return new ICheckCredentialProgressCallback.Stub() { + + @Override + public void onCredentialVerified() throws RemoteException { + mHandler.post(callback::onEarlyMatched); + } + }; + } + } + + /** + * Callback to be notified about progress when checking credentials. + */ + public interface CheckCredentialProgressCallback { + + /** + * Called as soon as possible when we know that the credentials match but the user hasn't + * been fully unlocked. + */ + void onEarlyMatched(); + } + /** * Tracks the global strong authentication state. */ @@ -1544,7 +1603,8 @@ public class LockPatternUtils { value = { STRONG_AUTH_NOT_REQUIRED, STRONG_AUTH_REQUIRED_AFTER_BOOT, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, - SOME_AUTH_REQUIRED_AFTER_USER_REQUEST}) + SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, + STRONG_AUTH_REQUIRED_AFTER_LOCKOUT}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1575,13 +1635,12 @@ public class LockPatternUtils { public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8; /** - * Some authentication is required because the user has entered a wrong credential. + * Strong auth flags that do not prevent fingerprint from being accepted as auth. + * + * If any other flags are set, fingerprint is disabled. */ - public static final int SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL = 0x10; - private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED - | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST - | SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL; + | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); private final H mHandler; diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index 9dd118c5a942164e06ab9c8943de8d944a3deb74..073aac542e31535da94e2f03c33e94a860a45e6a 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -17,11 +17,13 @@ package com.android.internal.widget; import android.content.Context; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Pair; import android.view.Gravity; +import android.view.RemotableViewMethod; import android.view.View; -import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.TextView; @@ -33,11 +35,14 @@ import java.util.Comparator; * the remaining available width, and the last action consumes the remaining space. */ @RemoteViews.RemoteView -public class NotificationActionListLayout extends ViewGroup { +public class NotificationActionListLayout extends LinearLayout { private int mTotalWidth = 0; private ArrayList> mMeasureOrderTextViews = new ArrayList<>(); private ArrayList mMeasureOrderOther = new ArrayList<>(); + private boolean mMeasureLinearly; + private int mDefaultPaddingEnd; + private Drawable mDefaultBackground; public NotificationActionListLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -45,6 +50,10 @@ public class NotificationActionListLayout extends ViewGroup { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mMeasureLinearly) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } final int N = getChildCount(); int textViews = 0; int otherViews = 0; @@ -186,6 +195,10 @@ public class NotificationActionListLayout extends ViewGroup { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mMeasureLinearly) { + super.onLayout(changed, left, top, right, bottom); + return; + } final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; @@ -241,26 +254,24 @@ public class NotificationActionListLayout extends ViewGroup { } @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - return new MarginLayoutParams(getContext(), attrs); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + protected void onFinishInflate() { + super.onFinishInflate(); + mDefaultPaddingEnd = getPaddingEnd(); + mDefaultBackground = getBackground(); } - @Override - protected LayoutParams generateLayoutParams(LayoutParams p) { - if (p instanceof MarginLayoutParams) { - return new MarginLayoutParams((MarginLayoutParams)p); - } - return new MarginLayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(LayoutParams p) { - return p instanceof MarginLayoutParams; + /** + * Set whether the list is in a mode where some actions are emphasized. This will trigger an + * equal measuring where all actions are full height and change a few parameters like + * the padding. + */ + @RemotableViewMethod + public void setEmphasizedMode(boolean emphasizedMode) { + mMeasureLinearly = emphasizedMode; + setPaddingRelative(getPaddingStart(), getPaddingTop(), + emphasizedMode ? 0 : mDefaultPaddingEnd, getPaddingBottom()); + setBackground(emphasizedMode ? null : mDefaultBackground); + requestLayout(); } public static final Comparator> MEASURE_ORDER_COMPARATOR diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index 277fafd4adf726e983ec3e030ecdbe0af163a922..d5b6def97426d48bccacea77b197c8783a14f741 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -55,34 +55,7 @@ import java.util.Collections; import java.util.Comparator; /** - * Layout manager that allows the user to flip left and right - * through pages of data. You supply an implementation of a - * {@link android.support.v4.view.PagerAdapter} to generate the pages that the view shows. - * - *

    Note this class is currently under early design and - * development. The API will likely change in later updates of - * the compatibility library, requiring changes to the source code - * of apps when they are compiled against the newer version.

    - * - *

    ViewPager is most often used in conjunction with {@link android.app.Fragment}, - * which is a convenient way to supply and manage the lifecycle of each page. - * There are standard adapters implemented for using fragments with the ViewPager, - * which cover the most common use cases. These are - * {@link android.support.v4.app.FragmentPagerAdapter} and - * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these - * classes have simple code showing how to build a full user interface - * with them. - * - *

    For more information about how to use ViewPager, read Creating Swipe Views with - * Tabs.

    - * - *

    Below is a more complicated example of ViewPager, using it in conjunction - * with {@link android.app.ActionBar} tabs. You can find other examples of using - * ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code. - * - * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/ActionBarTabsPager.java - * complete} + * Framework copy of the support-v4 ViewPager class. */ public class ViewPager extends ViewGroup { private static final String TAG = "ViewPager"; diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java new file mode 100644 index 0000000000000000000000000000000000000000..3d32d86cd49c48ac82a8d122d25dfce6303cc180 --- /dev/null +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.annotation.IdRes; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.HeaderViewListAdapter; + +import java.util.ArrayList; + +import com.android.internal.util.Predicate; + +public class WatchHeaderListView extends ListView { + private View mTopPanel; + + public WatchHeaderListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public WatchHeaderListView( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected HeaderViewListAdapter wrapHeaderListAdapterInternal( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + return new WatchHeaderListAdapter(headerViewInfos, footerViewInfos, adapter); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (mTopPanel == null) { + setTopPanel(child); + } else { + throw new IllegalStateException("WatchHeaderListView can host only one header"); + } + } + + public void setTopPanel(View v) { + mTopPanel = v; + wrapAdapterIfNecessary(); + } + + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(adapter); + wrapAdapterIfNecessary(); + } + + @Override + protected View findViewTraversal(@IdRes int id) { + View v = super.findViewTraversal(id); + if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewById(id); + } + return v; + } + + @Override + protected View findViewWithTagTraversal(Object tag) { + View v = super.findViewWithTagTraversal(tag); + if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewWithTag(tag); + } + return v; + } + + @Override + protected View findViewByPredicateTraversal(Predicate predicate, View childToSkip) { + View v = super.findViewByPredicateTraversal(predicate, childToSkip); + if (v == null && mTopPanel != null && mTopPanel != childToSkip + && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewByPredicate(predicate); + } + return v; + } + + @Override + public int getHeaderViewsCount() { + return mTopPanel == null ? super.getHeaderViewsCount() : super.getHeaderViewsCount() + 1; + } + + private void wrapAdapterIfNecessary() { + ListAdapter adapter = getAdapter(); + if (adapter != null && mTopPanel != null) { + if (!(adapter instanceof WatchHeaderListAdapter)) { + wrapHeaderListAdapterInternal(); + } + + ((WatchHeaderListAdapter) getAdapter()).setTopPanel(mTopPanel); + dispatchDataSetObserverOnChangedInternal(); + } + } + + private static class WatchHeaderListAdapter extends HeaderViewListAdapter { + private View mTopPanel; + + public WatchHeaderListAdapter( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + super(headerViewInfos, footerViewInfos, adapter); + } + + public void setTopPanel(View v) { + mTopPanel = v; + } + + private int getTopPanelCount() { + return mTopPanel == null ? 0 : 1; + } + + @Override + public int getCount() { + return super.getCount() + getTopPanelCount(); + } + + @Override + public boolean areAllItemsEnabled() { + return mTopPanel == null && super.areAllItemsEnabled(); + } + + @Override + public boolean isEnabled(int position) { + if (mTopPanel != null) { + if (position == 0) { + return false; + } else { + return super.isEnabled(position - 1); + } + } + + return super.isEnabled(position); + } + + @Override + public Object getItem(int position) { + if (mTopPanel != null) { + if (position == 0) { + return null; + } else { + return super.getItem(position - 1); + } + } + + return super.getItem(position); + } + + @Override + public long getItemId(int position) { + int numHeaders = getHeadersCount() + getTopPanelCount(); + if (getWrappedAdapter() != null && position >= numHeaders) { + int adjPosition = position - numHeaders; + int adapterCount = getWrappedAdapter().getCount(); + if (adjPosition < adapterCount) { + return getWrappedAdapter().getItemId(adjPosition); + } + } + return -1; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (mTopPanel != null) { + if (position == 0) { + return mTopPanel; + } else { + return super.getView(position - 1, convertView, parent); + } + } + + return super.getView(position, convertView, parent); + } + + @Override + public int getItemViewType(int position) { + int numHeaders = getHeadersCount() + getTopPanelCount(); + if (getWrappedAdapter() != null && position >= numHeaders) { + int adjPosition = position - numHeaders; + int adapterCount = getWrappedAdapter().getCount(); + if (adjPosition < adapterCount) { + return getWrappedAdapter().getItemViewType(adjPosition); + } + } + + return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + } + } +} diff --git a/core/java/com/android/internal/widget/WatchListDecorLayout.java b/core/java/com/android/internal/widget/WatchListDecorLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..538cecaec6167ff4de1e9d12d04639c0e0d1f7e0 --- /dev/null +++ b/core/java/com/android/internal/widget/WatchListDecorLayout.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ListView; +import android.widget.FrameLayout; + +import java.util.ArrayList; + + +/** + * Layout for the decor for ListViews on watch-type devices with small screens. + *

    + * Supports one panel with the gravity set to top, and one panel with gravity set to bottom. + *

    + * Use with one ListView child. The top and bottom panels will track the ListView's scrolling. + * If there is no ListView child, it will act like a normal FrameLayout. + */ +public class WatchListDecorLayout extends FrameLayout + implements ViewTreeObserver.OnScrollChangedListener { + + private int mForegroundPaddingLeft = 0; + private int mForegroundPaddingTop = 0; + private int mForegroundPaddingRight = 0; + private int mForegroundPaddingBottom = 0; + + private final ArrayList mMatchParentChildren = new ArrayList<>(1); + + /** Track the amount the ListView has to scroll up to account for padding change difference. */ + private int mPendingScroll; + private View mBottomPanel; + private View mTopPanel; + private ListView mListView; + private ViewTreeObserver mObserver; + + + public WatchListDecorLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WatchListDecorLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public WatchListDecorLayout( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mPendingScroll = 0; + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof ListView) { + if (mListView != null) { + throw new IllegalArgumentException("only one ListView child allowed"); + } + mListView = (ListView) child; + + mListView.setNestedScrollingEnabled(true); + mObserver = mListView.getViewTreeObserver(); + mObserver.addOnScrollChangedListener(this); + } else { + int gravity = (((LayoutParams) child.getLayoutParams()).gravity + & Gravity.VERTICAL_GRAVITY_MASK); + if (gravity == Gravity.TOP && mTopPanel == null) { + mTopPanel = child; + } else if (gravity == Gravity.BOTTOM && mBottomPanel == null) { + mBottomPanel = child; + } + } + } + } + + @Override + public void onDetachedFromWindow() { + mListView = null; + mBottomPanel = null; + mTopPanel = null; + if (mObserver != null) { + if (mObserver.isAlive()) { + mObserver.removeOnScrollChangedListener(this); + } + mObserver = null; + } + } + + private void applyMeasureToChild(View child, int widthMeasureSpec, int heightMeasureSpec) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childWidthMeasureSpec; + if (lp.width == LayoutParams.MATCH_PARENT) { + final int width = Math.max(0, getMeasuredWidth() + - getPaddingLeftWithForeground() - getPaddingRightWithForeground() + - lp.leftMargin - lp.rightMargin); + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + width, MeasureSpec.EXACTLY); + } else { + childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, + getPaddingLeftWithForeground() + getPaddingRightWithForeground() + + lp.leftMargin + lp.rightMargin, + lp.width); + } + + final int childHeightMeasureSpec; + if (lp.height == LayoutParams.MATCH_PARENT) { + final int height = Math.max(0, getMeasuredHeight() + - getPaddingTopWithForeground() - getPaddingBottomWithForeground() + - lp.topMargin - lp.bottomMargin); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + height, MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, + getPaddingTopWithForeground() + getPaddingBottomWithForeground() + + lp.topMargin + lp.bottomMargin, + lp.height); + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + private int measureAndGetHeight(View child, int widthMeasureSpec, int heightMeasureSpec) { + if (child != null) { + if (child.getVisibility() != GONE) { + applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec); + return child.getMeasuredHeight(); + } else if (getMeasureAllChildren()) { + applyMeasureToChild(mBottomPanel, widthMeasureSpec, heightMeasureSpec); + } + } + return 0; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int count = getChildCount(); + + final boolean measureMatchParentChildren = + MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || + MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; + mMatchParentChildren.clear(); + + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (getMeasureAllChildren() || child.getVisibility() != GONE) { + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + maxWidth = Math.max(maxWidth, + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); + maxHeight = Math.max(maxHeight, + child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + if (measureMatchParentChildren) { + if (lp.width == LayoutParams.MATCH_PARENT || + lp.height == LayoutParams.MATCH_PARENT) { + mMatchParentChildren.add(child); + } + } + } + } + + // Account for padding too + maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); + maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); + + // Check against our minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + // Check against our foreground's minimum height and width + final Drawable drawable = getForeground(); + if (drawable != null) { + maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); + maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); + } + + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + + if (mListView != null) { + if (mPendingScroll != 0) { + mListView.scrollListBy(mPendingScroll); + mPendingScroll = 0; + } + + int paddingTop = Math.max(mListView.getPaddingTop(), + measureAndGetHeight(mTopPanel, widthMeasureSpec, heightMeasureSpec)); + int paddingBottom = Math.max(mListView.getPaddingBottom(), + measureAndGetHeight(mBottomPanel, widthMeasureSpec, heightMeasureSpec)); + + if (paddingTop != mListView.getPaddingTop() + || paddingBottom != mListView.getPaddingBottom()) { + mPendingScroll += mListView.getPaddingTop() - paddingTop; + mListView.setPadding( + mListView.getPaddingLeft(), paddingTop, + mListView.getPaddingRight(), paddingBottom); + } + } + + count = mMatchParentChildren.size(); + if (count > 1) { + for (int i = 0; i < count; i++) { + final View child = mMatchParentChildren.get(i); + if (mListView == null || (child != mTopPanel && child != mBottomPanel)) { + applyMeasureToChild(child, widthMeasureSpec, heightMeasureSpec); + } + } + } + } + + @Override + public void setForegroundGravity(int foregroundGravity) { + if (getForegroundGravity() != foregroundGravity) { + super.setForegroundGravity(foregroundGravity); + + // calling get* again here because the set above may apply default constraints + final Drawable foreground = getForeground(); + if (getForegroundGravity() == Gravity.FILL && foreground != null) { + Rect padding = new Rect(); + if (foreground.getPadding(padding)) { + mForegroundPaddingLeft = padding.left; + mForegroundPaddingTop = padding.top; + mForegroundPaddingRight = padding.right; + mForegroundPaddingBottom = padding.bottom; + } + } else { + mForegroundPaddingLeft = 0; + mForegroundPaddingTop = 0; + mForegroundPaddingRight = 0; + mForegroundPaddingBottom = 0; + } + } + } + + private int getPaddingLeftWithForeground() { + return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) : + mPaddingLeft + mForegroundPaddingLeft; + } + + private int getPaddingRightWithForeground() { + return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) : + mPaddingRight + mForegroundPaddingRight; + } + + private int getPaddingTopWithForeground() { + return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) : + mPaddingTop + mForegroundPaddingTop; + } + + private int getPaddingBottomWithForeground() { + return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) : + mPaddingBottom + mForegroundPaddingBottom; + } + + @Override + public void onScrollChanged() { + if (mListView == null) { + return; + } + + if (mTopPanel != null) { + if (mListView.getChildCount() > 0) { + if (mListView.getFirstVisiblePosition() == 0) { + View firstChild = mListView.getChildAt(0); + setScrolling(mTopPanel, + firstChild.getY() - mTopPanel.getHeight() - mTopPanel.getTop()); + } else { + // shift to hide the frame, last child is not the last position + setScrolling(mTopPanel, -mTopPanel.getHeight()); + } + } else { + setScrolling(mTopPanel, 0); // no visible child, fallback to default behaviour + } + } + + if (mBottomPanel != null) { + if (mListView.getChildCount() > 0) { + if (mListView.getLastVisiblePosition() >= mListView.getCount() - 1) { + View lastChild = mListView.getChildAt(mListView.getChildCount() - 1); + setScrolling(mBottomPanel, + lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop()); + } else { + // shift to hide the frame, last child is not the last position + setScrolling(mBottomPanel, mBottomPanel.getHeight()); + } + } else { + setScrolling(mBottomPanel, 0); // no visible child, fallback to default behaviour + } + } + } + + /** Only set scrolling for the panel if there is a change in its translationY. */ + private void setScrolling(View panel, float translationY) { + if (panel.getTranslationY() != translationY) { + panel.setTranslationY(translationY); + } + } +} diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index bf27bed36a55a3684dd90ca724204bb7079e44f9..353888cc76780d6f10d571d2a2fea5ca040dbb21 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -44,6 +44,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * Loads global system configuration info. @@ -127,6 +129,11 @@ public class SystemConfig { final ArrayMap> mSignatureAllowances = new ArrayMap>(); + // These are the packages of carrier-associated apps which should be disabled until used until + // a SIM is inserted which grants carrier privileges to that carrier app. + final ArrayMap> mDisabledUntilUsedPreinstalledCarrierAssociatedApps = + new ArrayMap<>(); + public static SystemConfig getInstance() { synchronized (SystemConfig.class) { if (sInstance == null) { @@ -192,6 +199,10 @@ public class SystemConfig { return mSignatureAllowances; } + public ArrayMap> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() { + return mDisabledUntilUsedPreinstalledCarrierAssociatedApps; + } + SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( @@ -528,6 +539,26 @@ public class SystemConfig { } } XmlUtils.skipCurrentTag(parser); + } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name) + && allowAppConfigs) { + String pkgname = parser.getAttributeValue(null, "package"); + String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage"); + if (pkgname == null || carrierPkgname == null) { + Slog.w(TAG, " associatedPkgs = + mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get( + carrierPkgname); + if (associatedPkgs == null) { + associatedPkgs = new ArrayList<>(); + mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put( + carrierPkgname, associatedPkgs); + } + associatedPkgs.add(pkgname); + } + XmlUtils.skipCurrentTag(parser); } else { XmlUtils.skipCurrentTag(parser); continue; diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index d68124660a51e4832c2a0be859d13fee759913f3..d7550a4d96958b83e4a8d5413abae950cb258dfe 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -18,6 +18,7 @@ #include "CreateJavaOutputStreamAdaptor.h" #include #include +#include #include "core_jni_helpers.h" @@ -1361,6 +1362,14 @@ static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { return reinterpret_cast(pixelRef); } +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + if (!bitmapHandle.valid()) return; + SkBitmap bitmap; + bitmapHandle->getSkBitmap(&bitmap); + android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { @@ -1404,6 +1413,7 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, }; int register_android_graphics_Bitmap(JNIEnv* env) diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 898cf77cb32dcd785dbb678b05a4bb0a863df934..85092ad4fdd4738bb90cea7a7816472271a70b1b 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -768,6 +768,21 @@ namespace PaintGlue { return false; } + // Don't count glyphs that are the recommended "space" glyph and are zero width. + // This logic makes assumptions about HarfBuzz layout, but does correctly handle + // cases where ligatures form and zero width space glyphs are left in as + // placeholders. + static size_t countNonSpaceGlyphs(const Layout& layout) { + size_t count = 0; + static unsigned int kSpaceGlyphId = 3; + for (size_t i = 0; i < layout.nGlyphs(); i++) { + if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) { + count++; + } + } + return count; + } + // Returns true if the given string is exact one pair of regional indicators. static bool isFlag(const jchar* str, size_t length) { const jchar RI_LEAD_SURROGATE = 0xD83C; @@ -831,7 +846,7 @@ namespace PaintGlue { Layout layout; MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(), str.size()); - size_t nGlyphs = layout.nGlyphs(); + size_t nGlyphs = countNonSpaceGlyphs(layout); if (nGlyphs != 1 && nChars > 1) { // multiple-character input, and was not a ligature // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp index 24ee9591fc2f90e7dab6c7b5e357b72bce096528..3e7c039e2129c7d83a432e65f9ac4d56b0473ba8 100644 --- a/core/jni/android_app_ApplicationLoaders.cpp +++ b/core/jni/android_app_ApplicationLoaders.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "ApplicationLoaders" + #include #include #include @@ -22,10 +24,17 @@ static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring librarySearchPath) { + android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader); ScopedUtfChars layerPathChars(env, librarySearchPath); + vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance(); - loader_data.layer_path = layerPathChars.c_str(); - loader_data.app_namespace = android::FindNamespaceByClassLoader(env, classLoader); + if (loader_data.layer_path.empty()) { + loader_data.layer_path = layerPathChars.c_str(); + loader_data.app_namespace = ns; + } else { + ALOGD("ignored Vulkan layer search path %s for namespace %p", + layerPathChars.c_str(), ns); + } } static const JNINativeMethod g_methods[] = { diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index ded4daca07073d20158b89e2e7312ad8c01a7635..2ab4a356e4360c616b08c92e5453472a67c5868d 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -614,10 +614,10 @@ static const JNINativeMethod gMethods[] = { {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath}, {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, - {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, - {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, - {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars}, {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString}, diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp index 4b2a72d8e28e39ebd6da3d1cb86f1707380ad949..ade718b9e6b80603892339bb6c5502c2b9abf656 100644 --- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp +++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -83,11 +83,13 @@ static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishLis } static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr, - jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount) { + jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount, + jint repeatMode) { PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); PropertyValuesHolder* holder = reinterpret_cast(propertyHolderPtr); Interpolator* interpolator = reinterpret_cast(interpolatorPtr); - set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount); + RepeatMode mode = static_cast(repeatMode); + set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount, mode); } static jlong createAnimatorSet(JNIEnv*, jobject) { @@ -95,6 +97,12 @@ static jlong createAnimatorSet(JNIEnv*, jobject) { return reinterpret_cast(animatorSet); } +static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) { + VectorDrawable::Tree* tree = reinterpret_cast(vectorDrawablePtr); + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorPtr); + set->setVectorDrawable(tree); +} + static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, jfloat startValue, jfloat endValue) { VectorDrawable::Group* group = reinterpret_cast(nativePtr); @@ -136,14 +144,24 @@ static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jf startValue, endValue); return reinterpret_cast(newHolder); } -static void setPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, +static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, jfloatArray srcData, jint length) { - jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr); - PropertyValuesHolder* holder = reinterpret_cast(propertyHolderPtr); + PropertyValuesHolderImpl* holder = + reinterpret_cast*>(propertyHolderPtr); holder->setPropertyDataSource(propertyData, length); env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT); } + +static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, + jintArray srcData, jint length) { + jint* propertyData = env->GetIntArrayElements(srcData, nullptr); + PropertyValuesHolderImpl* holder = + reinterpret_cast*>(propertyHolderPtr); + holder->setPropertyDataSource(propertyData, length); + env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT); +} + static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); AnimationListener* listener = createAnimationListener(env, finishListener, id); @@ -168,13 +186,15 @@ static void reset(JNIEnv*, jobject, jlong animatorSetPtr) { static const JNINativeMethod gMethods[] = { {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet}, - {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator}, + {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget}, + {"nAddAnimator", "(JJJJJII)V", (void*)addAnimator}, {"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder}, {"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder}, {"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder}, {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder}, {"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder}, - {"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData}, + {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData}, + {"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData}, {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start}, {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse}, {"nEnd", "!(J)V", (void*)end}, diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 69288903bf2a5ed1f8cca4c91a12c698a1655411..db44dd260fd4bda95eb9df24bc6ea39091af03ae 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -787,109 +787,16 @@ static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyN // exception thrown by ScopedUtfChars return 0; } - size_t keyLength = strlen(key); - ALOGV("%s (key = '%s')", __FUNCTION__, key); - sp vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); - - SortedVector vendorSections; - size_t vendorSectionCount = 0; - - if (vTags != NULL) { - vendorSections = vTags->getAllSectionNames(); - vendorSectionCount = vendorSections.size(); - } - - // First, find the section by the longest string match - const char *section = NULL; - size_t sectionIndex = 0; - size_t sectionLength = 0; - size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount; - for (size_t i = 0; i < totalSectionCount; ++i) { - - const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] : - vendorSections[i - ANDROID_SECTION_COUNT].string(); - if (kIsDebug) { - ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str); - } - if (strstr(key, str) == key) { // key begins with the section name - size_t strLength = strlen(str); - - if (kIsDebug) { - ALOGV("%s: Key begins with section name", __FUNCTION__); - } - - // section name is the longest we've found so far - if (section == NULL || sectionLength < strLength) { - section = str; - sectionIndex = i; - sectionLength = strLength; - - if (kIsDebug) { - ALOGV("%s: Found new best section (%s)", __FUNCTION__, section); - } - } - } - } - - // TODO: Make above get_camera_metadata_section_from_name ? - - if (section == NULL) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "Could not find section name for key '%s')", key); - return 0; - } else { - ALOGV("%s: Found matched section '%s' (%zu)", - __FUNCTION__, section, sectionIndex); - } - - // Get the tag name component of the key - const char *keyTagName = key + sectionLength + 1; // x.y.z -> z - if (sectionLength + 1 >= keyLength) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "Key length too short for key '%s')", key); - return 0; - } - - // Match rest of name against the tag names in that section only uint32_t tag = 0; - if (sectionIndex < ANDROID_SECTION_COUNT) { - // Match built-in tags (typically android.*) - uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd) - tagBegin = camera_metadata_section_bounds[sectionIndex][0]; - tagEnd = camera_metadata_section_bounds[sectionIndex][1]; - - for (tag = tagBegin; tag < tagEnd; ++tag) { - const char *tagName = get_camera_metadata_tag_name(tag); - - if (strcmp(keyTagName, tagName) == 0) { - ALOGV("%s: Found matched tag '%s' (%d)", - __FUNCTION__, tagName, tag); - break; - } - } - - if (tag == tagEnd) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "Could not find tag name for key '%s')", key); - return 0; - } - } else if (vTags != NULL) { - // Match vendor tags (typically com.*) - const String8 sectionName(section); - const String8 tagName(keyTagName); - - status_t res = OK; - if ((res = vTags->lookupTag(tagName, sectionName, &tag)) != OK) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "%s: No vendor tag matches key '%s'", __FUNCTION__, key); - return 0; - } + sp vTags = + VendorTagDescriptor::getGlobalVendorTagDescriptor(); + status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Could not find tag for key '%s')", key); } - - // TODO: Make above get_camera_metadata_tag_from_name ? - return tag; } diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp index 3881d6d9e99f4d6e8bacc77b7c9a18eb4b3d0568..9515a0e336aa5109bc22eb19b50906e7555843e6 100644 --- a/core/jni/android_hardware_location_ContextHubService.cpp +++ b/core/jni/android_hardware_location_ContextHubService.cpp @@ -21,28 +21,34 @@ #include #include -#include -#include +#include #include #include #include #include +#include +#include #include #include "JNIHelp.h" #include "core_jni_helpers.h" -static constexpr int OS_APP_ID=-1; +static constexpr int OS_APP_ID = -1; +static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF); + +static constexpr int MIN_APP_ID = 1; +static constexpr int MAX_APP_ID = 128; -static constexpr int MIN_APP_ID=1; -static constexpr int MAX_APP_ID=128; +static constexpr size_t MSG_HEADER_SIZE = 4; +static constexpr size_t HEADER_FIELD_MSG_TYPE = 0; +static constexpr size_t HEADER_FIELD_MSG_VERSION = 1; +static constexpr size_t HEADER_FIELD_HUB_HANDLE = 2; +static constexpr size_t HEADER_FIELD_APP_INSTANCE = 3; -static constexpr size_t MSG_HEADER_SIZE=4; -static constexpr int HEADER_FIELD_MSG_TYPE=0; -//static constexpr int HEADER_FIELD_MSG_VERSION=1; -static constexpr int HEADER_FIELD_HUB_HANDLE=2; -static constexpr int HEADER_FIELD_APP_INSTANCE=3; +static constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE; +static constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1; +static constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2; namespace android { @@ -83,6 +89,7 @@ struct jniInfo_s { jmethodID contextHubServiceMsgReceiptCallback; jmethodID contextHubServiceAddAppInstance; + jmethodID contextHubServiceDeleteAppInstance; }; struct context_hub_info_s { @@ -93,10 +100,53 @@ struct context_hub_info_s { }; struct app_instance_info_s { - uint32_t hubHandle; // Id of the hub this app is on - int instanceId; // systemwide unique instance id - assigned + uint64_t truncName; // Possibly truncated name for logging + uint32_t hubHandle; // Id of the hub this app is on + int instanceId; // system wide unique instance id - assigned struct hub_app_info appInfo; // returned from the HAL - uint64_t truncName; // Possibly truncated name - logging +}; + +/* + * TODO(ashutoshj): From original code review: + * + * So, I feel like we could possible do a better job of organizing this code, + * and being more C++-y. Consider something like this: + * class TxnManager { + * public: + * TxnManager(); + * ~TxnManager(); + * int add(hub_message_e identifier, void *data); + * int close(); + * bool isPending() const; + * int fetchData(hub_message_e *identifier, void **data) const; + * + * private: + * bool mPending; + * mutable std::mutex mLock; + * hub_message_e mIdentifier; + * void *mData; + * }; + * + * And then, for example, we'd have things like: + * TxnManager::TxnManager() : mPending(false), mLock(), mIdentifier(), mData(nullptr) {} + * int TxnManager::add(hub_message_e identifier, void *data) { + * std::lock_guard lock(mLock); + * mPending = true; + * mData = txnData; + * mIdentifier = txnIdentifier; + * return 0; + * } + * And then calling code would look like: + * if (!db.txnManager.add(CONTEXT_HUB_LOAD_APP, txnInfo)) { + * + * This would make it clearer the nothing is manipulating any state within TxnManager + * unsafely and outside of these couple of calls. + */ +struct txnManager_s { + bool txnPending; // Is a transaction pending + std::mutex m; // mutex for manager + hub_messages_e txnIdentifier; // What are we doing + void *txnData; // Details }; struct contextHubServiceDb_s { @@ -105,12 +155,69 @@ struct contextHubServiceDb_s { jniInfo_s jniInfo; std::queue freeIds; std::unordered_map appInstances; + txnManager_s txnManager; }; } // unnamed namespace static contextHubServiceDb_s db; +static bool initTxnManager() { + txnManager_s *mgr = &db.txnManager; + + mgr->txnData = nullptr; + mgr->txnPending = false; + return true; +} + +static int addTxn(hub_messages_e txnIdentifier, void *txnData) { + txnManager_s *mgr = &db.txnManager; + + std::lock_guardlock(mgr->m); + + mgr->txnPending = true; + mgr->txnData = txnData; + mgr->txnIdentifier = txnIdentifier; + + return 0; +} + +static int closeTxn() { + txnManager_s *mgr = &db.txnManager; + std::lock_guardlock(mgr->m); + mgr->txnPending = false; + free(mgr->txnData); + mgr->txnData = nullptr; + + return 0; +} + +static bool isTxnPending() { + txnManager_s *mgr = &db.txnManager; + std::lock_guardlock(mgr->m); + return mgr->txnPending; +} + +static int fetchTxnData(hub_messages_e *id, void **data) { + txnManager_s *mgr = &db.txnManager; + + if (!id || !data) { + ALOGW("Null params id %p, data %p", id, data); + return -1; + } + + std::lock_guardlock(mgr->m); + if (!mgr->txnPending) { + ALOGW("No Transactions pending"); + return -1; + } + + // else + *id = mgr->txnIdentifier; + *data = mgr->txnData; + return 0; +} + int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg, void *cookie); @@ -152,13 +259,21 @@ static int get_hub_id_for_hub_handle(int hubHandle) { } } -static int get_hub_id_for_app_instance(int id) { +static int get_hub_handle_for_app_instance(int id) { if (!db.appInstances.count(id)) { ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id); return -1; } - int hubHandle = db.appInstances[id].hubHandle; + return db.appInstances[id].hubHandle; +} + +static int get_hub_id_for_app_instance(int id) { + int hubHandle = get_hub_handle_for_app_instance(id); + + if (hubHandle < 0) { + return -1; + } return db.hubInfo.hubs[hubHandle].hub_id; } @@ -184,29 +299,41 @@ static int set_dest_app(hub_message_t *msg, int id) { return 0; } -static void send_query_for_apps() { +static void query_hub_for_apps(uint64_t appId, uint32_t hubHandle) { hub_message_t msg; + query_apps_request_t queryMsg; + + queryMsg.app_name.id = NANOAPP_VENDOR_ALL_APPS; msg.message_type = CONTEXT_HUB_QUERY_APPS; - msg.message_len = 0; + msg.message_len = sizeof(queryMsg); + msg.message = &queryMsg; + ALOGD("Sending query for apps to hub %" PRIu32, hubHandle); + set_os_app_as_destination(&msg, hubHandle); + if (send_msg_to_hub(&msg, hubHandle) != 0) { + ALOGW("Could not query hub %" PRIu32 " for apps", hubHandle); + } +} + +static void sendQueryForApps(uint64_t appId) { for (int i = 0; i < db.hubInfo.numHubs; i++ ) { - ALOGD("Sending query for apps to hub %d", i); - set_os_app_as_destination(&msg, i); - if (send_msg_to_hub(&msg, i) != 0) { - ALOGW("Could not query hub %i for apps", i); - } + query_hub_for_apps(appId, i); } } static int return_id(int id) { // Note : This method is not thread safe. - // id returned is guarenteed to be in use - db.freeIds.push(id); - return 0; + // id returned is guaranteed to be in use + if (id >= 0) { + db.freeIds.push(id); + return 0; + } + + return -1; } -static int generate_id(void) { +static int generate_id() { // Note : This method is not thread safe. int retVal = -1; @@ -218,23 +345,31 @@ static int generate_id(void) { return retVal; } -int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) { + +static int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, + int appInstanceHandle, JNIEnv *env) { + + ALOGI("Loading App"); + // Not checking if the apps are indeed distinct app_instance_info_s entry; - int appInstanceHandle = generate_id(); - assert(appInfo); - if (appInstanceHandle < 0) { - ALOGE("Cannot find resources to add app instance %d", - appInstanceHandle); - return -1; + if (db.appInstances.count(appInstanceHandle) == 0) { + appInstanceHandle = generate_id(); + if (appInstanceHandle < 0) { + ALOGE("Cannot find resources to add app instance %d", + appInstanceHandle); + return -1; + } } entry.appInfo = *appInfo; + entry.instanceId = appInstanceHandle; entry.truncName = appInfo->app_name.id; entry.hubHandle = hubHandle; + db.appInstances[appInstanceHandle] = entry; // Finally - let the service know of this app instance @@ -250,17 +385,70 @@ int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *en return appInstanceHandle; } -int delete_app_instance(int id) { +int delete_app_instance(int id, JNIEnv *env) { if (!db.appInstances.count(id)) { + ALOGW("Cannot find App id : %d", id); return -1; } return_id(id); db.appInstances.erase(id); + if (env->CallIntMethod(db.jniInfo.jContextHubService, + db.jniInfo.contextHubServiceDeleteAppInstance, + id) != 0) { + ALOGW("Could not delete App id : %d", id); + return -1; + } + + ALOGI("Deleted App id : %d", id); + + return 0; +} + +static int startLoadAppTxn(uint64_t appId, int hubHandle) { + app_instance_info_s *txnInfo = (app_instance_info_s *)malloc(sizeof(app_instance_info_s)); + int instanceId = generate_id(); + + if (!txnInfo || instanceId < 0) { + return_id(instanceId); + free(txnInfo); + return -1; + } + + txnInfo->truncName = appId; + txnInfo->hubHandle = hubHandle; + txnInfo->instanceId = instanceId; + + txnInfo->appInfo.app_name.id = appId; + txnInfo->appInfo.num_mem_ranges = 0; + txnInfo->appInfo.version = -1; // Awaited + + if (addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) { + return_id(instanceId); + free(txnInfo); + return -1; + } return 0; } +static int startUnloadAppTxn(uint32_t appInstanceHandle) { + uint32_t *txnData = (uint32_t *) malloc(sizeof(uint32_t)); + if (!txnData) { + ALOGW("Cannot allocate memory to start unload transaction"); + return -1; + } + + *txnData = appInstanceHandle; + + if (addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) { + free(txnData); + ALOGW("Cannot start transaction to unload app"); + return -1; + } + + return 0; +} static void initContextHubService() { int err = 0; @@ -281,6 +469,7 @@ static void initContextHubService() { db.freeIds.push(i); } + initTxnManager(); if (db.hubInfo.contextHubModule) { int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule, &db.hubInfo.hubs); @@ -298,6 +487,7 @@ static void initContextHubService() { for (i = 0; i < db.hubInfo.numHubs; i++) { db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id; + ALOGI("Subscribing to hubHandle %d with OS App name %" PRIu64, i, db.hubInfo.hubs[i].os_app_name.id); if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id, context_hub_callback, &db.hubInfo.cookies[i]) == 0) { @@ -305,7 +495,7 @@ static void initContextHubService() { } } - send_query_for_apps(); + sendQueryForApps(ALL_APPS); } else { ALOGW("No Context Hub Module present"); } @@ -342,7 +532,8 @@ static int onMessageReceipt(uint32_t *header, size_t headerLen, char *msg, size_ return ret; } -int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) { +int handle_query_apps_response(const uint8_t *msg, int msgLen, + uint32_t hubHandle) { JNIEnv *env; if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { return -1; @@ -350,53 +541,202 @@ int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) { int numApps = msgLen/sizeof(hub_app_info); hub_app_info info; - hub_app_info *unalignedInfoAddr = (hub_app_info*)msg; + const hub_app_info *unalignedInfoAddr = (const hub_app_info*)msg; for (int i = 0; i < numApps; i++, unalignedInfoAddr++) { memcpy(&info, unalignedInfoAddr, sizeof(info)); - add_app_instance(&info, hubHandle, env); + // We will only have one instance of the app + // TODO : Change this logic once we support multiple instances of the same app + int appInstance = get_app_instance_for_app_id(info.app_name.id); + add_app_instance(&info, hubHandle, appInstance, env); } return 0; } +static void passOnOsResponse(uint32_t hubHandle, uint32_t msgType, + status_response_t *rsp, int8_t *additionalData, + size_t additionalDataLen) { + JNIEnv *env; -int handle_os_message(uint32_t msgType, uint32_t hubHandle, - char *msg, int msgLen) { - int retVal; + if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { + ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32, msgType); + return; + } - //ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d", - // hubHandle, msgType, msgLen); + uint32_t header[MSG_HEADER_SIZE]; + memset(header, 0, sizeof(header)); - switch(msgType) { - case CONTEXT_HUB_APPS_ENABLE: - retVal = 0; - break; + if (!additionalData) { + additionalDataLen = 0; // clamp + } + int msgLen = 1 + additionalDataLen; - case CONTEXT_HUB_APPS_DISABLE: - retVal = 0; - break; + int8_t *msg = new int8_t[msgLen]; - case CONTEXT_HUB_LOAD_APP: - retVal = 0; - break; + if (!msg) { + ALOGW("Unexpected : Ran out of memory, cannot send response"); + return; + } + + header[HEADER_FIELD_MSG_TYPE] = msgType; + header[HEADER_FIELD_MSG_VERSION] = 0; + header[HEADER_FIELD_HUB_HANDLE] = hubHandle; + header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID; + + msg[0] = rsp->result; + + if (additionalData) { + memcpy(&msg[1], additionalData, additionalDataLen); + } + + jbyteArray jmsg = env->NewByteArray(msgLen); + jintArray jheader = env->NewIntArray(sizeof(header)); + + env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg); + env->SetIntArrayRegion(jheader, 0, sizeof(header), (jint *)header); + + ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32, + header[HEADER_FIELD_MSG_TYPE], header[HEADER_FIELD_APP_INSTANCE], + header[HEADER_FIELD_HUB_HANDLE]); + + env->CallIntMethod(db.jniInfo.jContextHubService, + db.jniInfo.contextHubServiceMsgReceiptCallback, + jheader, jmsg); + + delete[] msg; +} + +void closeUnloadTxn(bool success) { + void *txnData = nullptr; + hub_messages_e txnId; + + if (success && fetchTxnData(&txnId, &txnData) == 0 && + txnId == CONTEXT_HUB_UNLOAD_APP) { + db.appInstances.erase(*(uint32_t *)txnData); + } else { + ALOGW("Could not unload the app successfully ! success %d, txnData %p", success, txnData); + } + + closeTxn(); +} + +void closeLoadTxn(bool success, int *appInstanceHandle) { + void *txnData; + hub_messages_e txnId; + + if (success && fetchTxnData(&txnId, &txnData) == 0 && + txnId == CONTEXT_HUB_LOAD_APP) { + app_instance_info_s *info = (app_instance_info_s *)txnData; + *appInstanceHandle = info->instanceId; + + JNIEnv *env; + if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) { + add_app_instance(&info->appInfo, info->hubHandle, info->instanceId, env); + } else { + ALOGW("Could not attach to JVM !"); + } + sendQueryForApps(info->appInfo.app_name.id); + } else { + ALOGW("Could not load the app successfully ! Unexpected failure"); + } + + closeTxn(); +} + +static bool isValidOsStatus(const uint8_t *msg, size_t msgLen, + status_response_t *rsp) { + // Workaround a bug in some HALs + if (msgLen == 1) { + rsp->result = msg[0]; + return true; + } + + if (!msg || msgLen != sizeof(*rsp)) { + ALOGW("Received invalid response %p of size %zu", msg, msgLen); + return false; + } + + memcpy(rsp, msg, sizeof(*rsp)); + + // No sanity checks on return values + return true; +} + +static void invalidateNanoApps(uint32_t hubHandle) { + JNIEnv *env; + + if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { + ALOGW("Could not attach to JVM !"); + } + + auto end = db.appInstances.end(); + for (auto current = db.appInstances.begin(); current != end; ) { + app_instance_info_s info = current->second; + current++; + if (info.hubHandle == hubHandle) { + delete_app_instance(info.instanceId, env); + } + } +} - case CONTEXT_HUB_UNLOAD_APP: - retVal = 0; - break; +static int handle_os_message(uint32_t msgType, uint32_t hubHandle, + const uint8_t *msg, int msgLen) { + int retVal = -1; - case CONTEXT_HUB_QUERY_APPS: - retVal = handle_query_apps_response(msg, msgLen, hubHandle); - break; + ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d", + hubHandle, msgType, msgLen); - case CONTEXT_HUB_QUERY_MEMORY: - retVal = 0; - break; + struct status_response_t rsp; - default: - retVal = -1; - break; + switch(msgType) { + case CONTEXT_HUB_APPS_ENABLE: + case CONTEXT_HUB_APPS_DISABLE: + case CONTEXT_HUB_LOAD_APP: + case CONTEXT_HUB_UNLOAD_APP: + if (isValidOsStatus(msg, msgLen, &rsp)) { + if (msgType == CONTEXT_HUB_LOAD_APP) { + int appInstanceHandle; + closeLoadTxn(rsp.result == 0, &appInstanceHandle); + passOnOsResponse(hubHandle, msgType, &rsp, (int8_t *)(&appInstanceHandle), + sizeof(appInstanceHandle)); + } else if (msgType == CONTEXT_HUB_UNLOAD_APP) { + closeUnloadTxn(rsp.result == 0); + passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0); + } else { + passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0); + } + retVal = 0; + } + break; + + case CONTEXT_HUB_QUERY_APPS: + rsp.result = 0; + retVal = handle_query_apps_response(msg, msgLen, hubHandle); + passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0); + break; + + case CONTEXT_HUB_QUERY_MEMORY: + // Deferring this use + retVal = 0; + break; + + case CONTEXT_HUB_OS_REBOOT: + if (isValidOsStatus(msg, msgLen, &rsp)) { + rsp.result = 0; + ALOGW("Context Hub handle %d restarted", hubHandle); + closeTxn(); + passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0); + invalidateNanoApps(hubHandle); + query_hub_for_apps(ALL_APPS, hubHandle); + retVal = 0; + } + break; + + default: + retVal = -1; + break; } return retVal; @@ -416,10 +756,12 @@ static bool sanity_check_cookie(void *cookie, uint32_t hub_id) { } } + int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg, void *cookie) { if (!msg) { + ALOGW("NULL message"); return -1; } if (!sanity_check_cookie(cookie, hubId)) { @@ -429,11 +771,12 @@ int context_hub_callback(uint32_t hubId, return -1; } + uint32_t messageType = msg->message_type; uint32_t hubHandle = *(uint32_t*) cookie; if (messageType < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) { - handle_os_message(messageType, hubHandle, (char*) msg->message, msg->message_len); + handle_os_message(messageType, hubHandle, (uint8_t*) msg->message, msg->message_len); } else { int appHandle = get_app_instance_for_app_id(msg->app_name.id); if (appHandle < 0) { @@ -524,7 +867,9 @@ static int init_jni(JNIEnv *env, jobject instance) { env->GetMethodID(db.jniInfo.contextHubServiceClass, "addAppInstance", "(IIJI)I"); - + db.jniInfo.contextHubServiceDeleteAppInstance = + env->GetMethodID(db.jniInfo.contextHubServiceClass, + "deleteAppInstance", "(I)I"); return 0; } @@ -534,8 +879,6 @@ static jobject constructJContextHubInfo(JNIEnv *env, const struct context_hub_t jintArray jintBuf; jobjectArray jmemBuf; - int dummyConnectedSensors[] = {1, 2, 3, 4, 5}; - jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass, db.jniInfo.contextHubInfoCtor); env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub->hub_id); @@ -565,11 +908,21 @@ static jobject constructJContextHubInfo(JNIEnv *env, const struct context_hub_t hub->max_supported_msg_len); - // TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors); - // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, - // hub->connected_sensors); - jintBuf = env->NewIntArray(array_length(dummyConnectedSensors)); - env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors); + jintBuf = env->NewIntArray(hub->num_connected_sensors); + int *connectedSensors = new int[hub->num_connected_sensors]; + + if (!connectedSensors) { + ALOGW("Cannot allocate memory! Unexpected"); + assert(false); + } else { + for (unsigned int i = 0; i < hub->num_connected_sensors; i++) { + connectedSensors[i] = hub->connected_sensors[i].sensor_id; + } + } + + env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, + connectedSensors); + env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf); env->DeleteLocalRef(jintBuf); @@ -580,6 +933,7 @@ static jobject constructJContextHubInfo(JNIEnv *env, const struct context_hub_t env->DeleteLocalRef(jmemBuf); + delete[] connectedSensors; return jHub; } @@ -618,33 +972,98 @@ static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_, jbyte *data = env->GetByteArrayElements(data_, 0); int dataBufferLength = env->GetArrayLength(data_); + if (numHeaderElements < MSG_HEADER_SIZE) { + ALOGW("Malformed header len"); + return -1; + } + + uint32_t appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE]; + uint32_t msgType = header[HEADER_FIELD_MSG_TYPE]; + int hubHandle = -1; + int hubId; + uint64_t appId; + + if (msgType == CONTEXT_HUB_UNLOAD_APP) { + hubHandle = get_hub_handle_for_app_instance(appInstanceHandle); + } else if (msgType == CONTEXT_HUB_LOAD_APP) { + if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) { + return -1; + } + uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO]; + uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI]; + appId = appIdHi << 32 | appIdLo; + + hubHandle = header[HEADER_FIELD_HUB_HANDLE]; + } else { + hubHandle = header[HEADER_FIELD_HUB_HANDLE]; + } + + if (hubHandle < 0) { + ALOGD("Invalid hub Handle %d", hubHandle); + return -1; + } + + if (msgType == CONTEXT_HUB_LOAD_APP || + msgType == CONTEXT_HUB_UNLOAD_APP) { + + if (isTxnPending()) { + ALOGW("Cannot load or unload app while a transaction is pending !"); + return -1; + } - if (numHeaderElements >= MSG_HEADER_SIZE) { - bool setAddressSuccess; - int hubId; - hub_message_t msg; + if (msgType == CONTEXT_HUB_LOAD_APP) { + if (startLoadAppTxn(appId, hubHandle) != 0) { + ALOGW("Cannot Start Load Transaction"); + return -1; + } + } else if (msgType == CONTEXT_HUB_UNLOAD_APP) { + if (startUnloadAppTxn(appInstanceHandle) != 0) { + ALOGW("Cannot Start UnLoad Transaction"); + return -1; + } + } + } + + bool setAddressSuccess = false; + hub_message_t msg; + + msg.message_type = msgType; + + if (msgType == CONTEXT_HUB_UNLOAD_APP) { + msg.message_len = sizeof(db.appInstances[appInstanceHandle].appInfo.app_name); + msg.message = &db.appInstances[appInstanceHandle].appInfo.app_name; + setAddressSuccess = (set_os_app_as_destination(&msg, hubHandle) == 0); + hubId = get_hub_id_for_hub_handle(hubHandle); + } else { + msg.message_len = dataBufferLength; + msg.message = data; if (header[HEADER_FIELD_APP_INSTANCE] == OS_APP_ID) { - setAddressSuccess = (set_os_app_as_destination(&msg, header[HEADER_FIELD_HUB_HANDLE]) == 0); - hubId = get_hub_id_for_hub_handle(header[HEADER_FIELD_HUB_HANDLE]); + setAddressSuccess = (set_os_app_as_destination(&msg, hubHandle) == 0); + hubId = get_hub_id_for_hub_handle(hubHandle); } else { setAddressSuccess = (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0); hubId = get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]); } + } - if (setAddressSuccess && hubId >= 0) { - msg.message_type = header[HEADER_FIELD_MSG_TYPE]; - msg.message_len = dataBufferLength; - msg.message = data; - retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg); - } else { - ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d", - header[HEADER_FIELD_APP_INSTANCE], - header[HEADER_FIELD_HUB_HANDLE], - (int)setAddressSuccess); - } + if (setAddressSuccess && hubId >= 0) { + ALOGD("Asking HAL to remove app"); + retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg); } else { - ALOGD("Malformed header len"); + ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d", + header[HEADER_FIELD_APP_INSTANCE], + header[HEADER_FIELD_HUB_HANDLE], + (int)setAddressSuccess); + } + + if (retVal != 0) { + ALOGD("Send Message failure - %d", retVal); + if (msgType == CONTEXT_HUB_LOAD_APP) { + closeLoadTxn(false, nullptr); + } else if (msgType == CONTEXT_HUB_UNLOAD_APP) { + closeUnloadTxn(false); + } } env->ReleaseIntArrayElements(header_, header, 0); diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 973b0a5119f6fb7c8191e8bbfcdef15d6a1b85e2..defd4dcb2ea7b923a81eef425d0e9673ba78ed5c 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -32,6 +32,7 @@ #define ENCODING_AAC_HE_V1 11 #define ENCODING_AAC_HE_V2 12 #define ENCODING_IEC61937 13 +#define ENCODING_DOLBY_TRUEHD 14 #define ENCODING_AMR_NB 100 #define ENCODING_AMR_WB 101 @@ -73,6 +74,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_AAC_HE_V1; case ENCODING_AAC_HE_V2: return AUDIO_FORMAT_AAC_HE_V2; + case ENCODING_DOLBY_TRUEHD: + return AUDIO_FORMAT_DOLBY_TRUEHD; case ENCODING_IEC61937: return AUDIO_FORMAT_IEC61937; case ENCODING_AMR_NB: @@ -140,6 +143,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_EVRC_WB; case AUDIO_FORMAT_EVRCNW: return ENCODING_EVRC_NW; + case AUDIO_FORMAT_DOLBY_TRUEHD: + return ENCODING_DOLBY_TRUEHD; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index ef16ef5055de4f53170d0100fc67a0414cd2c9e9..497600212095c9095c2d9a520c3696894671b125 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -20,13 +20,14 @@ #define LOG_TAG "AudioSystem-JNI" #include +#include #include #include #include "core_jni_helpers.h" #include #include - +#include #include #include #include "android_media_AudioFormat.h" @@ -903,6 +904,12 @@ static bool hasFormat(int* formats, size_t size, int format) { return false; // not found } +// TODO: pull out to separate file +template +static constexpr size_t array_size(const T (&)[N]) { + return N; +} + static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, const struct audio_port *nAudioPort) { @@ -923,6 +930,38 @@ static jint convertAudioPortFromNative(JNIEnv *env, ALOGV("convertAudioPortFromNative id %d role %d type %d name %s", nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name); + // Verify audio port array count info. + if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates) + || nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks) + || nAudioPort->num_formats > array_size(nAudioPort->formats) + || nAudioPort->num_gains > array_size(nAudioPort->gains)) { + + std::stringstream ss; + ss << "convertAudioPortFromNative array count out of bounds:" + << " num_sample_rates " << nAudioPort->num_sample_rates + << " num_channel_masks " << nAudioPort->num_channel_masks + << " num_formats " << nAudioPort->num_formats + << " num_gains " << nAudioPort->num_gains + ; + std::string s = ss.str(); + + // Prefer to log through Java wtf instead of native ALOGE. + ScopedLocalRef jLogClass(env, env->FindClass("android/util/Log")); + jmethodID jWtfId = (jLogClass.get() == nullptr) + ? nullptr + : env->GetStaticMethodID(jLogClass.get(), "wtf", + "(Ljava/lang/String;Ljava/lang/String;)I"); + if (jWtfId != nullptr) { + ScopedLocalRef jMessage(env, env->NewStringUTF(s.c_str())); + ScopedLocalRef jTag(env, env->NewStringUTF(LOG_TAG)); + (void)env->CallStaticIntMethod(jLogClass.get(), jWtfId, jTag.get(), jMessage.get()); + } else { + ALOGE("%s", s.c_str()); + } + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates); if (jSamplingRates == NULL) { jStatus = (jint)AUDIO_JAVA_ERROR; @@ -1066,7 +1105,7 @@ static jint convertAudioPortFromNative(JNIEnv *env, &jAudioPortConfig, &nAudioPort->active_config); if (jStatus != AUDIO_JAVA_SUCCESS) { - return jStatus; + goto exit; } env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 23647871df8733f693d1193898c8799d4fc1f0f4..679e8829999242bc9bde38aa9598cba581492260 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -125,6 +125,99 @@ static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject } } +static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, + jint ifIndex) +{ + static const int kLinkLocalHopLimit = 255; + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + + // Set an ICMPv6 filter that only passes Router Solicitations. + struct icmp6_filter rs_only; + ICMP6_FILTER_SETBLOCKALL(&rs_only); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only); + socklen_t len = sizeof(rs_only); + if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(ICMP6_FILTER): %s", strerror(errno)); + return; + } + + // Most/all of the rest of these options can be set via Java code, but + // because we're here on account of setting an icmp6_filter go ahead + // and do it all natively for now. + // + // TODO: Consider moving these out to Java. + + // Set the multicast hoplimit to 255 (link-local only). + int hops = kLinkLocalHopLimit; + len = sizeof(hops); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); + return; + } + + // Set the unicast hoplimit to 255 (link-local only). + hops = kLinkLocalHopLimit; + len = sizeof(hops); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); + return; + } + + // Explicitly disable multicast loopback. + int off = 0; + len = sizeof(off); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); + return; + } + + // Specify the IPv6 interface to use for outbound multicast. + len = sizeof(ifIndex); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); + return; + } + + // Additional options to be considered: + // - IPV6_TCLASS + // - IPV6_RECVPKTINFO + // - IPV6_RECVHOPLIMIT + + // Bind to [::]. + const struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_flowinfo = 0, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_scope_id = 0, + }; + auto sa = reinterpret_cast(&sin6); + len = sizeof(sin6); + if (bind(fd, sa, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "bind(IN6ADDR_ANY): %s", strerror(errno)); + return; + } + + // Join the all-routers multicast group, ff02::2%index. + struct ipv6_mreq all_rtrs = { + .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}, + .ipv6mr_interface = ifIndex, + }; + len = sizeof(all_rtrs); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); + return; + } +} + static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) { return (jboolean) !setNetworkForProcess(netId); @@ -173,6 +266,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, + { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, }; int register_android_net_NetworkUtils(JNIEnv* env) diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 3d952b0f713fa1dbe716b88fc030e229e1b6f342..3c72274f1fcaf5e332db108729ab6291b73a6b19 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -58,21 +58,19 @@ static pthread_key_t gBgKey = -1; #endif // For both of these, err should be in the errno range (positive), not a status_t (negative) - -static void signalExceptionForPriorityError(JNIEnv* env, int err) -{ +static void signalExceptionForError(JNIEnv* env, int err, int tid) { switch (err) { case EINVAL: - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Invalid argument: %d", tid); break; case ESRCH: - jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist"); + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Given thread %d does not exist", tid); break; case EPERM: - jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread"); - break; - case EACCES: - jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority"); + jniThrowExceptionFmt(env, "java/lang/SecurityException", + "No permission to modify given thread %d", tid); break; default: jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); @@ -80,23 +78,27 @@ static void signalExceptionForPriorityError(JNIEnv* env, int err) } } -static void signalExceptionForGroupError(JNIEnv* env, int err) -{ +static void signalExceptionForPriorityError(JNIEnv* env, int err, int tid) { switch (err) { - case EINVAL: - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - break; - case ESRCH: - jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist"); + case EACCES: + jniThrowExceptionFmt(env, "java/lang/SecurityException", + "No permission to set the priority of %d", tid); break; - case EPERM: - jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread"); + default: + signalExceptionForError(env, err, tid); break; + } + +} + +static void signalExceptionForGroupError(JNIEnv* env, int err, int tid) { + switch (err) { case EACCES: - jniThrowException(env, "java/lang/SecurityException", "No permission to set to given group"); + jniThrowExceptionFmt(env, "java/lang/SecurityException", + "No permission to set the group of %d", tid); break; default: - jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); + signalExceptionForError(env, err, tid); break; } } @@ -171,7 +173,7 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint SchedPolicy sp = (SchedPolicy) grp; int res = set_sched_policy(tid, sp); if (res != NO_ERROR) { - signalExceptionForGroupError(env, -res); + signalExceptionForGroupError(env, -res, tid); } } @@ -183,7 +185,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin struct dirent *de; if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) { - signalExceptionForGroupError(env, EINVAL); + signalExceptionForGroupError(env, EINVAL, pid); return; } @@ -219,7 +221,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin if (!(d = opendir(proc_path))) { // If the process exited on us, don't generate an exception if (errno != ENOENT) - signalExceptionForGroupError(env, errno); + signalExceptionForGroupError(env, errno, pid); return; } @@ -254,7 +256,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin #ifdef ENABLE_CPUSETS int err = set_cpuset_policy(t_pid, sp); if (err != NO_ERROR) { - signalExceptionForGroupError(env, -err); + signalExceptionForGroupError(env, -err, t_pid); break; } #endif @@ -266,14 +268,14 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin // set both cpuset and cgroup for general threads err = set_cpuset_policy(t_pid, sp); if (err != NO_ERROR) { - signalExceptionForGroupError(env, -err); + signalExceptionForGroupError(env, -err, t_pid); break; } #endif err = set_sched_policy(t_pid, sp); if (err != NO_ERROR) { - signalExceptionForGroupError(env, -err); + signalExceptionForGroupError(env, -err, t_pid); break; } @@ -285,7 +287,7 @@ jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) { SchedPolicy sp; if (get_sched_policy(pid, &sp) != 0) { - signalExceptionForGroupError(env, errno); + signalExceptionForGroupError(env, errno, pid); } return (int) sp; } @@ -400,7 +402,7 @@ jintArray android_os_Process_getExclusiveCores(JNIEnv* env, jobject clazz) { jintArray cpus; int pid = getpid(); if (get_sched_policy(pid, &sp) != 0) { - signalExceptionForGroupError(env, errno); + signalExceptionForGroupError(env, errno, pid); return NULL; } get_exclusive_cpuset_cores(sp, &cpu_set); @@ -440,6 +442,23 @@ static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, #endif } +jint android_os_Process_getThreadScheduler(JNIEnv* env, jclass clazz, + jint tid) +{ + int policy = 0; +// linux has sched_getscheduler(), others don't. +#if defined(__linux__) + errno = 0; + policy = sched_getscheduler(tid); + if (errno != 0) { + signalExceptionForPriorityError(env, errno, tid); + } +#else + signalExceptionForPriorityError(env, ENOSYS, tid); +#endif + return policy; +} + void android_os_Process_setThreadScheduler(JNIEnv* env, jclass clazz, jint tid, jint policy, jint pri) { @@ -449,10 +468,10 @@ void android_os_Process_setThreadScheduler(JNIEnv* env, jclass clazz, param.sched_priority = pri; int rc = sched_setscheduler(tid, policy, ¶m); if (rc) { - signalExceptionForPriorityError(env, errno); + signalExceptionForPriorityError(env, errno, tid); } #else - signalExceptionForPriorityError(env, ENOSYS); + signalExceptionForPriorityError(env, ENOSYS, tid); #endif } @@ -477,9 +496,9 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, int rc = androidSetThreadPriority(pid, pri); if (rc != 0) { if (rc == INVALID_OPERATION) { - signalExceptionForPriorityError(env, errno); + signalExceptionForPriorityError(env, errno, pid); } else { - signalExceptionForGroupError(env, errno); + signalExceptionForGroupError(env, errno, pid); } } @@ -499,7 +518,7 @@ jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz, errno = 0; jint pri = getpriority(PRIO_PROCESS, pid); if (errno != 0) { - signalExceptionForPriorityError(env, errno); + signalExceptionForPriorityError(env, errno, pid); } //ALOGI("Returning priority of %" PRId32 ": %" PRId32 "\n", pid, pri); return pri; @@ -1191,6 +1210,7 @@ static const JNINativeMethod methods[] = { {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, + {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 4fc546c98e97dee60e9fdb1ec46b6cd92f698752..b0028e1b6f5b4ca813d6256582da87ec72318107 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -573,8 +573,9 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, bounds.roundOut(); } + incStrong(0); auto functor = std::bind( - std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this, + std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this, (jlong) info.canvasContext.getFrameNumber(), (jint) bounds.left, (jint) bounds.top, (jint) bounds.right, (jint) bounds.bottom); @@ -585,15 +586,18 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override { if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return; - if (info) { - auto functor = std::bind( - std::mem_fn(&SurfaceViewPositionUpdater::doNotifyPositionLost), this, - (jlong) info->canvasContext.getFrameNumber()); - - info->canvasContext.enqueueFrameWork(std::move(functor)); - } else { - doNotifyPositionLost(0); + ATRACE_NAME("SurfaceView position lost"); + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + return; } + + env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, + info ? info->canvasContext.getFrameNumber() : 0); + env->DeleteLocalRef(localref); } private: @@ -605,36 +609,23 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, return env; } - void doUpdatePosition(jlong frameNumber, jint left, jint top, + void doUpdatePositionAsync(jlong frameNumber, jint left, jint top, jint right, jint bottom) { ATRACE_NAME("Update SurfaceView position"); JNIEnv* env = jnienv(); jobject localref = env->NewLocalRef(mWeakRef); if (CC_UNLIKELY(!localref)) { - jnienv()->DeleteWeakGlobalRef(mWeakRef); - mWeakRef = nullptr; - return; - } - - env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod, - frameNumber, left, top, right, bottom); - env->DeleteLocalRef(localref); - } - - void doNotifyPositionLost(jlong frameNumber) { - ATRACE_NAME("SurfaceView position lost"); - - JNIEnv* env = jnienv(); - jobject localref = env->NewLocalRef(mWeakRef); - if (CC_UNLIKELY(!localref)) { - jnienv()->DeleteWeakGlobalRef(mWeakRef); + env->DeleteWeakGlobalRef(mWeakRef); mWeakRef = nullptr; - return; + } else { + env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod, + frameNumber, left, top, right, bottom); + env->DeleteLocalRef(localref); } - env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, frameNumber); - env->DeleteLocalRef(localref); + // We need to release ourselves here + decStrong(0); } JavaVM* mVm; diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ff75677536c48a2cfabee8b69196194770e90865..0d8a95c9bcd338f517baf25398fe91dc31d725f7 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +59,6 @@ static struct { jfieldID secure; jfieldID appVsyncOffsetNanos; jfieldID presentationDeadlineNanos; - jfieldID colorTransform; } gPhysicalDisplayInfoClassInfo; static struct { @@ -248,10 +248,10 @@ static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong nativeObject, jfl } } -static void nativeSetPositionAppliesWithResize(JNIEnv* env, jclass clazz, +static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz, jlong nativeObject) { SurfaceControl* const ctrl = reinterpret_cast(nativeObject); - status_t err = ctrl->setPositionAppliesWithResize(); + status_t err = ctrl->setGeometryAppliesWithResize(); if (err < 0 && err != NO_INIT) { doThrowIAE(env); } @@ -429,8 +429,6 @@ static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, info.appVsyncOffset); env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos, info.presentationDeadline); - env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.colorTransform, - info.colorTransform); env->SetObjectArrayElement(configArray, static_cast(c), infoObj); env->DeleteLocalRef(infoObj); } @@ -451,6 +449,43 @@ static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenOb return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; } +static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) { + sp token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return NULL; + Vector colorModes; + if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR || + colorModes.isEmpty()) { + return NULL; + } + + jintArray colorModesArray = env->NewIntArray(colorModes.size()); + if (colorModesArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0); + for (size_t i = 0; i < colorModes.size(); i++) { + colorModesArrayValues[i] = static_cast(colorModes[i]); + } + env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0); + return colorModesArray; +} + +static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) { + sp token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return -1; + return static_cast(SurfaceComposerClient::getActiveColorMode(token)); +} + +static jboolean nativeSetActiveColorMode(JNIEnv* env, jclass, + jobject tokenObj, jint colorMode) { + sp token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return JNI_FALSE; + status_t err = SurfaceComposerClient::setActiveColorMode(token, + static_cast(colorMode)); + return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; +} + static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenObj, jint mode) { sp token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return; @@ -626,6 +661,16 @@ static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { return javaObjectForIBinder(env, ctrl->getHandle()); } +static jboolean nativeGetTransformToDisplayInverse(JNIEnv* env, jclass clazz, jlong nativeObject) { + bool out = false; + auto ctrl = reinterpret_cast(nativeObject); + status_t status = ctrl->getTransformToDisplayInverse(&out); + if (status != NO_ERROR) { + return false; + } + return out; +} + static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) { sp token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return NULL; @@ -667,8 +712,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetLayer }, {"nativeSetPosition", "(JFF)V", (void*)nativeSetPosition }, - {"nativeSetPositionAppliesWithResize", "(J)V", - (void*)nativeSetPositionAppliesWithResize }, + {"nativeSetGeometryAppliesWithResize", "(J)V", + (void*)nativeSetGeometryAppliesWithResize }, {"nativeSetSize", "(JII)V", (void*)nativeSetSize }, {"nativeSetTransparentRegionHint", "(JLandroid/graphics/Region;)V", @@ -705,6 +750,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetActiveConfig }, {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveConfig }, + {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", + (void*)nativeGetDisplayColorModes}, + {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I", + (void*)nativeGetActiveColorMode}, + {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", + (void*)nativeSetActiveColorMode}, {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;", (void*)nativeGetHdrCapabilities }, {"nativeClearContentFrameStats", "(J)Z", @@ -722,7 +773,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeSetOverrideScalingMode", "(JI)V", (void*)nativeSetOverrideScalingMode }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", - (void*)nativeGetHandle } + (void*)nativeGetHandle }, + {"nativeGetTransformToDisplayInverse", "(J)Z", + (void*)nativeGetTransformToDisplayInverse }, }; int register_android_view_SurfaceControl(JNIEnv* env) @@ -745,8 +798,6 @@ int register_android_view_SurfaceControl(JNIEnv* env) clazz, "appVsyncOffsetNanos", "J"); gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = GetFieldIDOrDie(env, clazz, "presentationDeadlineNanos", "J"); - gPhysicalDisplayInfoClassInfo.colorTransform = GetFieldIDOrDie(env, clazz, - "colorTransform", "I"); jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect"); gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I"); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 5b4bfe96392c9903a20eb1c911f3a91f23c74ec5..7cd0d2a3e8884319ecb283c0f2c1c016ceb94c0f 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +124,31 @@ private: std::vector mOnFinishedEvents; }; +class FinishAndInvokeListener : public MessageHandler { +public: + explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) + : mAnimator(anim) { + mListener = anim->getOneShotListener(); + mRequestId = anim->getRequestId(); + } + + virtual void handleMessage(const Message& message) { + if (mAnimator->getRequestId() == mRequestId) { + // Request Id has not changed, meaning there's no animation lifecyle change since the + // message is posted, so go ahead and call finish to make sure the PlayState is properly + // updated. This is needed because before the next frame comes in from UI thread to + // trigger an animation update, there could be reverse/cancel etc. So we need to update + // the playstate in time to ensure all the subsequent events get chained properly. + mAnimator->end(); + } + mListener->onAnimationFinished(nullptr); + } +private: + sp mAnimator; + sp mListener; + uint32_t mRequestId; +}; + class RenderingException : public MessageHandler { public: RenderingException(JavaVM* vm, const std::string& message) @@ -160,6 +187,23 @@ public: virtual void prepareTree(TreeInfo& info) override { info.errorHandler = this; + + for (auto& anim : mRunningVDAnimators) { + // Assume that the property change in VD from the animators will not be consumed. Mark + // otherwise if the VDs are found in the display list tree. For VDs that are not in + // the display list tree, we stop providing animation pulses by 1) removing them from + // the animation list, 2) post a delayed message to end them at end time so their + // listeners can receive the corresponding callbacks. + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + // Mark the VD dirty so it will damage itself during prepareTree. + anim->getVectorDrawable()->markDirty(); + } + if (info.mode == TreeInfo::MODE_FULL) { + for (auto &anim : mPausedVDAnimators) { + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + anim->getVectorDrawable()->markDirty(); + } + } // TODO: This is hacky info.windowInsetLeft = -stagingProperties().getLeft(); info.windowInsetTop = -stagingProperties().getTop(); @@ -175,10 +219,39 @@ public: mLooper->sendMessage(handler, 0); } + void sendMessageDelayed(const sp& handler, nsecs_t delayInMs) { + mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0); + } + void attachAnimatingNode(RenderNode* animatingNode) { mPendingAnimatingRenderNodes.push_back(animatingNode); } + void attachPendingVectorDrawableAnimators() { + mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(), + mPendingVectorDrawableAnimators.end()); + mPendingVectorDrawableAnimators.clear(); + } + + void detachAnimators() { + // Remove animators from the list and post a delayed message in future to end the animator + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + } + mRunningVDAnimators.clear(); + mPausedVDAnimators.clear(); + } + + // Move all the animators to the paused list, and send a delayed message to notify the finished + // listener. + void pauseAnimators() { + mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end()); + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + } + mRunningVDAnimators.clear(); + } + void doAttachAnimatingNodes(AnimationContext* context) { for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { RenderNode* node = mPendingAnimatingRenderNodes[i].get(); @@ -187,17 +260,144 @@ public: mPendingAnimatingRenderNodes.clear(); } + // Run VectorDrawable animators after prepareTree. + void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) { + // Push staging. + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingVectorDrawableAnimators(context); + } + + // Run the animators in the running list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if ((*it)->animate(*context)) { + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Run the animators in paused list during full sync. + if (info.mode == TreeInfo::MODE_FULL) { + // During full sync we also need to pulse paused animators, in case their targets + // have been added back to the display list. All the animators that passed the + // scheduled finish time will be removed from the paused list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->animate(*context)) { + // Animator has finished, remove from the list. + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + } + + // Move the animators with a target not in DisplayList to paused list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + // Vector Drawable is not in the display list, we should remove this animator from + // the list, put it in the paused list, and post a delayed message to end the + // animator. + detachVectorDrawableAnimator(it->get()); + mPausedVDAnimators.insert(*it); + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Move the animators with a target in DisplayList from paused list to running list, and + // trim paused list. + if (info.mode == TreeInfo::MODE_FULL) { + // Check whether any paused animator's target is back in Display List. If so, put the + // animator back in the running list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + mRunningVDAnimators.insert(*it); + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + // Trim paused VD animators at full sync, so that when Java loses reference to an + // animator, we know we won't be requested to animate it any more, then we remove such + // animators from the paused list so they can be properly freed. We also remove the + // animators from paused list when the time elapsed since start has exceeded duration. + trimPausedVDAnimators(context); + } + + info.out.hasAnimations |= !mRunningVDAnimators.empty(); + } + + void trimPausedVDAnimators(AnimationContext* context) { + // Trim paused vector drawable animator list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + // Remove paused VD animator if no one else is referencing it. Note that animators that + // have passed scheduled finish time are removed from list when they are being pulsed + // before prepare tree. + // TODO: this is a bit hacky, need to figure out a better way to track when the paused + // animators should be freed. + if ((*it)->getStrongCount() == 1) { + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + } + + void pushStagingVectorDrawableAnimators(AnimationContext* context) { + for (auto& anim : mRunningVDAnimators) { + anim->pushStaging(*context); + } + } + void destroy() { for (auto& renderNode : mPendingAnimatingRenderNodes) { renderNode->animators().endAllStagingAnimators(); } mPendingAnimatingRenderNodes.clear(); + mPendingVectorDrawableAnimators.clear(); + } + + void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + mPendingVectorDrawableAnimators.insert(anim); } private: sp mLooper; JavaVM* mVm; std::vector< sp > mPendingAnimatingRenderNodes; + std::set< sp > mPendingVectorDrawableAnimators; + std::set< sp > mRunningVDAnimators; + // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but + // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when + // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators + // need to be re-activated once their VD target is added back into DisplayList. Since that could + // only happen when we do a full sync, we need to make sure to pulse these paused animators at + // full sync. If any animator's VD target is found in DisplayList during a full sync, we move + // the animator back to the running list. + std::set< sp > mPausedVDAnimators; + void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + if (anim->isInfinite() || !anim->isRunning()) { + // Do not need to post anything if the animation is infinite (i.e. no meaningful + // end listener action), or if the animation has already ended. + return; + } + nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); + // Post a delayed onFinished event that is scheduled to be handled when the animator ends. + if (anim->getOneShotListener()) { + // VectorDrawable's oneshot listener is updated when there are user triggered animation + // lifecycle changes, such as start(), end(), etc. By using checking and clearing + // one shot listener, we ensure the same end listener event gets posted only once. + // Therefore no duplicates. Another benefit of using one shot listener is that no + // removal is necessary: the end time of animation will not change unless triggered by + // user events, in which case the already posted listener's id will become stale, and + // the onFinished callback will then be ignored. + sp message + = new FinishAndInvokeListener(anim); + sendMessageDelayed(message, remainingTimeInMs); + anim->clearOneShotListener(); + } + } }; class AnimationContextBridge : public AnimationContext { @@ -213,6 +413,7 @@ public: virtual void startFrame(TreeInfo::TraversalMode mode) { if (mode == TreeInfo::MODE_FULL) { mRootNode->doAttachAnimatingNodes(this); + mRootNode->attachPendingVectorDrawableAnimators(); } AnimationContext::startFrame(mode); } @@ -220,9 +421,14 @@ public: // Runs any animations still left in mCurrentFrameAnimations virtual void runRemainingAnimations(TreeInfo& info) { AnimationContext::runRemainingAnimations(info); + mRootNode->runVectorDrawableAnimators(this, info); postOnFinishedEvents(); } + virtual void pauseAnimators() override { + mRootNode->pauseAnimators(); + } + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { OnFinishedEvent event(animator, listener); mOnFinishedEvents.push_back(event); @@ -230,6 +436,7 @@ public: virtual void destroy() { AnimationContext::destroy(); + mRootNode->detachAnimators(); postOnFinishedEvents(); } @@ -416,6 +623,12 @@ static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, job proxy->setProcessStatsBuffer(fd); } +static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + return proxy->getRenderThreadTid(); +} + static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { RootRenderNode* node = new RootRenderNode(env); node->incStrong(0); @@ -528,6 +741,13 @@ static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* en rootRenderNode->attachAnimatingNode(animatingNode); } +static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatorPtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootNodePtr); + PropertyValuesAnimatorSet* animator = reinterpret_cast(animatorPtr); + rootRenderNode->addVectorDrawableAnimator(animator); +} + static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, jlong functorPtr, jboolean waitForCompletion) { Functor* functor = reinterpret_cast(functorPtr); @@ -724,6 +944,7 @@ const char* const kClassPathName = "android/view/ThreadedRenderer"; static const JNINativeMethod gMethods[] = { { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas }, { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, + { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, @@ -739,6 +960,7 @@ static const JNINativeMethod gMethods[] = { { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, + { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 3f4b2a61321b86398a63ceef1332780871797bf6..a04fc2a70686a3cfa89cfe8104a3bca83f34273d 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -176,7 +176,9 @@ static void SetGids(JNIEnv* env, jintArray javaGids) { } int rc = setgroups(gids.size(), reinterpret_cast(&gids[0])); if (rc == -1) { - RuntimeAbort(env, __LINE__, "setgroups failed"); + std::ostringstream oss; + oss << "setgroups failed: " << strerror(errno) << ", gids.size=" << gids.size(); + RuntimeAbort(env, __LINE__, oss.str().c_str()); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b14321e7c70e042b52370c494139902305f9a825..b624305e519bc8f7161e65acf6481de3ac13b7a4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -50,6 +50,7 @@ + @@ -195,6 +196,7 @@ + @@ -488,12 +490,6 @@ - - - - - - @@ -505,6 +501,8 @@ + + @@ -1286,6 +1284,11 @@ + + + - - + + + + + + diff --git a/core/res/res/anim/watch_switch_thumb_to_on_animation.xml b/core/res/res/anim/watch_switch_thumb_to_on_animation.xml new file mode 100644 index 0000000000000000000000000000000000000000..e6442177660504f86cc13b8de2d705f3e6fdcc31 --- /dev/null +++ b/core/res/res/anim/watch_switch_thumb_to_on_animation.xml @@ -0,0 +1,37 @@ + + + + + + + + diff --git a/core/res/res/color/watch_switch_thumb_color_material.xml b/core/res/res/color/watch_switch_thumb_color_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4796a0326316fb62e2eaf8e76cb41fbbd815134 --- /dev/null +++ b/core/res/res/color/watch_switch_thumb_color_material.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/night_mode_settings.xml b/core/res/res/drawable-watch/dialog_background_material.xml similarity index 72% rename from packages/SystemUI/res/layout/night_mode_settings.xml rename to core/res/res/drawable-watch/dialog_background_material.xml index 3725e78cc481e369cef3bb287c600ac8778187ad..de52f08c00bf47968ba2c784bf1bb16543000f3c 100644 --- a/packages/SystemUI/res/layout/night_mode_settings.xml +++ b/core/res/res/drawable-watch/dialog_background_material.xml @@ -14,11 +14,6 @@ limitations under the License. --> - - - - - + + + diff --git a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml b/core/res/res/drawable/ic_doc_apk.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_apk.xml rename to core/res/res/drawable/ic_doc_apk.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml b/core/res/res/drawable/ic_doc_audio.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_audio.xml rename to core/res/res/drawable/ic_doc_audio.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml b/core/res/res/drawable/ic_doc_certificate.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_certificate.xml rename to core/res/res/drawable/ic_doc_certificate.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml b/core/res/res/drawable/ic_doc_codes.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_codes.xml rename to core/res/res/drawable/ic_doc_codes.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml b/core/res/res/drawable/ic_doc_compressed.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_compressed.xml rename to core/res/res/drawable/ic_doc_compressed.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml b/core/res/res/drawable/ic_doc_contact.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_contact.xml rename to core/res/res/drawable/ic_doc_contact.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_document.xml b/core/res/res/drawable/ic_doc_document.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_document.xml rename to core/res/res/drawable/ic_doc_document.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_event.xml b/core/res/res/drawable/ic_doc_event.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_event.xml rename to core/res/res/drawable/ic_doc_event.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml b/core/res/res/drawable/ic_doc_excel.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_excel.xml rename to core/res/res/drawable/ic_doc_excel.xml diff --git a/core/res/res/drawable/ic_doc_folder.xml b/core/res/res/drawable/ic_doc_folder.xml new file mode 100644 index 0000000000000000000000000000000000000000..dcbce010810edef6e7197a2a662ea31f4f0cc409 --- /dev/null +++ b/core/res/res/drawable/ic_doc_folder.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/DocumentsUI/res/drawable/ic_doc_font.xml b/core/res/res/drawable/ic_doc_font.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_font.xml rename to core/res/res/drawable/ic_doc_font.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml b/core/res/res/drawable/ic_doc_generic.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_generic.xml rename to core/res/res/drawable/ic_doc_generic.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_image.xml b/core/res/res/drawable/ic_doc_image.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_image.xml rename to core/res/res/drawable/ic_doc_image.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml b/core/res/res/drawable/ic_doc_pdf.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_pdf.xml rename to core/res/res/drawable/ic_doc_pdf.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml b/core/res/res/drawable/ic_doc_powerpoint.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml rename to core/res/res/drawable/ic_doc_powerpoint.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml b/core/res/res/drawable/ic_doc_presentation.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_presentation.xml rename to core/res/res/drawable/ic_doc_presentation.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml b/core/res/res/drawable/ic_doc_spreadsheet.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml rename to core/res/res/drawable/ic_doc_spreadsheet.xml diff --git a/core/res/res/drawable/ic_doc_text.xml b/core/res/res/drawable/ic_doc_text.xml new file mode 100644 index 0000000000000000000000000000000000000000..7fc04e811b1cce2dce65a4fb69af194ae301c568 --- /dev/null +++ b/core/res/res/drawable/ic_doc_text.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/DocumentsUI/res/drawable/ic_doc_video.xml b/core/res/res/drawable/ic_doc_video.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_video.xml rename to core/res/res/drawable/ic_doc_video.xml diff --git a/packages/DocumentsUI/res/drawable/ic_doc_word.xml b/core/res/res/drawable/ic_doc_word.xml similarity index 100% rename from packages/DocumentsUI/res/drawable/ic_doc_word.xml rename to core/res/res/drawable/ic_doc_word.xml diff --git a/core/res/res/drawable/ic_lock_power_off.xml b/core/res/res/drawable/ic_lock_power_off.xml index 718f17ed0c19637383bf527e85c9e494bf90a9ec..babd1be220cb086b3551742e079fcab393245351 100644 --- a/core/res/res/drawable/ic_lock_power_off.xml +++ b/core/res/res/drawable/ic_lock_power_off.xml @@ -1,19 +1,25 @@ - - - - + + + diff --git a/packages/SystemUI/res/drawable/ic_night_mode.xml b/core/res/res/drawable/ic_restart.xml similarity index 61% rename from packages/SystemUI/res/drawable/ic_night_mode.xml rename to core/res/res/drawable/ic_restart.xml index caa7a47ee5b463aa4e074b4a3dd7ae035487a350..47ac483fa3115722f6eae1f9aa9196ffa7cac134 100644 --- a/packages/SystemUI/res/drawable/ic_night_mode.xml +++ b/core/res/res/drawable/ic_restart.xml @@ -16,9 +16,13 @@ + android:tint="?attr/colorControlNormal"> + android:fillColor="#FF000000" + android:pathData="M12.0,4.0L12.0,1.0L8.0,5.0l4.0,4.0L12.0,6.0c3.9,0.0 7.0,3.1 7.0,7.0c0.0,3.9 -3.1,7.0 -7.0,7.0l0.0,2.0c5.0,0.0 9.0,-4.0 9.0,-9.0C21.0,8.0 17.0,4.0 12.0,4.0z"/> + diff --git a/core/res/res/drawable/watch_switch_thumb_material.xml b/core/res/res/drawable/watch_switch_thumb_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..3463a4f546846daade85227b754b6395deccf41f --- /dev/null +++ b/core/res/res/drawable/watch_switch_thumb_material.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/core/res/res/drawable/watch_switch_thumb_material_anim.xml b/core/res/res/drawable/watch_switch_thumb_material_anim.xml new file mode 100644 index 0000000000000000000000000000000000000000..686fb976cf1c18c057b7377d0b87c8effd12b409 --- /dev/null +++ b/core/res/res/drawable/watch_switch_thumb_material_anim.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml b/core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c6ba2f7575345dc7c781976f2bff2c2e026c79c --- /dev/null +++ b/core/res/res/drawable/watch_switch_thumb_to_off_anim_mtrl.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml b/core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f92361e70ea631821a28847edf5339914850795 --- /dev/null +++ b/core/res/res/drawable/watch_switch_thumb_to_on_anim_mtrl.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/watch_switch_track_material.xml b/core/res/res/drawable/watch_switch_track_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..00cdadb1b59db482ca2339847f464124ae180a17 --- /dev/null +++ b/core/res/res/drawable/watch_switch_track_material.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml b/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc840d9fa73fa1890d4516a1f28a880c881f211a --- /dev/null +++ b/core/res/res/layout-notround-watch/alert_dialog_header_micro.xml @@ -0,0 +1,41 @@ + + + + + + diff --git a/core/res/res/layout-round-watch/alert_dialog_header_micro.xml b/core/res/res/layout-round-watch/alert_dialog_header_micro.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f7ae02388a34d3162cdf8ff784df72e9466e592 --- /dev/null +++ b/core/res/res/layout-round-watch/alert_dialog_header_micro.xml @@ -0,0 +1,38 @@ + + + + + + diff --git a/core/res/res/layout-watch/alert_dialog_material.xml b/core/res/res/layout-watch/alert_dialog_material.xml new file mode 100644 index 0000000000000000000000000000000000000000..ce8e20a1218790c139177ef419cf90a308fdd7cc --- /dev/null +++ b/core/res/res/layout-watch/alert_dialog_material.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + +