summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk7
-rw-r--r--CleanSpec.mk1
-rw-r--r--api/current.txt305
-rw-r--r--api/removed.txt15
-rw-r--r--api/system-current.txt305
-rw-r--r--api/system-removed.txt15
-rw-r--r--cmds/bootanimation/BootAnimation.cpp5
-rw-r--r--core/java/android/animation/LayoutTransition.java67
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/Application.java6
-rw-r--r--core/java/android/app/Dialog.java2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java3
-rw-r--r--core/java/android/content/Intent.java71
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java3
-rw-r--r--core/java/android/provider/Settings.java24
-rw-r--r--core/java/android/service/dreams/DreamService.java2
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java75
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/util/FloatMath.java10
-rw-r--r--core/java/android/view/GhostView.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java28
-rw-r--r--core/java/android/widget/MediaController.java2
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java233
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java289
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java14
-rw-r--r--core/java/com/android/internal/policy/PhoneFallbackEventHandler.java (renamed from core/java/android/view/PhoneFallbackEventHandler.java)7
-rw-r--r--core/java/com/android/internal/policy/PhoneLayoutInflater.java (renamed from core/java/android/view/PhoneLayoutInflater.java)4
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java (renamed from core/java/android/view/PhoneWindow.java)41
-rw-r--r--core/java/com/android/internal/transition/EpicenterClipReveal.java233
-rw-r--r--core/java/com/android/internal/transition/EpicenterTranslate.java191
-rw-r--r--core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java334
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java8
-rw-r--r--core/java/com/android/internal/util/CallbackRegistry.java395
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl3
-rw-r--r--core/jni/Android.mk1
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp53
-rw-r--r--core/jni/android/graphics/Bitmap.h4
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp4
-rw-r--r--core/jni/android/graphics/Graphics.cpp4
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h2
-rw-r--r--core/jni/android_media_AudioRecord.cpp92
-rw-r--r--core/jni/android_media_AudioSystem.cpp5
-rw-r--r--core/jni/android_media_AudioTrack.cpp50
-rw-r--r--core/jni/android_media_DeviceCallback.cpp82
-rw-r--r--core/jni/android_media_DeviceCallback.h47
-rw-r--r--core/jni/android_view_SurfaceControl.cpp2
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/layout-land/time_picker_material.xml29
-rw-r--r--core/res/res/layout/date_picker_header_material.xml16
-rw-r--r--core/res/res/layout/time_picker_header_material.xml29
-rw-r--r--core/res/res/transition/popup_window_enter.xml15
-rw-r--r--core/res/res/values/attrs.xml9
-rw-r--r--core/tests/coretests/src/android/util/FloatMathTest.java55
-rw-r--r--core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java305
-rw-r--r--docs/html/jd_collections.js8
-rw-r--r--docs/html/jd_tag_helpers.js3
-rw-r--r--docs/html/preview/api-overview.jd2
-rw-r--r--docs/html/preview/index.jd60
-rw-r--r--docs/html/preview/overview.jd7
-rw-r--r--docs/html/preview/preview_toc.cs10
-rw-r--r--docs/html/preview/reference.jd2
-rw-r--r--keystore/java/android/security/AndroidKeyPairGenerator.java21
-rw-r--r--keystore/java/android/security/AndroidKeyStore.java31
-rw-r--r--keystore/java/android/security/KeyChain.java17
-rw-r--r--keystore/java/android/security/KeyGeneratorSpec.java31
-rw-r--r--keystore/java/android/security/KeyPairGeneratorSpec.java57
-rw-r--r--keystore/java/android/security/KeyStore.java6
-rw-r--r--keystore/java/android/security/KeyStoreCipherSpi.java10
-rw-r--r--keystore/java/android/security/KeyStoreCryptoOperationUtils.java28
-rw-r--r--keystore/java/android/security/KeyStoreKeyGeneratorSpi.java23
-rw-r--r--keystore/java/android/security/KeyStoreKeyProperties.java520
-rw-r--r--keystore/java/android/security/KeyStoreKeySpec.java30
-rw-r--r--keystore/java/android/security/KeyStoreParameter.java52
-rw-r--r--keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java14
-rw-r--r--keystore/java/android/security/KeymasterUtils.java281
-rw-r--r--libs/hwui/tests/main.cpp93
-rw-r--r--media/java/android/media/AudioDevicePort.java10
-rw-r--r--media/java/android/media/AudioDevicesManager.java17
-rw-r--r--media/java/android/media/AudioManager.java12
-rw-r--r--media/java/android/media/AudioMixPort.java19
-rw-r--r--media/java/android/media/AudioPort.java8
-rw-r--r--media/java/android/media/AudioRecord.java58
-rw-r--r--media/java/android/media/AudioSystem.java6
-rw-r--r--media/java/android/media/AudioTrack.java53
-rw-r--r--media/java/android/media/routing/IMediaRouteClientCallback.aidl41
-rw-r--r--media/java/android/media/routing/IMediaRouteService.aidl46
-rw-r--r--media/java/android/media/routing/IMediaRouter.aidl22
-rw-r--r--media/java/android/media/routing/IMediaRouterDelegate.aidl22
-rw-r--r--media/java/android/media/routing/IMediaRouterRoutingCallback.aidl22
-rw-r--r--media/java/android/media/routing/IMediaRouterStateCallback.aidl22
-rw-r--r--media/java/android/media/routing/MediaRouteSelector.aidl18
-rw-r--r--media/java/android/media/routing/MediaRouteSelector.java356
-rw-r--r--media/java/android/media/routing/MediaRouteService.java1023
-rw-r--r--media/java/android/media/routing/MediaRouter.java1886
-rw-r--r--media/java/android/media/routing/ParcelableConnectionInfo.aidl18
-rw-r--r--media/java/android/media/routing/ParcelableConnectionInfo.java71
-rw-r--r--media/java/android/media/routing/ParcelableDestinationInfo.aidl18
-rw-r--r--media/java/android/media/routing/ParcelableDestinationInfo.java65
-rw-r--r--media/java/android/media/routing/ParcelableRouteInfo.aidl18
-rw-r--r--media/java/android/media/routing/ParcelableRouteInfo.java64
-rw-r--r--media/java/android/media/session/ISession.aidl2
-rw-r--r--media/java/android/media/session/ISessionController.aidl4
-rw-r--r--media/java/android/media/session/MediaController.java12
-rw-r--r--media/java/android/media/session/MediaSession.java18
-rw-r--r--media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java2
-rw-r--r--native/graphics/jni/bitmap.cpp12
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java3
-rw-r--r--packages/SettingsLib/res/values/strings.xml6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java32
-rw-r--r--packages/SystemUI/res/layout/zen_mode_panel.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/EventLogConstants.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java13
-rw-r--r--rs/java/android/renderscript/Script.java4
-rw-r--r--rs/jni/android_renderscript_RenderScript.cpp4
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java71
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java29
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java34
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java6
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java138
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java3
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java15
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/power/Notifier.java4
-rw-r--r--telecomm/java/android/telecom/DefaultDialerManager.java41
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java6
-rw-r--r--tests/OneMedia/Android.mk3
-rw-r--r--tests/OneMedia/AndroidManifest.xml9
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerSession.java83
-rw-r--r--tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java47
-rw-r--r--tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java270
-rw-r--r--tools/layoutlib/bridge/src/android/view/BridgeInflater.java22
-rw-r--r--tools/layoutlib/bridge/src/android/view/RectShadowPainter.java15
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java18
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java16
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java123
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java19
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java18
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java8
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.classbin1157 -> 1157 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.classbin488 -> 1978 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.classbin485 -> 519 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.classbin452 -> 452 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.classbin538 -> 538 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.classbin461 -> 461 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.classbin897 -> 897 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.pngbin0 -> 11038 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml393
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml47
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java41
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java6
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java6
162 files changed, 4199 insertions, 6199 deletions
diff --git a/Android.mk b/Android.mk
index b71f08c..27dedd9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -348,12 +348,6 @@ LOCAL_SRC_FILES += \
media/java/android/media/projection/IMediaProjectionCallback.aidl \
media/java/android/media/projection/IMediaProjectionManager.aidl \
media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \
- media/java/android/media/routing/IMediaRouteService.aidl \
- media/java/android/media/routing/IMediaRouteClientCallback.aidl \
- media/java/android/media/routing/IMediaRouter.aidl \
- media/java/android/media/routing/IMediaRouterDelegate.aidl \
- media/java/android/media/routing/IMediaRouterRoutingCallback.aidl \
- media/java/android/media/routing/IMediaRouterStateCallback.aidl \
media/java/android/media/session/IActiveSessionsListener.aidl \
media/java/android/media/session/ISessionController.aidl \
media/java/android/media/session/ISessionControllerCallback.aidl \
@@ -460,7 +454,6 @@ aidl_files := \
frameworks/base/location/java/android/location/Criteria.aidl \
frameworks/base/media/java/android/media/MediaMetadata.aidl \
frameworks/base/media/java/android/media/MediaDescription.aidl \
- frameworks/base/media/java/android/media/routing/MediaRouteSelector.aidl \
frameworks/base/media/java/android/media/Rating.aidl \
frameworks/base/media/java/android/media/AudioAttributes.aidl \
frameworks/base/media/java/android/media/AudioFocusInfo.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c812b6a..667ed02 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -233,6 +233,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libinputflingerhost.so $
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libinputflingerhost.so $(PRODUCT_OUT)/symbols/system/lib64/libinputflingerhost.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libinputflingerhost.so $(PRODUCT_OUT)/obj_arm/lib/libinputflingerhost.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinputflingerhost_intermediates $(PRODUCT_OUT)/obj_arm/SHARED_LIBRARIES/libinputflingerhost_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 375bee7..cdc2404 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28,7 +28,6 @@ package android {
field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
- field public static final java.lang.String BIND_MEDIA_ROUTE_SERVICE = "android.permission.BIND_MEDIA_ROUTE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -8282,6 +8281,7 @@ package android.content {
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
+ field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT";
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
@@ -8293,6 +8293,7 @@ package android.content {
field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list";
field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+ field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
@@ -8325,6 +8326,7 @@ package android.content {
field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+ field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -17424,248 +17426,11 @@ package android.media.projection {
}
-package android.media.routing {
-
- public final class MediaRouteSelector implements android.os.Parcelable {
- method public boolean containsProtocol(java.lang.Class<?>);
- method public boolean containsProtocol(java.lang.String);
- method public int describeContents();
- method public android.os.Bundle getExtras();
- method public int getOptionalFeatures();
- method public java.util.List<java.lang.String> getOptionalProtocols();
- method public int getRequiredFeatures();
- method public java.util.List<java.lang.String> getRequiredProtocols();
- method public java.lang.String getServicePackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.routing.MediaRouteSelector> CREATOR;
- }
-
- public static final class MediaRouteSelector.Builder {
- ctor public MediaRouteSelector.Builder();
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector build();
- method public android.media.routing.MediaRouteSelector.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouteSelector.Builder setOptionalFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setRequiredFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setServicePackageName(java.lang.String);
- }
-
- public abstract class MediaRouteService extends android.app.Service {
- ctor public MediaRouteService();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.routing.MediaRouteService.ClientSession onCreateClientSession(android.media.routing.MediaRouteService.ClientInfo);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.routing.MediaRouteService";
- }
-
- public static final class MediaRouteService.ClientInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- }
-
- public static abstract class MediaRouteService.ClientSession {
- ctor public MediaRouteService.ClientSession();
- method public abstract boolean onConnect(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouteService.ConnectionCallback);
- method public abstract void onDisconnect();
- method public void onPauseStream();
- method public void onRelease();
- method public void onResumeStream();
- method public abstract boolean onStartDiscovery(android.media.routing.MediaRouter.DiscoveryRequest, android.media.routing.MediaRouteService.DiscoveryCallback);
- method public abstract void onStopDiscovery();
- }
-
- public final class MediaRouteService.ConnectionCallback {
- method public void onConnected(android.media.routing.MediaRouter.ConnectionInfo);
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDisconnected();
- }
-
- public final class MediaRouteService.DiscoveryCallback {
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- }
-
- public final class MediaRouter {
- ctor public MediaRouter(android.content.Context);
- method public void addSelector(android.media.routing.MediaRouteSelector);
- method public void clearSelectors();
- method public android.media.routing.MediaRouter.Delegate createDelegate();
- method public android.media.routing.MediaRouter.ConnectionInfo getConnection();
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public java.util.List<android.media.routing.MediaRouter.RouteInfo> getDiscoveredRoutes(android.media.routing.MediaRouter.DestinationInfo);
- method public int getDiscoveryState();
- method public android.media.AudioAttributes getPreferredAudioAttributes();
- method public android.view.Display getPreferredPresentationDisplay();
- method public android.media.VolumeProvider getPreferredVolumeProvider();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public android.media.routing.MediaRouter.RouteInfo getSelectedRoute();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public boolean isReleased();
- method public void pauseStream();
- method public void release();
- method public void removeSelector(android.media.routing.MediaRouteSelector);
- method public void resumeStream();
- method public void setRoutingCallback(android.media.routing.MediaRouter.RoutingCallback, android.os.Handler);
- field public static final int CONNECTION_ERROR_ABORTED = 1; // 0x1
- field public static final int CONNECTION_ERROR_BARGED = 7; // 0x7
- field public static final int CONNECTION_ERROR_BROKEN = 6; // 0x6
- field public static final int CONNECTION_ERROR_BUSY = 4; // 0x4
- field public static final int CONNECTION_ERROR_TIMEOUT = 5; // 0x5
- field public static final int CONNECTION_ERROR_UNAUTHORIZED = 2; // 0x2
- field public static final int CONNECTION_ERROR_UNKNOWN = 0; // 0x0
- field public static final int CONNECTION_ERROR_UNREACHABLE = 3; // 0x3
- field public static final int CONNECTION_FLAG_BARGE = 1; // 0x1
- field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
- field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
- field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0; // 0x0
- field public static final int DISCONNECTION_REASON_ERROR = 2; // 0x2
- field public static final int DISCONNECTION_REASON_USER_REQUEST = 1; // 0x1
- field public static final int DISCOVERY_ERROR_ABORTED = 1; // 0x1
- field public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2; // 0x2
- field public static final int DISCOVERY_ERROR_UNKNOWN = 0; // 0x0
- field public static final int DISCOVERY_FLAG_BACKGROUND = 1; // 0x1
- field public static final int DISCOVERY_STATE_STARTED = 1; // 0x1
- field public static final int DISCOVERY_STATE_STOPPED = 0; // 0x0
- field public static final int ROUTE_FEATURE_LIVE_AUDIO = 1; // 0x1
- field public static final int ROUTE_FEATURE_LIVE_VIDEO = 2; // 0x2
- }
-
- public static final class MediaRouter.ConnectionInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public android.view.Display getPresentationDisplay();
- method public android.os.IBinder getProtocolBinder(java.lang.String);
- method public android.os.IBinder getProtocolBinder(int);
- method public T getProtocolObject(java.lang.Class<T>);
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public android.media.VolumeProvider getVolumeProvider();
- }
-
- public static final class MediaRouter.ConnectionInfo.Builder {
- ctor public MediaRouter.ConnectionInfo.Builder(android.media.routing.MediaRouter.RouteInfo);
- method public android.media.routing.MediaRouter.ConnectionInfo build();
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setAudioAttributes(android.media.AudioAttributes);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setPresentationDisplay(android.view.Display);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolBinder(java.lang.String, android.os.IBinder);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolStub(java.lang.Class<?>, android.os.IInterface);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setVolumeProvider(android.media.VolumeProvider);
- }
-
- public static final class MediaRouter.ConnectionRequest {
- method public android.os.Bundle getExtras();
- method public int getFlags();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public void setExtras(android.os.Bundle);
- method public void setFlags(int);
- method public void setRoute(android.media.routing.MediaRouter.RouteInfo);
- }
-
- public static final class MediaRouter.Delegate {
- ctor public MediaRouter.Delegate();
- method public void addStateCallback(android.media.routing.MediaRouter.StateCallback, android.os.Handler);
- method public void connect(android.media.routing.MediaRouter.DestinationInfo, int);
- method public void disconnect(int);
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public int getDiscoveryState();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public boolean isReleased();
- method public void removeStateCallback(android.media.routing.MediaRouter.StateCallback);
- method public void startDiscovery(int);
- method public void stopDiscovery();
- }
-
- public static final class MediaRouter.DestinationInfo {
- method public java.lang.CharSequence getDescription();
- method public android.os.Bundle getExtras();
- method public int getIconResourceId();
- method public java.lang.String getId();
- method public java.lang.CharSequence getName();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
- }
-
- public static final class MediaRouter.DestinationInfo.Builder {
- ctor public MediaRouter.DestinationInfo.Builder(java.lang.String, android.media.routing.MediaRouter.ServiceMetadata, java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo build();
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setDescription(java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setIconResourceId(int);
- }
-
- public static final class MediaRouter.DiscoveryRequest {
- method public int getFlags();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public void setFlags(int);
- method public void setSelectors(java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.RouteInfo {
- method public android.media.routing.MediaRouter.DestinationInfo getDestination();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public java.lang.String getId();
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouteSelector getSelector();
- }
-
- public static final class MediaRouter.RouteInfo.Builder {
- ctor public MediaRouter.RouteInfo.Builder(java.lang.String, android.media.routing.MediaRouter.DestinationInfo, android.media.routing.MediaRouteSelector);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.Class<T>);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.String);
- method public android.media.routing.MediaRouter.RouteInfo build();
- method public android.media.routing.MediaRouter.RouteInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.RouteInfo.Builder setFeatures(int);
- }
-
- public static abstract class MediaRouter.RoutingCallback extends android.media.routing.MediaRouter.StateCallback {
- ctor public MediaRouter.RoutingCallback();
- method public boolean onPrepareConnectionRequest(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public boolean onPrepareDiscoveryRequest(android.media.routing.MediaRouter.DiscoveryRequest, java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.ServiceMetadata {
- method public android.content.ComponentName getComponentName();
- method public android.graphics.drawable.Drawable getIcon(android.content.pm.PackageManager);
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
- method public java.lang.String getPackageName();
- method public android.content.pm.ServiceInfo getService();
- }
-
- public static abstract class MediaRouter.StateCallback {
- ctor public MediaRouter.StateCallback();
- method public void onConnected();
- method public void onConnecting();
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onConnectionStateChanged(int);
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDisconnected();
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDiscoveryStarted();
- method public void onDiscoveryStateChanged(int);
- method public void onDiscoveryStopped();
- method public void onReleased();
- method public void onSelectedDestinationChanged(android.media.routing.MediaRouter.DestinationInfo);
- }
-
-}
-
package android.media.session {
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void adjustVolume(int, int);
- method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
method public long getFlags();
@@ -17739,7 +17504,6 @@ package android.media.session {
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
method public void setMediaButtonReceiver(android.app.PendingIntent);
- method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -28742,6 +28506,53 @@ package android.security {
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -28761,6 +28572,14 @@ package android.security {
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
@@ -34187,14 +34006,6 @@ package android.util {
}
public deprecated class FloatMath {
- method public static float ceil(float);
- method public static float cos(float);
- method public static float exp(float);
- method public static float floor(float);
- method public static float hypot(float, float);
- method public static float pow(float, float);
- method public static float sin(float);
- method public static float sqrt(float);
}
public final class JsonReader implements java.io.Closeable {
diff --git a/api/removed.txt b/api/removed.txt
index 5e77e15..0046a70 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -108,6 +108,21 @@ package android.text.format {
}
+package android.util {
+
+ public deprecated class FloatMath {
+ method public static float ceil(float);
+ method public static float cos(float);
+ method public static float exp(float);
+ method public static float floor(float);
+ method public static float hypot(float, float);
+ method public static float pow(float, float);
+ method public static float sin(float);
+ method public static float sqrt(float);
+ }
+
+}
+
package android.view {
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/api/system-current.txt b/api/system-current.txt
index 63ba05f..7012773 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39,7 +39,6 @@ package android {
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
- field public static final java.lang.String BIND_MEDIA_ROUTE_SERVICE = "android.permission.BIND_MEDIA_ROUTE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -8508,6 +8507,7 @@ package android.content {
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
+ field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT";
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
@@ -8519,6 +8519,7 @@ package android.content {
field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list";
field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
+ field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
@@ -8554,6 +8555,7 @@ package android.content {
field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+ field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -18717,248 +18719,11 @@ package android.media.projection {
}
-package android.media.routing {
-
- public final class MediaRouteSelector implements android.os.Parcelable {
- method public boolean containsProtocol(java.lang.Class<?>);
- method public boolean containsProtocol(java.lang.String);
- method public int describeContents();
- method public android.os.Bundle getExtras();
- method public int getOptionalFeatures();
- method public java.util.List<java.lang.String> getOptionalProtocols();
- method public int getRequiredFeatures();
- method public java.util.List<java.lang.String> getRequiredProtocols();
- method public java.lang.String getServicePackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.routing.MediaRouteSelector> CREATOR;
- }
-
- public static final class MediaRouteSelector.Builder {
- ctor public MediaRouteSelector.Builder();
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector build();
- method public android.media.routing.MediaRouteSelector.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouteSelector.Builder setOptionalFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setRequiredFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setServicePackageName(java.lang.String);
- }
-
- public abstract class MediaRouteService extends android.app.Service {
- ctor public MediaRouteService();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.routing.MediaRouteService.ClientSession onCreateClientSession(android.media.routing.MediaRouteService.ClientInfo);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.routing.MediaRouteService";
- }
-
- public static final class MediaRouteService.ClientInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- }
-
- public static abstract class MediaRouteService.ClientSession {
- ctor public MediaRouteService.ClientSession();
- method public abstract boolean onConnect(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouteService.ConnectionCallback);
- method public abstract void onDisconnect();
- method public void onPauseStream();
- method public void onRelease();
- method public void onResumeStream();
- method public abstract boolean onStartDiscovery(android.media.routing.MediaRouter.DiscoveryRequest, android.media.routing.MediaRouteService.DiscoveryCallback);
- method public abstract void onStopDiscovery();
- }
-
- public final class MediaRouteService.ConnectionCallback {
- method public void onConnected(android.media.routing.MediaRouter.ConnectionInfo);
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDisconnected();
- }
-
- public final class MediaRouteService.DiscoveryCallback {
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- }
-
- public final class MediaRouter {
- ctor public MediaRouter(android.content.Context);
- method public void addSelector(android.media.routing.MediaRouteSelector);
- method public void clearSelectors();
- method public android.media.routing.MediaRouter.Delegate createDelegate();
- method public android.media.routing.MediaRouter.ConnectionInfo getConnection();
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public java.util.List<android.media.routing.MediaRouter.RouteInfo> getDiscoveredRoutes(android.media.routing.MediaRouter.DestinationInfo);
- method public int getDiscoveryState();
- method public android.media.AudioAttributes getPreferredAudioAttributes();
- method public android.view.Display getPreferredPresentationDisplay();
- method public android.media.VolumeProvider getPreferredVolumeProvider();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public android.media.routing.MediaRouter.RouteInfo getSelectedRoute();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public boolean isReleased();
- method public void pauseStream();
- method public void release();
- method public void removeSelector(android.media.routing.MediaRouteSelector);
- method public void resumeStream();
- method public void setRoutingCallback(android.media.routing.MediaRouter.RoutingCallback, android.os.Handler);
- field public static final int CONNECTION_ERROR_ABORTED = 1; // 0x1
- field public static final int CONNECTION_ERROR_BARGED = 7; // 0x7
- field public static final int CONNECTION_ERROR_BROKEN = 6; // 0x6
- field public static final int CONNECTION_ERROR_BUSY = 4; // 0x4
- field public static final int CONNECTION_ERROR_TIMEOUT = 5; // 0x5
- field public static final int CONNECTION_ERROR_UNAUTHORIZED = 2; // 0x2
- field public static final int CONNECTION_ERROR_UNKNOWN = 0; // 0x0
- field public static final int CONNECTION_ERROR_UNREACHABLE = 3; // 0x3
- field public static final int CONNECTION_FLAG_BARGE = 1; // 0x1
- field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
- field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
- field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0; // 0x0
- field public static final int DISCONNECTION_REASON_ERROR = 2; // 0x2
- field public static final int DISCONNECTION_REASON_USER_REQUEST = 1; // 0x1
- field public static final int DISCOVERY_ERROR_ABORTED = 1; // 0x1
- field public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2; // 0x2
- field public static final int DISCOVERY_ERROR_UNKNOWN = 0; // 0x0
- field public static final int DISCOVERY_FLAG_BACKGROUND = 1; // 0x1
- field public static final int DISCOVERY_STATE_STARTED = 1; // 0x1
- field public static final int DISCOVERY_STATE_STOPPED = 0; // 0x0
- field public static final int ROUTE_FEATURE_LIVE_AUDIO = 1; // 0x1
- field public static final int ROUTE_FEATURE_LIVE_VIDEO = 2; // 0x2
- }
-
- public static final class MediaRouter.ConnectionInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public android.view.Display getPresentationDisplay();
- method public android.os.IBinder getProtocolBinder(java.lang.String);
- method public android.os.IBinder getProtocolBinder(int);
- method public T getProtocolObject(java.lang.Class<T>);
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public android.media.VolumeProvider getVolumeProvider();
- }
-
- public static final class MediaRouter.ConnectionInfo.Builder {
- ctor public MediaRouter.ConnectionInfo.Builder(android.media.routing.MediaRouter.RouteInfo);
- method public android.media.routing.MediaRouter.ConnectionInfo build();
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setAudioAttributes(android.media.AudioAttributes);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setPresentationDisplay(android.view.Display);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolBinder(java.lang.String, android.os.IBinder);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolStub(java.lang.Class<?>, android.os.IInterface);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setVolumeProvider(android.media.VolumeProvider);
- }
-
- public static final class MediaRouter.ConnectionRequest {
- method public android.os.Bundle getExtras();
- method public int getFlags();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public void setExtras(android.os.Bundle);
- method public void setFlags(int);
- method public void setRoute(android.media.routing.MediaRouter.RouteInfo);
- }
-
- public static final class MediaRouter.Delegate {
- ctor public MediaRouter.Delegate();
- method public void addStateCallback(android.media.routing.MediaRouter.StateCallback, android.os.Handler);
- method public void connect(android.media.routing.MediaRouter.DestinationInfo, int);
- method public void disconnect(int);
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public int getDiscoveryState();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public boolean isReleased();
- method public void removeStateCallback(android.media.routing.MediaRouter.StateCallback);
- method public void startDiscovery(int);
- method public void stopDiscovery();
- }
-
- public static final class MediaRouter.DestinationInfo {
- method public java.lang.CharSequence getDescription();
- method public android.os.Bundle getExtras();
- method public int getIconResourceId();
- method public java.lang.String getId();
- method public java.lang.CharSequence getName();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
- }
-
- public static final class MediaRouter.DestinationInfo.Builder {
- ctor public MediaRouter.DestinationInfo.Builder(java.lang.String, android.media.routing.MediaRouter.ServiceMetadata, java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo build();
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setDescription(java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setIconResourceId(int);
- }
-
- public static final class MediaRouter.DiscoveryRequest {
- method public int getFlags();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public void setFlags(int);
- method public void setSelectors(java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.RouteInfo {
- method public android.media.routing.MediaRouter.DestinationInfo getDestination();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public java.lang.String getId();
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouteSelector getSelector();
- }
-
- public static final class MediaRouter.RouteInfo.Builder {
- ctor public MediaRouter.RouteInfo.Builder(java.lang.String, android.media.routing.MediaRouter.DestinationInfo, android.media.routing.MediaRouteSelector);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.Class<T>);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.String);
- method public android.media.routing.MediaRouter.RouteInfo build();
- method public android.media.routing.MediaRouter.RouteInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.RouteInfo.Builder setFeatures(int);
- }
-
- public static abstract class MediaRouter.RoutingCallback extends android.media.routing.MediaRouter.StateCallback {
- ctor public MediaRouter.RoutingCallback();
- method public boolean onPrepareConnectionRequest(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public boolean onPrepareDiscoveryRequest(android.media.routing.MediaRouter.DiscoveryRequest, java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.ServiceMetadata {
- method public android.content.ComponentName getComponentName();
- method public android.graphics.drawable.Drawable getIcon(android.content.pm.PackageManager);
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
- method public java.lang.String getPackageName();
- method public android.content.pm.ServiceInfo getService();
- }
-
- public static abstract class MediaRouter.StateCallback {
- ctor public MediaRouter.StateCallback();
- method public void onConnected();
- method public void onConnecting();
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onConnectionStateChanged(int);
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDisconnected();
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDiscoveryStarted();
- method public void onDiscoveryStateChanged(int);
- method public void onDiscoveryStopped();
- method public void onReleased();
- method public void onSelectedDestinationChanged(android.media.routing.MediaRouter.DestinationInfo);
- }
-
-}
-
package android.media.session {
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void adjustVolume(int, int);
- method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
method public long getFlags();
@@ -19032,7 +18797,6 @@ package android.media.session {
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
method public void setMediaButtonReceiver(android.app.PendingIntent);
- method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -30756,6 +30520,53 @@ package android.security {
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -30775,6 +30586,14 @@ package android.security {
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
@@ -36398,14 +36217,6 @@ package android.util {
}
public deprecated class FloatMath {
- method public static float ceil(float);
- method public static float cos(float);
- method public static float exp(float);
- method public static float floor(float);
- method public static float hypot(float, float);
- method public static float pow(float, float);
- method public static float sin(float);
- method public static float sqrt(float);
}
public final class JsonReader implements java.io.Closeable {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 5e77e15..0046a70 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -108,6 +108,21 @@ package android.text.format {
}
+package android.util {
+
+ public deprecated class FloatMath {
+ method public static float ceil(float);
+ method public static float cos(float);
+ method public static float exp(float);
+ method public static float floor(float);
+ method public static float hypot(float, float);
+ method public static float pow(float, float);
+ method public static float sin(float);
+ method public static float sqrt(float);
+ }
+
+}
+
package android.view {
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index bb25ec6..21dc1e2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -630,7 +630,10 @@ bool BootAnimation::movie()
}
glDisable(GL_SCISSOR_TEST);
}
- glDrawTexiOES(xc, yc, 0, animation.width, animation.height);
+ // 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);
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5790682..cdd72be 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -28,6 +28,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* This class enables automatic animations on layout changes in ViewGroup objects. To enable
@@ -757,7 +758,7 @@ public class LayoutTransition {
// reset the inter-animation delay, in case we use it later
staggerDelay = 0;
- final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+ final ViewTreeObserver observer = parent.getViewTreeObserver();
if (!observer.isAlive()) {
// If the observer's not in a good state, skip the transition
return;
@@ -790,21 +791,9 @@ public class LayoutTransition {
// This is the cleanup step. When we get this rendering event, we know that all of
// the appropriate animations have been set up and run. Now we can clear out the
// layout listeners.
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- public boolean onPreDraw() {
- parent.getViewTreeObserver().removeOnPreDrawListener(this);
- int count = layoutChangeListenerMap.size();
- if (count > 0) {
- Collection<View> views = layoutChangeListenerMap.keySet();
- for (View view : views) {
- View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
- view.removeOnLayoutChangeListener(listener);
- }
- }
- layoutChangeListenerMap.clear();
- return true;
- }
- });
+ CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
+ observer.addOnPreDrawListener(callback);
+ parent.addOnAttachStateChangeListener(callback);
}
/**
@@ -1499,4 +1488,50 @@ public class LayoutTransition {
View view, int transitionType);
}
+ /**
+ * Utility class to clean up listeners after animations are setup. Cleanup happens
+ * when either the OnPreDrawListener method is called or when the parent is detached,
+ * whichever comes first.
+ */
+ private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
+
+ final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
+ final ViewGroup parent;
+
+ CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
+ this.layoutChangeListenerMap = listenerMap;
+ this.parent = parent;
+ }
+
+ private void cleanup() {
+ parent.getViewTreeObserver().removeOnPreDrawListener(this);
+ parent.removeOnAttachStateChangeListener(this);
+ int count = layoutChangeListenerMap.size();
+ if (count > 0) {
+ Collection<View> views = layoutChangeListenerMap.keySet();
+ for (View view : views) {
+ View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
+ view.removeOnLayoutChangeListener(listener);
+ }
+ layoutChangeListenerMap.clear();
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ cleanup();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ cleanup();
+ return true;
+ }
+ };
+
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7260d10..9968dbb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -89,7 +89,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 75e4bab..1174387 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -18,6 +18,7 @@ package android.app;
import java.util.ArrayList;
+import android.annotation.CallSuper;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -89,6 +90,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
* service, or receiver in a process.
* If you override this method, be sure to call super.onCreate().
*/
+ @CallSuper
public void onCreate() {
}
@@ -98,9 +100,11 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
* removed by simply killing them; no user code (including this callback)
* is executed when doing so.
*/
+ @CallSuper
public void onTerminate() {
}
+ @CallSuper
public void onConfigurationChanged(Configuration newConfig) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
@@ -110,6 +114,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
}
}
+ @CallSuper
public void onLowMemory() {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
@@ -119,6 +124,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
}
}
+ @CallSuper
public void onTrimMemory(int level) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index d049104..f6e0e1e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -48,7 +48,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 46da025..391131a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -91,7 +91,6 @@ import android.os.IPowerManager;
import android.os.IUserManager;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemVibrator;
import android.os.UserHandle;
@@ -111,7 +110,7 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import android.view.PhoneLayoutInflater;
+import com.android.internal.policy.PhoneLayoutInflater;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 54fe786..d0298cd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -17,6 +17,7 @@
package android.content;
import android.content.pm.ApplicationInfo;
+import android.os.ResultReceiver;
import android.provider.MediaStore;
import android.util.ArraySet;
@@ -3291,11 +3292,79 @@ public class Intent implements Parcelable, Cloneable {
/**
* An Intent describing the choices you would like shown with
- * {@link #ACTION_PICK_ACTIVITY}.
+ * {@link #ACTION_PICK_ACTIVITY} or {@link #ACTION_CHOOSER}.
*/
public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
/**
+ * An Intent[] describing additional, alternate choices you would like shown with
+ * {@link #ACTION_CHOOSER}.
+ *
+ * <p>An app may be capable of providing several different payload types to complete a
+ * user's intended action. For example, an app invoking {@link #ACTION_SEND} to share photos
+ * with another app may use EXTRA_ALTERNATE_INTENTS to have the chooser transparently offer
+ * several different supported sending mechanisms for sharing, such as the actual "image/*"
+ * photo data or a hosted link where the photos can be viewed.</p>
+ *
+ * <p>The intent present in {@link #EXTRA_INTENT} will be treated as the
+ * first/primary/preferred intent in the set. Additional intents specified in
+ * this extra are ordered; by default intents that appear earlier in the array will be
+ * preferred over intents that appear later in the array as matches for the same
+ * target component. To alter this preference, a calling app may also supply
+ * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p>
+ */
+ public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
+
+ /**
+ * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection
+ * from the chooser activity presented by {@link #ACTION_CHOOSER}.
+ *
+ * <p>An app preparing an action for another app to complete may wish to allow the user to
+ * disambiguate between several options for completing the action based on the chosen target
+ * or otherwise refine the action before it is invoked.
+ * </p>
+ *
+ * <p>When sent, this IntentSender may be filled in with the following extras:</p>
+ * <ul>
+ * <li>{@link #EXTRA_INTENT} The first intent that matched the user's chosen target</li>
+ * <li>{@link #EXTRA_ALTERNATE_INTENTS} Any additional intents that also matched the user's
+ * chosen target beyond the first</li>
+ * <li>{@link #EXTRA_RESULT_RECEIVER} A {@link ResultReceiver} that the refinement activity
+ * should fill in and send once the disambiguation is complete</li>
+ * </ul>
+ */
+ public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER
+ = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+
+ /**
+ * A {@link ResultReceiver} used to return data back to the sender.
+ *
+ * <p>Used to complete an app-specific
+ * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER refinement} for {@link #ACTION_CHOOSER}.</p>
+ *
+ * <p>If {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} is present in the intent
+ * used to start a {@link #ACTION_CHOOSER} activity this extra will be
+ * {@link #fillIn(Intent, int) filled in} to that {@link IntentSender} and sent
+ * when the user selects a target component from the chooser. It is up to the recipient
+ * to send a result to this ResultReceiver to signal that disambiguation is complete
+ * and that the chooser should invoke the user's choice.</p>
+ *
+ * <p>The disambiguator should provide a Bundle to the ResultReceiver with an intent
+ * assigned to the key {@link #EXTRA_INTENT}. This supplied intent will be used by the chooser
+ * to match and fill in the final Intent or ChooserTarget before starting it.
+ * The supplied intent must {@link #filterEquals(Intent) match} one of the intents from
+ * {@link #EXTRA_INTENT} or {@link #EXTRA_ALTERNATE_INTENTS} passed to
+ * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} to be accepted.</p>
+ *
+ * <p>The result code passed to the ResultReceiver should be
+ * {@link android.app.Activity#RESULT_OK} if the refinement succeeded and the supplied intent's
+ * target in the chooser should be started, or {@link android.app.Activity#RESULT_CANCELED} if
+ * the chooser should finish without starting a target.</p>
+ */
+ public static final String EXTRA_RESULT_RECEIVER
+ = "android.intent.extra.RESULT_RECEIVER";
+
+ /**
* A CharSequence dialog title to provide to the user when used with a
* {@link #ACTION_CHOOSER}.
*/
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a4d6be0..691798f 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -326,9 +326,6 @@ public class RequestThreadManager {
}
try {
- startPreview(); // If preview is not running (i.e. after a JPEG capture), we need to
- // explicitely start and stop preview before setting preview surface.
- // null.
stopPreview();
} catch (RuntimeException e) {
Log.e(TAG, "Received device exception in configure call: ", e);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 293cf6f..a7af838 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -324,23 +324,7 @@ public final class Settings {
"android.settings.BLUETOOTH_SETTINGS";
/**
- * Activity Action: Show settings to allow configuration of Wifi Displays.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you
- * safeguard against this.
- * <p>
- * Input: Nothing.
- * <p>
- * Output: Nothing.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_WIFI_DISPLAY_SETTINGS =
- "android.settings.WIFI_DISPLAY_SETTINGS";
-
- /**
- * Activity Action: Show settings to allow configuration of
- * {@link android.media.routing.MediaRouteService media route providers}.
+ * Activity Action: Show settings to allow configuration of cast endpoints.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
* safeguard against this.
@@ -5951,6 +5935,12 @@ public final class Settings {
"wireless_charging_started_sound";
/**
+ * Whether to play a sound for charging events.
+ * @hide
+ */
+ public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
+
+ /**
* Whether we keep the device on while the device is plugged in.
* Supported values are:
* <ul>
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 29aaf30..0171869 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -40,7 +40,7 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.ViewGroup;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index cf29310..3eeb04a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1481,14 +1481,8 @@ public class TextToSpeech {
// interface).
// Sanitize locale using isLanguageAvailable.
- int result = service.isLanguageAvailable( language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
+ int result = service.isLanguageAvailable(language, country, variant);
+ if (result >= LANG_AVAILABLE) {
// Get the default voice for the locale.
String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
if (TextUtils.isEmpty(voiceName)) {
@@ -1502,10 +1496,28 @@ public class TextToSpeech {
return LANG_NOT_SUPPORTED;
}
+ // Set the language/country/variant of the voice, so #getLanguage will return
+ // the currently set voice locale when called.
+ Voice voice = getVoice(service, voiceName);
+ String voiceLanguage = "";
+ try {
+ voiceLanguage = voice.getLocale().getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
+ voice.getLocale(), e);
+ }
+
+ String voiceCountry = "";
+ try {
+ voiceCountry = voice.getLocale().getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
+ voice.getLocale(), e);
+ }
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
- mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
- mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
- mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
}
return result;
}
@@ -1654,20 +1666,32 @@ public class TextToSpeech {
if (TextUtils.isEmpty(voiceName)) {
return null;
}
- List<Voice> voices = service.getVoices();
- if (voices == null) {
- return null;
- }
- for (Voice voice : voices) {
- if (voice.getName().equals(voiceName)) {
- return voice;
- }
- }
- return null;
+ return getVoice(service, voiceName);
}
}, null, "getVoice");
}
+
+ /**
+ * Returns a Voice instance of the voice with the given voice name.
+ *
+ * @return Voice instance with the given voice name, or {@code null} if not set or on error.
+ *
+ * @see Voice
+ */
+ private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException {
+ List<Voice> voices = service.getVoices();
+ if (voices == null) {
+ return null;
+ }
+ for (Voice voice : voices) {
+ if (voice.getName().equals(voiceName)) {
+ return voice;
+ }
+ }
+ return null;
+ }
+
/**
* Returns a Voice instance that's the default voice for the default Text-to-speech language.
* @return The default voice instance for the default language, or {@code null} if not set or
@@ -1690,14 +1714,7 @@ public class TextToSpeech {
// Sanitize the locale using isLanguageAvailable.
int result = service.isLanguageAvailable(language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
- } else {
+ if (result < LANG_AVAILABLE) {
// The default language is not supported.
return null;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 451abea..59c7c6d 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -91,6 +91,7 @@ public class StaticLayout extends Layout {
b.mEllipsizedWidth = width;
b.mEllipsize = null;
b.mMaxLines = Integer.MAX_VALUE;
+ b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -100,6 +101,8 @@ public class StaticLayout extends Layout {
b.mPaint = null;
b.mText = null;
MeasuredText.recycle(b.mMeasuredText);
+ b.mMeasuredText = null;
+ nFinishBuilder(b.mNativePtr);
sPool.release(b);
}
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index 8f488af..bb7d15f 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -25,6 +25,8 @@ package android.util;
* {@link java.lang.Math}. {@link java.lang.Math} should be used in
* preference.
*
+ * <p>All methods were removed from the public API in version 23.
+ *
* @deprecated Use {@link java.lang.Math} instead.
*/
@Deprecated
@@ -39,6 +41,7 @@ public class FloatMath {
*
* @param value to be converted
* @return the floor of value
+ * @removed
*/
public static float floor(float value) {
return (float) Math.floor(value);
@@ -50,6 +53,7 @@ public class FloatMath {
*
* @param value to be converted
* @return the ceiling of value
+ * @removed
*/
public static float ceil(float value) {
return (float) Math.ceil(value);
@@ -60,6 +64,7 @@ public class FloatMath {
*
* @param angle to compute the cosine of, in radians
* @return the sine of angle
+ * @removed
*/
public static float sin(float angle) {
return (float) Math.sin(angle);
@@ -70,6 +75,7 @@ public class FloatMath {
*
* @param angle to compute the cosine of, in radians
* @return the cosine of angle
+ * @removed
*/
public static float cos(float angle) {
return (float) Math.cos(angle);
@@ -81,6 +87,7 @@ public class FloatMath {
*
* @param value to compute sqrt of
* @return the square root of value
+ * @removed
*/
public static float sqrt(float value) {
return (float) Math.sqrt(value);
@@ -92,6 +99,7 @@ public class FloatMath {
*
* @param value to compute the exponential of
* @return the exponential of value
+ * @removed
*/
public static float exp(float value) {
return (float) Math.exp(value);
@@ -104,6 +112,7 @@ public class FloatMath {
* @param x the base of the operation.
* @param y the exponent of the operation.
* @return {@code x} to the power of {@code y}.
+ * @removed
*/
public static float pow(float x, float y) {
return (float) Math.pow(x, y);
@@ -116,6 +125,7 @@ public class FloatMath {
* @param x a float number
* @param y a float number
* @return the hypotenuse
+ * @removed
*/
public static float hypot(float x, float y) {
return (float) Math.hypot(x, y);
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index bc38e1a..9f46f45 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -40,8 +40,7 @@ public class GhostView extends View {
mView.mGhostView = this;
final ViewGroup parent = (ViewGroup) mView.getParent();
setGhostedVisibility(View.INVISIBLE);
- parent.mRecreateDisplayList = true;
- parent.updateDisplayListIfDirty();
+ parent.invalidate();
}
@Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fda6e63..c9c2a82 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -76,6 +76,7 @@ import android.widget.Scroller;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 78604bf..040fd37 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -247,6 +247,13 @@ public final class InputMethodManager {
/** @hide */
public static final int DISPATCH_HANDLED = 1;
+ /** @hide */
+ public static final int SHOW_IM_PICKER_MODE_AUTO = 0;
+ /** @hide */
+ public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1;
+ /** @hide */
+ public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2;
+
final IInputMethodManager mService;
final Looper mMainLooper;
@@ -1890,9 +1897,28 @@ public final class InputMethodManager {
}
}
+ /**
+ * Shows the input method chooser dialog.
+ *
+ * @param showAuxiliarySubtypes Set true to show auxiliary input methods.
+ * @hide
+ */
+ public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
+ synchronized (mH) {
+ try {
+ final int mode = showAuxiliarySubtypes ?
+ SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
+ SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
+ mService.showInputMethodPickerFromClient(mClient, mode);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ }
+
private void showInputMethodPickerLocked() {
try {
- mService.showInputMethodPickerFromClient(mClient);
+ mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 8d8b3a3..97348e30 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -28,7 +28,7 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index e347faa..62ca1f0 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
@@ -34,6 +35,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.UserHandle;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
@@ -53,11 +55,13 @@ public class ChooserActivity extends ResolverActivity {
private static final boolean DEBUG = false;
- private static final int QUERY_TARGET_LIMIT = 5;
+ private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
private static final int WATCHDOG_TIMEOUT_MILLIS = 5000;
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
+ private IntentSender mRefinementIntentSender;
+ private RefinementResultReceiver mRefinementResultReceiver;
private ChooserTarget[] mCallerChooserTargets;
@@ -113,6 +117,32 @@ public class ChooserActivity extends ResolverActivity {
if (target != null) {
modifyTargetIntent(target);
}
+ Parcelable[] targetsParcelable
+ = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS);
+ if (targetsParcelable != null) {
+ final boolean offset = target == null;
+ Intent[] additionalTargets =
+ new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length];
+ for (int i = 0; i < targetsParcelable.length; i++) {
+ if (!(targetsParcelable[i] instanceof Intent)) {
+ Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: "
+ + targetsParcelable[i]);
+ finish();
+ super.onCreate(null);
+ return;
+ }
+ final Intent additionalTarget = (Intent) targetsParcelable[i];
+ if (i == 0 && target == null) {
+ target = additionalTarget;
+ modifyTargetIntent(target);
+ } else {
+ additionalTargets[offset ? i - 1 : i] = additionalTarget;
+ modifyTargetIntent(additionalTarget);
+ }
+ }
+ setAdditionalTargets(additionalTargets);
+ }
+
mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
int defaultTitleRes = 0;
@@ -125,7 +155,7 @@ public class ChooserActivity extends ResolverActivity {
initialIntents = new Intent[pa.length];
for (int i=0; i<pa.length; i++) {
if (!(pa[i] instanceof Intent)) {
- Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]);
+ Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
finish();
super.onCreate(null);
return;
@@ -141,8 +171,7 @@ public class ChooserActivity extends ResolverActivity {
final ChooserTarget[] targets = new ChooserTarget[pa.length];
for (int i = 0; i < pa.length; i++) {
if (!(pa[i] instanceof ChooserTarget)) {
- Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " +
- pa[i]);
+ Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]);
finish();
super.onCreate(null);
return;
@@ -153,12 +182,23 @@ public class ChooserActivity extends ResolverActivity {
}
mChosenComponentSender = intent.getParcelableExtra(
Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
+ mRefinementIntentSender = intent.getParcelableExtra(
+ Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
setSafeForwardingMode(true);
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
}
@Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mRefinementResultReceiver != null) {
+ mRefinementResultReceiver.destroy();
+ mRefinementResultReceiver = null;
+ }
+ }
+
+ @Override
public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Intent result = defIntent;
if (mReplacementExtras != null) {
@@ -211,6 +251,37 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ @Override
+ protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
+ if (mRefinementIntentSender != null) {
+ final Intent fillIn = new Intent();
+ final List<Intent> sourceIntents = target.getAllSourceIntents();
+ if (!sourceIntents.isEmpty()) {
+ fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0));
+ if (sourceIntents.size() > 1) {
+ final Intent[] alts = new Intent[sourceIntents.size() - 1];
+ for (int i = 1, N = sourceIntents.size(); i < N; i++) {
+ alts[i - 1] = sourceIntents.get(i);
+ }
+ fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts);
+ }
+ if (mRefinementResultReceiver != null) {
+ mRefinementResultReceiver.destroy();
+ }
+ mRefinementResultReceiver = new RefinementResultReceiver(this, target, null);
+ fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER,
+ mRefinementResultReceiver);
+ try {
+ mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null);
+ return false;
+ } catch (SendIntentException e) {
+ Log.e(TAG, "Refinement IntentSender failed to send", e);
+ }
+ }
+ }
+ return super.onTargetSelected(target, alwaysCheck);
+ }
+
void queryTargetServices(ChooserListAdapter adapter) {
final PackageManager pm = getPackageManager();
int targetsToQuery = 0;
@@ -258,8 +329,9 @@ public class ChooserActivity extends ResolverActivity {
targetsToQuery++;
}
}
- if (targetsToQuery >= QUERY_TARGET_LIMIT) {
- if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT);
+ if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
+ if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
+ + QUERY_TARGET_SERVICE_LIMIT);
break;
}
}
@@ -303,6 +375,43 @@ public class ChooserActivity extends ResolverActivity {
mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
}
+ void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
+ if (mRefinementResultReceiver != null) {
+ mRefinementResultReceiver.destroy();
+ mRefinementResultReceiver = null;
+ }
+
+ if (selectedTarget == null) {
+ Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
+ } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
+ Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
+ + " cannot match refined source intent " + matchingIntent);
+ } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) {
+ finish();
+ return;
+ }
+ onRefinementCanceled();
+ }
+
+ void onRefinementCanceled() {
+ if (mRefinementResultReceiver != null) {
+ mRefinementResultReceiver.destroy();
+ mRefinementResultReceiver = null;
+ }
+ finish();
+ }
+
+ boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) {
+ final List<Intent> targetIntents = target.getAllSourceIntents();
+ for (int i = 0, N = targetIntents.size(); i < N; i++) {
+ final Intent targetIntent = targetIntents.get(i);
+ if (targetIntent.filterEquals(matchingIntent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
ResolveListAdapter createAdapter(Context context, Intent[] initialIntents,
List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
@@ -313,17 +422,19 @@ public class ChooserActivity extends ResolverActivity {
return adapter;
}
- class ChooserTargetInfo implements TargetInfo {
- private final TargetInfo mSourceInfo;
+ final class ChooserTargetInfo implements TargetInfo {
+ private final DisplayResolveInfo mSourceInfo;
private final ResolveInfo mBackupResolveInfo;
private final ChooserTarget mChooserTarget;
private final Drawable mDisplayIcon;
+ private final Intent mFillInIntent;
+ private final int mFillInFlags;
public ChooserTargetInfo(ChooserTarget target) {
this(null, target);
}
- public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) {
+ public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) {
mSourceInfo = sourceInfo;
mChooserTarget = chooserTarget;
mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon());
@@ -333,6 +444,18 @@ public class ChooserActivity extends ResolverActivity {
} else {
mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0);
}
+
+ mFillInIntent = null;
+ mFillInFlags = 0;
+ }
+
+ private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) {
+ mSourceInfo = other.mSourceInfo;
+ mBackupResolveInfo = other.mBackupResolveInfo;
+ mChooserTarget = other.mChooserTarget;
+ mDisplayIcon = other.mDisplayIcon;
+ mFillInIntent = fillInIntent;
+ mFillInFlags = flags;
}
@Override
@@ -358,22 +481,42 @@ public class ChooserActivity extends ResolverActivity {
}
private Intent getFillInIntent() {
- return mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent();
+ Intent result = mSourceInfo != null
+ ? mSourceInfo.getResolvedIntent() : getTargetIntent();
+ if (result == null) {
+ Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available");
+ } else if (mFillInIntent != null) {
+ result = new Intent(result);
+ result.fillIn(mFillInIntent, mFillInFlags);
+ }
+ return result;
}
@Override
public boolean start(Activity activity, Bundle options) {
- return mChooserTarget.sendIntent(activity, getFillInIntent());
+ final Intent intent = getFillInIntent();
+ if (intent == null) {
+ return false;
+ }
+ return mChooserTarget.sendIntent(activity, intent);
}
@Override
public boolean startAsCaller(Activity activity, Bundle options, int userId) {
- return mChooserTarget.sendIntentAsCaller(activity, getFillInIntent(), userId);
+ final Intent intent = getFillInIntent();
+ if (intent == null) {
+ return false;
+ }
+ return mChooserTarget.sendIntentAsCaller(activity, intent, userId);
}
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
- return mChooserTarget.sendIntentAsUser(activity, getFillInIntent(), user);
+ final Intent intent = getFillInIntent();
+ if (intent == null) {
+ return false;
+ }
+ return mChooserTarget.sendIntentAsUser(activity, intent, user);
}
@Override
@@ -395,6 +538,21 @@ public class ChooserActivity extends ResolverActivity {
public Drawable getDisplayIcon() {
return mDisplayIcon;
}
+
+ @Override
+ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
+ return new ChooserTargetInfo(this, fillInIntent, flags);
+ }
+
+ @Override
+ public List<Intent> getAllSourceIntents() {
+ final List<Intent> results = new ArrayList<>();
+ if (mSourceInfo != null) {
+ // We only queried the service for the first one in our sourceinfo.
+ results.add(mSourceInfo.getAllSourceIntents().get(0));
+ }
+ return results;
+ }
}
public class ChooserListAdapter extends ResolveListAdapter {
@@ -542,4 +700,53 @@ public class ChooserActivity extends ResolverActivity {
connection = c;
}
}
+
+ static class RefinementResultReceiver extends ResultReceiver {
+ private ChooserActivity mChooserActivity;
+ private TargetInfo mSelectedTarget;
+
+ public RefinementResultReceiver(ChooserActivity host, TargetInfo target,
+ Handler handler) {
+ super(handler);
+ mChooserActivity = host;
+ mSelectedTarget = target;
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (mChooserActivity == null) {
+ Log.e(TAG, "Destroyed RefinementResultReceiver received a result");
+ return;
+ }
+ if (resultData == null) {
+ Log.e(TAG, "RefinementResultReceiver received null resultData");
+ return;
+ }
+
+ switch (resultCode) {
+ case RESULT_CANCELED:
+ mChooserActivity.onRefinementCanceled();
+ break;
+ case RESULT_OK:
+ Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT);
+ if (intentParcelable instanceof Intent) {
+ mChooserActivity.onRefinementResult(mSelectedTarget,
+ (Intent) intentParcelable);
+ } else {
+ Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent"
+ + " in resultData with key Intent.EXTRA_INTENT");
+ }
+ break;
+ default:
+ Log.w(TAG, "Unknown result code " + resultCode
+ + " sent to RefinementResultReceiver");
+ break;
+ }
+ }
+
+ public void destroy() {
+ mChooserActivity = null;
+ mSelectedTarget = null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 8dd7836..2048664 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -102,7 +102,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
private int mLastSelected = AbsListView.INVALID_POSITION;
private boolean mResolvingHome = false;
private int mProfileSwitchMessageId = -1;
- private Intent mIntent;
+ private final ArrayList<Intent> mIntents = new ArrayList<>();
private UsageStatsManager mUsm;
private Map<String, UsageStats> mStats;
@@ -229,7 +229,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
mIconDpi = am.getLauncherLargeIconDensity();
- mIntent = new Intent(intent);
+ mIntents.add(0, new Intent(intent));
mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption);
final int layoutId;
@@ -250,7 +250,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return;
}
- int count = mAdapter.mList.size();
+ int count = mAdapter.mDisplayList.size();
if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
setContentView(layoutId);
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
@@ -376,8 +376,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
}
+ protected final void setAdditionalTargets(Intent[] intents) {
+ if (intents != null) {
+ for (Intent intent : intents) {
+ mIntents.add(intent);
+ }
+ }
+ }
+
public Intent getTargetIntent() {
- return mIntent;
+ return mIntents.isEmpty() ? null : mIntents.get(0);
}
private String getReferrerPackageName() {
@@ -630,8 +638,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
- onTargetSelected(target, always);
- finish();
+ if (onTargetSelected(target, always)) {
+ finish();
+ }
}
/**
@@ -641,7 +650,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return defIntent;
}
- protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) {
+ protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
final ResolveInfo ri = target.getResolveInfo();
final Intent intent = target != null ? target.getResolvedIntent() : null;
@@ -728,7 +737,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
ComponentName[] set = new ComponentName[N];
int bestMatch = 0;
for (int i=0; i<N; i++) {
- ResolveInfo r = mAdapter.mOrigResolveList.get(i);
+ ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
set[i] = new ComponentName(r.activityInfo.packageName,
r.activityInfo.name);
if (r.match > bestMatch) bestMatch = r.match;
@@ -774,6 +783,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (target != null) {
safelyStartActivity(target);
}
+ return true;
}
void safelyStartActivity(TargetInfo cti) {
@@ -837,15 +847,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
private Drawable mDisplayIcon;
private final CharSequence mExtendedInfo;
private final Intent mResolvedIntent;
+ private final List<Intent> mSourceIntents = new ArrayList<>();
- DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
+ DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
CharSequence pInfo, Intent pOrigIntent) {
+ mSourceIntents.add(originalIntent);
mResolveInfo = pri;
mDisplayLabel = pLabel;
mExtendedInfo = pInfo;
final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
- getReplacementIntent(pri.activityInfo, mIntent));
+ getReplacementIntent(pri.activityInfo, getTargetIntent()));
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
| Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
final ActivityInfo ai = mResolveInfo.activityInfo;
@@ -854,6 +866,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
mResolvedIntent = intent;
}
+ private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) {
+ mSourceIntents.addAll(other.getAllSourceIntents());
+ mResolveInfo = other.mResolveInfo;
+ mDisplayLabel = other.mDisplayLabel;
+ mDisplayIcon = other.mDisplayIcon;
+ mExtendedInfo = other.mExtendedInfo;
+ mResolvedIntent = new Intent(other.mResolvedIntent);
+ mResolvedIntent.fillIn(fillInIntent, flags);
+ }
+
public ResolveInfo getResolveInfo() {
return mResolveInfo;
}
@@ -866,6 +888,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return mDisplayIcon;
}
+ @Override
+ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
+ return new DisplayResolveInfo(this, fillInIntent, flags);
+ }
+
+ @Override
+ public List<Intent> getAllSourceIntents() {
+ return mSourceIntents;
+ }
+
+ public void addAlternateSourceIntent(Intent alt) {
+ mSourceIntents.add(alt);
+ }
+
public void setDisplayIcon(Drawable icon) {
mDisplayIcon = icon;
}
@@ -986,6 +1022,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
* @return The drawable that should be used to represent this target
*/
public Drawable getDisplayIcon();
+
+ /**
+ * Clone this target with the given fill-in information.
+ */
+ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
+
+ /**
+ * @return the list of supported source intents deduped against this single target
+ */
+ public List<Intent> getAllSourceIntents();
}
class ResolveListAdapter extends BaseAdapter {
@@ -998,8 +1044,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
protected final LayoutInflater mInflater;
- List<DisplayResolveInfo> mList;
- List<ResolveInfo> mOrigResolveList;
+ List<DisplayResolveInfo> mDisplayList;
+ List<ResolvedComponentInfo> mOrigResolveList;
private int mLastChosenPosition = -1;
private boolean mFilterLastUsed;
@@ -1010,7 +1056,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
mBaseResolveList = rList;
mLaunchedFromUid = launchedFromUid;
mInflater = LayoutInflater.from(context);
- mList = new ArrayList<>();
+ mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed;
rebuildList();
}
@@ -1027,7 +1073,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
public DisplayResolveInfo getFilteredItem() {
if (mFilterLastUsed && mLastChosenPosition >= 0) {
// Not using getItem since it offsets to dodge this position for the list
- return mList.get(mLastChosenPosition);
+ return mDisplayList.get(mLastChosenPosition);
}
return null;
}
@@ -1048,11 +1094,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
private void rebuildList() {
- List<ResolveInfo> currentResolveList;
+ List<ResolvedComponentInfo> currentResolveList = null;
try {
+ final Intent primaryIntent = getTargetIntent();
mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
- mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
+ primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY);
} catch (RemoteException re) {
Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
@@ -1060,15 +1107,27 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// Clear the value of mOtherProfile from previous call.
mOtherProfile = null;
- mList.clear();
+ mDisplayList.clear();
if (mBaseResolveList != null) {
- currentResolveList = mOrigResolveList = mBaseResolveList;
+ currentResolveList = mOrigResolveList = new ArrayList<>();
+ addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
} else {
- currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
- PackageManager.MATCH_DEFAULT_ONLY
- | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
- | (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
- );
+ final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
+ final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
+ for (int i = 0, N = mIntents.size(); i < N; i++) {
+ final Intent intent = mIntents.get(i);
+ final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
+ if (infos != null) {
+ if (currentResolveList == null) {
+ currentResolveList = mOrigResolveList = new ArrayList<>();
+ }
+ addResolveListDedupe(currentResolveList, intent, infos);
+ }
+ }
+
// Filter out any activities that the launched uid does not
// have permission for. We don't do this when we have an explicit
// list of resolved activities, because that only happens when
@@ -1076,14 +1135,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// they gave us.
if (currentResolveList != null) {
for (int i=currentResolveList.size()-1; i >= 0; i--) {
- ActivityInfo ai = currentResolveList.get(i).activityInfo;
+ ActivityInfo ai = currentResolveList.get(i)
+ .getResolveInfoAt(0).activityInfo;
int granted = ActivityManager.checkComponentPermission(
ai.permission, mLaunchedFromUid,
ai.applicationInfo.uid, ai.exported);
if (granted != PackageManager.PERMISSION_GRANTED) {
// Access not allowed!
if (mOrigResolveList == currentResolveList) {
- mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
+ mOrigResolveList = new ArrayList<>(mOrigResolveList);
}
currentResolveList.remove(i);
}
@@ -1094,9 +1154,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
// Only display the first matches that are either of equal
// priority or have asked to be default options.
- ResolveInfo r0 = currentResolveList.get(0);
+ ResolvedComponentInfo rci0 = currentResolveList.get(0);
+ ResolveInfo r0 = rci0.getResolveInfoAt(0);
for (int i=1; i<N; i++) {
- ResolveInfo ri = currentResolveList.get(i);
+ ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
if (DEBUG) Log.v(
TAG,
r0.activityInfo.name + "=" +
@@ -1107,7 +1168,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
r0.isDefault != ri.isDefault) {
while (i < N) {
if (mOrigResolveList == currentResolveList) {
- mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
+ mOrigResolveList = new ArrayList<>(mOrigResolveList);
}
currentResolveList.remove(i);
N--;
@@ -1115,9 +1176,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
}
if (N > 1) {
- Comparator<ResolveInfo> rComparator =
- new ResolverComparator(ResolverActivity.this, mIntent);
- Collections.sort(currentResolveList, rComparator);
+ Collections.sort(currentResolveList,
+ new ResolverComparator(ResolverActivity.this, getTargetIntent()));
}
// First put the initial items at the top.
if (mInitialIntents != null) {
@@ -1146,14 +1206,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
ri.nonLocalizedLabel = li.getNonLocalizedLabel();
ri.icon = li.getIconResource();
}
- addResolveInfo(new DisplayResolveInfo(ri,
+ addResolveInfo(new DisplayResolveInfo(ii, ri,
ri.loadLabel(getPackageManager()), null, ii));
}
}
// Check for applications with same name and use application name or
// package name if necessary
- r0 = currentResolveList.get(0);
+ rci0 = currentResolveList.get(0);
+ r0 = rci0.getResolveInfoAt(0);
int start = 0;
CharSequence r0Label = r0.loadLabel(mPm);
mHasExtendedInfo = false;
@@ -1161,7 +1222,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (r0Label == null) {
r0Label = r0.activityInfo.packageName;
}
- ResolveInfo ri = currentResolveList.get(i);
+ ResolvedComponentInfo rci = currentResolveList.get(i);
+ ResolveInfo ri = rci.getResolveInfoAt(0);
CharSequence riLabel = ri.loadLabel(mPm);
if (riLabel == null) {
riLabel = ri.activityInfo.packageName;
@@ -1169,13 +1231,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (riLabel.equals(r0Label)) {
continue;
}
- processGroup(currentResolveList, start, (i-1), r0, r0Label);
+ processGroup(currentResolveList, start, (i-1), rci0, r0Label);
+ rci0 = rci;
r0 = ri;
r0Label = riLabel;
start = i;
}
// Process last group
- processGroup(currentResolveList, start, (N-1), r0, r0Label);
+ processGroup(currentResolveList, start, (N-1), rci0, r0Label);
}
// Layout doesn't handle both profile button and last chosen
@@ -1188,6 +1251,36 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
onListRebuilt();
}
+ private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
+ List<ResolveInfo> from) {
+ final int fromCount = from.size();
+ final int intoCount = into.size();
+ for (int i = 0; i < fromCount; i++) {
+ final ResolveInfo newInfo = from.get(i);
+ boolean found = false;
+ // Only loop to the end of into as it was before we started; no dupes in from.
+ for (int j = 0; j < intoCount; j++) {
+ final ResolvedComponentInfo rci = into.get(i);
+ if (isSameResolvedComponent(newInfo, rci)) {
+ found = true;
+ rci.add(intent, newInfo);
+ break;
+ }
+ }
+ if (!found) {
+ into.add(new ResolvedComponentInfo(new ComponentName(
+ newInfo.activityInfo.packageName, newInfo.activityInfo.name),
+ intent, newInfo));
+ }
+ }
+ }
+
+ private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
+ final ActivityInfo ai = a.activityInfo;
+ return ai.packageName.equals(b.name.getPackageName())
+ && ai.name.equals(b.name.getClassName());
+ }
+
public void onListRebuilt() {
// This space for rent
}
@@ -1196,18 +1289,18 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return mFilterLastUsed;
}
- private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
- CharSequence roLabel) {
+ private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
+ ResolvedComponentInfo ro, CharSequence roLabel) {
// Process labels from start to i
int num = end - start+1;
if (num == 1) {
// No duplicate labels. Use label for entry at start
- addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null));
- updateLastChosenPosition(ro);
+ addResolveInfoWithAlternates(ro, null, roLabel);
} else {
mHasExtendedInfo = true;
boolean usePkg = false;
- CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
+ CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
+ .loadLabel(mPm);
if (startApp == null) {
usePkg = true;
}
@@ -1217,7 +1310,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
new HashSet<CharSequence>();
duplicates.add(startApp);
for (int j = start+1; j <= end ; j++) {
- ResolveInfo jRi = rList.get(j);
+ ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
if ( (jApp == null) || (duplicates.contains(jApp))) {
usePkg = true;
@@ -1230,26 +1323,46 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
duplicates.clear();
}
for (int k = start; k <= end; k++) {
- ResolveInfo add = rList.get(k);
+ final ResolvedComponentInfo rci = rList.get(k);
+ final ResolveInfo add = rci.getResolveInfoAt(0);
+ final CharSequence extraInfo;
if (usePkg) {
- // Use application name for all entries from start to end-1
- addResolveInfo(new DisplayResolveInfo(add, roLabel,
- add.activityInfo.packageName, null));
- } else {
// Use package name for all entries from start to end-1
- addResolveInfo(new DisplayResolveInfo(add, roLabel,
- add.activityInfo.applicationInfo.loadLabel(mPm), null));
+ extraInfo = add.activityInfo.packageName;
+ } else {
+ // Use application name for all entries from start to end-1
+ extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
}
- updateLastChosenPosition(add);
+ addResolveInfoWithAlternates(rci, extraInfo, roLabel);
+ }
+ }
+ }
+
+ private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
+ CharSequence extraInfo, CharSequence roLabel) {
+ final int count = rci.getCount();
+ final Intent intent = rci.getIntentAt(0);
+ final ResolveInfo add = rci.getResolveInfoAt(0);
+ final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
+ final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
+ extraInfo, replaceIntent);
+ addResolveInfo(dri);
+ if (replaceIntent == intent) {
+ // Only add alternates if we didn't get a specific replacement from
+ // the caller. If we have one it trumps potential alternates.
+ for (int i = 1, N = count; i < N; i++) {
+ final Intent altIntent = rci.getIntentAt(i);
+ dri.addAlternateSourceIntent(altIntent);
}
}
+ updateLastChosenPosition(add);
}
private void updateLastChosenPosition(ResolveInfo info) {
if (mLastChosen != null
&& mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
&& mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
- mLastChosenPosition = mList.size() - 1;
+ mLastChosenPosition = mDisplayList.size() - 1;
}
}
@@ -1259,20 +1372,21 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// The first one we see gets special treatment.
mOtherProfile = dri;
} else {
- mList.add(dri);
+ mDisplayList.add(dri);
}
}
public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
- return (filtered ? getItem(position) : mList.get(position)).getResolveInfo();
+ return (filtered ? getItem(position) : mDisplayList.get(position))
+ .getResolveInfo();
}
public TargetInfo targetInfoForPosition(int position, boolean filtered) {
- return filtered ? getItem(position) : mList.get(position);
+ return filtered ? getItem(position) : mDisplayList.get(position);
}
public int getCount() {
- int result = mList.size();
+ int result = mDisplayList.size();
if (mFilterLastUsed && mLastChosenPosition >= 0) {
result--;
}
@@ -1283,7 +1397,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
position++;
}
- return mList.get(position);
+ return mDisplayList.get(position);
}
public long getItemId(int position) {
@@ -1295,8 +1409,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
public boolean hasResolvedTarget(ResolveInfo info) {
- for (int i = 0, N = mList.size(); i < N; i++) {
- if (info.equals(mList.get(i).getResolveInfo())) {
+ for (int i = 0, N = mDisplayList.size(); i < N; i++) {
+ if (info.equals(mDisplayList.get(i).getResolveInfo())) {
return true;
}
}
@@ -1304,11 +1418,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
protected int getDisplayResolveInfoCount() {
- return mList.size();
+ return mDisplayList.size();
}
protected DisplayResolveInfo getDisplayResolveInfo(int index) {
- return mList.get(index);
+ // Used to query services. We only query services for primary targets, not alternates.
+ return mDisplayList.get(index);
}
public final View getView(int position, View convertView, ViewGroup parent) {
@@ -1349,6 +1464,52 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
}
+ static final class ResolvedComponentInfo {
+ public final ComponentName name;
+ private final List<Intent> mIntents = new ArrayList<>();
+ private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
+
+ public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
+ this.name = name;
+ add(intent, info);
+ }
+
+ public void add(Intent intent, ResolveInfo info) {
+ mIntents.add(intent);
+ mResolveInfos.add(info);
+ }
+
+ public int getCount() {
+ return mIntents.size();
+ }
+
+ public Intent getIntentAt(int index) {
+ return index >= 0 ? mIntents.get(index) : null;
+ }
+
+ public ResolveInfo getResolveInfoAt(int index) {
+ return index >= 0 ? mResolveInfos.get(index) : null;
+ }
+
+ public int findIntent(Intent intent) {
+ for (int i = 0, N = mIntents.size(); i < N; i++) {
+ if (intent.equals(mIntents.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int findResolveInfo(ResolveInfo info) {
+ for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
+ if (info.equals(mResolveInfos.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ }
+
static class ViewHolder {
public TextView text;
public TextView text2;
@@ -1435,7 +1596,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
&& match <= IntentFilter.MATCH_CATEGORY_PATH;
}
- class ResolverComparator implements Comparator<ResolveInfo> {
+ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private final Collator mCollator;
private final boolean mHttp;
@@ -1446,7 +1607,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
}
@Override
- public int compare(ResolveInfo lhs, ResolveInfo rhs) {
+ public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
+ final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
+ final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
+
// We want to put the one targeted to another user at the end of the dialog.
if (lhs.targetUserId != UserHandle.USER_CURRENT) {
return 1;
@@ -1487,7 +1651,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (stats != null) {
return stats.getTotalTimeInForeground();
}
-
}
return 0;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 52485dd..ce94727 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -196,7 +196,7 @@ public class InputMethodSubtypeSwitchingController {
}
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
- boolean showSubtypes, boolean inputShown, boolean isScreenLocked) {
+ boolean showSubtypes, boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
final ArrayList<ImeSubtypeListItem> imList =
new ArrayList<ImeSubtypeListItem>();
final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
@@ -205,6 +205,12 @@ public class InputMethodSubtypeSwitchingController {
if (immis == null || immis.size() == 0) {
return Collections.emptyList();
}
+ if (isScreenLocked && includeAuxiliarySubtypes) {
+ if (DEBUG) {
+ Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
+ }
+ includeAuxiliarySubtypes = false;
+ }
mSortedImmis.clear();
mSortedImmis.putAll(immis);
for (InputMethodInfo imi : mSortedImmis.keySet()) {
@@ -227,7 +233,7 @@ public class InputMethodSubtypeSwitchingController {
final String subtypeHashCode = String.valueOf(subtype.hashCode());
// We show all enabled IMEs and subtypes when an IME is shown.
if (enabledSubtypeSet.contains(subtypeHashCode)
- && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
+ && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
final CharSequence subtypeLabel =
subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
.getDisplayName(mContext, imi.getPackageName(),
@@ -516,8 +522,8 @@ public class InputMethodSubtypeSwitchingController {
}
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
- boolean inputShown, boolean isScreenLocked) {
+ boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
return mSubtypeList.getSortedInputMethodAndSubtypeList(
- showSubtypes, inputShown, isScreenLocked);
+ showSubtypes, includingAuxiliarySubtypes, isScreenLocked);
}
}
diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index 350650d..2cb9c25 100644
--- a/core/java/android/view/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import android.app.KeyguardManager;
import android.app.SearchManager;
@@ -28,10 +28,11 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.view.View;
-import android.view.HapticFeedbackConstants;
import android.view.FallbackEventHandler;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
+import android.view.View;
+import com.android.internal.policy.PhoneWindow;
/**
* @hide
diff --git a/core/java/android/view/PhoneLayoutInflater.java b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
index 7d89a0b..991b6bb 100644
--- a/core/java/android/view/PhoneLayoutInflater.java
+++ b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
/**
* @hide
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a3e7a10..a578a6e 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
@@ -27,6 +27,34 @@ import android.app.ActivityManagerNative;
import android.app.SearchManager;
import android.os.UserHandle;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.IRotationWatcher.Stub;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputQueue;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.SearchEvent;
+import android.view.SurfaceHolder.Callback2;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.FloatingActionMode;
@@ -67,7 +95,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
@@ -140,7 +167,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ViewGroup mContentRoot;
- SurfaceHolder.Callback2 mTakeSurfaceCallback;
+ Callback2 mTakeSurfaceCallback;
InputQueue.Callback mTakeInputQueueCallback;
@@ -427,7 +454,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
@Override
- public void takeSurface(SurfaceHolder.Callback2 callback) {
+ public void takeSurface(Callback2 callback) {
mTakeSurfaceCallback = callback;
}
@@ -2181,7 +2208,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ActionBarContextView mPrimaryActionModeView;
private PopupWindow mPrimaryActionModePopup;
private Runnable mShowPrimaryActionModePopup;
- private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
+ private OnPreDrawListener mFloatingToolbarPreDrawListener;
private View mFloatingActionModeOriginatingView;
private FloatingToolbar mFloatingToolbar;
@@ -3354,7 +3381,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
mFloatingToolbarPreDrawListener =
- new ViewTreeObserver.OnPreDrawListener() {
+ new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mode.updateViewLocationInWindow();
@@ -4718,7 +4745,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
- static class RotationWatcher extends IRotationWatcher.Stub {
+ static class RotationWatcher extends Stub {
private Handler mHandler;
private final Runnable mRotationChanged = new Runnable() {
public void run() {
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
deleted file mode 100644
index 1a6736a..0000000
--- a/core/java/com/android/internal/transition/EpicenterClipReveal.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2015 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.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.R;
-
-/**
- * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
- * after the scene change and animates between those and the epicenter bounds
- * during a visibility transition.
- */
-public class EpicenterClipReveal extends Visibility {
- private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
- private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final boolean mCenterClipBounds;
-
- public EpicenterClipReveal() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mCenterClipBounds = false;
- }
-
- public EpicenterClipReveal(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterClipReveal, 0, 0);
-
- mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- super.captureStartValues(transitionValues);
- captureValues(transitionValues);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- super.captureEndValues(transitionValues);
- captureValues(transitionValues);
- }
-
- private void captureValues(TransitionValues values) {
- final View view = values.view;
- if (view.getVisibility() == View.GONE) {
- return;
- }
-
- final Rect clip = view.getClipBounds();
- values.values.put(PROPNAME_CLIP, clip);
-
- if (clip == null) {
- final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
- values.values.put(PROPNAME_BOUNDS, bounds);
- }
- }
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (endValues == null) {
- return null;
- }
-
- final Rect end = getBestRect(endValues);
- final Rect start = getEpicenterOrCenter(end);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (startValues == null) {
- return null;
- }
-
- final Rect start = getBestRect(startValues);
- final Rect end = getEpicenterOrCenter(start);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- // Translate the clip bounds to be centered within the target bounds.
- if (mCenterClipBounds) {
- final int offsetX = bestRect.centerX() - epicenter.centerX();
- final int offsetY = bestRect.centerY() - epicenter.centerY();
- epicenter.offset(offsetX, offsetY);
- }
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- private Rect getBestRect(TransitionValues values) {
- final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
- if (clipRect == null) {
- return (Rect) values.values.get(PROPNAME_BOUNDS);
- }
- return clipRect;
- }
-
- private static Animator createRectAnimator(final View view, Rect start, Rect end,
- TransitionValues endValues, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY) {
- final RectEvaluator evaluator = new RectEvaluator(new Rect());
- final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
-
- final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X);
- final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y);
- final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY);
- animSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setClipBounds(terminalClip);
- }
- });
- return animSet;
- }
-
- private static class ClipDimenProperty extends Property<View, Rect> {
- public static final char TARGET_X = 'x';
- public static final char TARGET_Y = 'y';
-
- private final Rect mTempRect = new Rect();
-
- private final int mTargetDimension;
-
- public ClipDimenProperty(char targetDimension) {
- super(Rect.class, "clip_bounds_" + targetDimension);
-
- mTargetDimension = targetDimension;
- }
-
- @Override
- public Rect get(View object) {
- final Rect tempRect = mTempRect;
- if (!object.getClipBounds(tempRect)) {
- tempRect.setEmpty();
- }
- return tempRect;
- }
-
- @Override
- public void set(View object, Rect value) {
- final Rect tempRect = mTempRect;
- if (object.getClipBounds(tempRect)) {
- if (mTargetDimension == TARGET_X) {
- tempRect.left = value.left;
- tempRect.right = value.right;
- } else {
- tempRect.top = value.top;
- tempRect.bottom = value.bottom;
- }
- object.setClipBounds(tempRect);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java
deleted file mode 100644
index eac3ff8..0000000
--- a/core/java/com/android/internal/transition/EpicenterTranslate.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2015 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.transition;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-
-/**
- * EpicenterTranslate captures the {@link View#getTranslationX()} and
- * {@link View#getTranslationY()} before and after the scene change and
- * animates between those and the epicenter's center during a visibility
- * transition.
- */
-public class EpicenterTranslate extends Visibility {
- private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
- private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
- private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
- private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
- private static final String PROPNAME_Z = "android:epicenterReveal:z";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final TimeInterpolator mInterpolatorZ;
-
- public EpicenterTranslate() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mInterpolatorZ = null;
- }
-
- public EpicenterTranslate(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterTranslate, 0, 0);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0);
- if (interpolatorZ != 0) {
- mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
- } else {
- mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- super.captureStartValues(transitionValues);
- captureValues(transitionValues);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- super.captureEndValues(transitionValues);
- captureValues(transitionValues);
- }
-
- private void captureValues(TransitionValues values) {
- final View view = values.view;
- if (view.getVisibility() == View.GONE) {
- return;
- }
-
- final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
- values.values.put(PROPNAME_BOUNDS, bounds);
- values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
- values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
- values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
- values.values.put(PROPNAME_Z, view.getZ());
- }
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (endValues == null) {
- return null;
- }
-
- final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect start = getEpicenterOrCenter(end);
- final float startX = start.centerX() - end.centerX();
- final float startY = start.centerY() - end.centerY();
- final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
-
- // Translate the view to be centered on the epicenter.
- view.setTranslationX(startX);
- view.setTranslationY(startY);
- view.setTranslationZ(startZ);
-
- final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (startValues == null) {
- return null;
- }
-
- final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect end = getEpicenterOrCenter(start);
- final float endX = end.centerX() - start.centerX();
- final float endY = end.centerY() - start.centerY();
- final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
-
- final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- private static Animator createAnimator(final View view, float startX, float startY,
- float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) {
- final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
- if (interpolatorZ != null) {
- animZ.setInterpolator(interpolatorZ);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY, animZ);
- return animSet;
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
new file mode 100644
index 0000000..9d62079
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2015 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.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.TypeEvaluator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
+/**
+ * EpicenterTranslateClipReveal captures the clip bounds and translation values
+ * before and after the scene change and animates between those and the
+ * epicenter bounds during a visibility transition.
+ */
+public class EpicenterTranslateClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+ private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
+ private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
+ private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
+ private static final String PROPNAME_Z = "android:epicenterReveal:z";
+
+ private final TimeInterpolator mInterpolatorX;
+ private final TimeInterpolator mInterpolatorY;
+ private final TimeInterpolator mInterpolatorZ;
+
+ public EpicenterTranslateClipReveal() {
+ mInterpolatorX = null;
+ mInterpolatorY = null;
+ mInterpolatorZ = null;
+ }
+
+ public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.EpicenterTranslateClipReveal, 0, 0);
+
+ final int interpolatorX = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0);
+ if (interpolatorX != 0) {
+ mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
+ } else {
+ mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
+ }
+
+ final int interpolatorY = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0);
+ if (interpolatorY != 0) {
+ mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
+ } else {
+ mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ final int interpolatorZ = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0);
+ if (interpolatorZ != 0) {
+ mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
+ } else {
+ mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ a.recycle();
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues values) {
+ final View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
+ values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
+ values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
+ values.values.put(PROPNAME_Z, view.getZ());
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect startBounds = getEpicenterOrCenter(endBounds);
+ final float startX = startBounds.centerX() - endBounds.centerX();
+ final float startY = startBounds.centerY() - endBounds.centerY();
+ final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
+
+ // Translate the view to be centered on the epicenter.
+ view.setTranslationX(startX);
+ view.setTranslationY(startY);
+ view.setTranslationZ(startZ);
+
+ final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect endClip = getBestRect(endValues);
+ final Rect startClip = getEpicenterOrCenter(endClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect endBounds = getEpicenterOrCenter(startBounds);
+ final float endX = endBounds.centerX() - startBounds.centerX();
+ final float endY = endBounds.centerY() - startBounds.centerY();
+ final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
+
+ final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect startClip = getBestRect(startValues);
+ final Rect endClip = getEpicenterOrCenter(startClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ private Rect getEpicenterOrCenter(Rect bestRect) {
+ final Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ /*
+ // Translate the clip bounds to be centered within the target bounds.
+ final int offsetX = bestRect.centerX() - epicenter.centerX();
+ final int offsetY = bestRect.centerY() - epicenter.centerY();
+ epicenter.offset(offsetX, offsetY);
+ */
+ return epicenter;
+ }
+
+ final int centerX = bestRect.centerX();
+ final int centerY = bestRect.centerY();
+ return new Rect(centerX, centerY, centerX, centerY);
+ }
+
+ private Rect getBestRect(TransitionValues values) {
+ final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+ if (clipRect == null) {
+ return (Rect) values.values.get(PROPNAME_BOUNDS);
+ }
+ return clipRect;
+ }
+
+ private static Animator createRectAnimator(final View view, State startX, State startY,
+ float startZ, State endX, State endY, float endZ, TransitionValues endValues,
+ TimeInterpolator interpolatorX, TimeInterpolator interpolatorY,
+ TimeInterpolator interpolatorZ) {
+ final StateEvaluator evaluator = new StateEvaluator();
+
+ final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
+ if (interpolatorZ != null) {
+ animZ.setInterpolator(interpolatorZ);
+ }
+
+ final StateProperty propX = new StateProperty(StateProperty.TARGET_X);
+ final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX);
+ if (interpolatorX != null) {
+ animX.setInterpolator(interpolatorX);
+ }
+
+ final StateProperty propY = new StateProperty(StateProperty.TARGET_Y);
+ final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY);
+ if (interpolatorY != null) {
+ animY.setInterpolator(interpolatorY);
+ }
+
+ final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+ final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(terminalClip);
+ }
+ };
+
+ final AnimatorSet animSet = new AnimatorSet();
+ animSet.playTogether(animX, animY, animZ);
+ animSet.addListener(animatorListener);
+ return animSet;
+ }
+
+ private static class State {
+ int lower;
+ int upper;
+ float trans;
+
+ public State() {}
+
+ public State(int lower, int upper, float trans) {
+ this.lower = lower;
+ this.upper = upper;
+ this.trans = trans;
+ }
+ }
+
+ private static class StateEvaluator implements TypeEvaluator<State> {
+ private final State mTemp = new State();
+
+ @Override
+ public State evaluate(float fraction, State startValue, State endValue) {
+ mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction);
+ mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction);
+ mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction);
+ return mTemp;
+ }
+ }
+
+ private static class StateProperty extends Property<View, State> {
+ public static final char TARGET_X = 'x';
+ public static final char TARGET_Y = 'y';
+
+ private final Rect mTempRect = new Rect();
+ private final State mTempState = new State();
+
+ private final int mTargetDimension;
+
+ public StateProperty(char targetDimension) {
+ super(State.class, "state_" + targetDimension);
+
+ mTargetDimension = targetDimension;
+ }
+
+ @Override
+ public State get(View object) {
+ final Rect tempRect = mTempRect;
+ if (!object.getClipBounds(tempRect)) {
+ tempRect.setEmpty();
+ }
+ final State tempState = mTempState;
+ if (mTargetDimension == TARGET_X) {
+ tempState.trans = object.getTranslationX();
+ tempState.lower = tempRect.left + (int) tempState.trans;
+ tempState.upper = tempRect.right + (int) tempState.trans;
+ } else {
+ tempState.trans = object.getTranslationY();
+ tempState.lower = tempRect.top + (int) tempState.trans;
+ tempState.upper = tempRect.bottom + (int) tempState.trans;
+ }
+ return tempState;
+ }
+
+ @Override
+ public void set(View object, State value) {
+ final Rect tempRect = mTempRect;
+ if (object.getClipBounds(tempRect)) {
+ if (mTargetDimension == TARGET_X) {
+ tempRect.left = value.lower - (int) value.trans;
+ tempRect.right = value.upper - (int) value.trans;
+ } else {
+ tempRect.top = value.lower - (int) value.trans;
+ tempRect.bottom = value.upper - (int) value.trans;
+ }
+ object.setClipBounds(tempRect);
+ }
+
+ if (mTargetDimension == TARGET_X) {
+ object.setTranslationX(value.trans);
+ } else {
+ object.setTranslationY(value.trans);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 34f62ba..bd0e6ce 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -462,10 +462,8 @@ public class AsyncChannel {
} catch(Exception e) {
}
// Tell source we're disconnected.
- if (mSrcHandler != null) {
- replyDisconnected(STATUS_SUCCESSFUL);
- mSrcHandler = null;
- }
+ replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
@@ -871,6 +869,8 @@ public class AsyncChannel {
* @param status to be stored in msg.arg1
*/
private void replyDisconnected(int status) {
+ // Can't reply if already disconnected. Avoid NullPointerException.
+ if (mSrcHandler == null) return;
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
msg.arg1 = status;
msg.obj = this;
diff --git a/core/java/com/android/internal/util/CallbackRegistry.java b/core/java/com/android/internal/util/CallbackRegistry.java
new file mode 100644
index 0000000..0f228d4
--- /dev/null
+++ b/core/java/com/android/internal/util/CallbackRegistry.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2015 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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks callbacks for the event. This class supports reentrant modification
+ * of the callbacks during notification without adversely disrupting notifications.
+ * A common pattern for callbacks is to receive a notification and then remove
+ * themselves. This class handles this behavior with constant memory under
+ * most circumstances.
+ *
+ * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to
+ * the constructor to define how notifications should be called. That implementation
+ * does the actual notification on the listener.</p>
+ *
+ * <p>This class supports only callbacks with at most two parameters.
+ * Typically, these are the notification originator and a parameter, but these may
+ * be used as required. If more than two parameters are required or primitive types
+ * must be used, <code>A</code> should be some kind of containing structure that
+ * the subclass may reuse between notifications.</p>
+ *
+ * @param <C> The callback type.
+ * @param <T> The notification sender type. Typically this is the containing class.
+ * @param <A> Opaque argument used to pass additional data beyond an int.
+ */
+public class CallbackRegistry<C, T, A> implements Cloneable {
+ private static final String TAG = "CallbackRegistry";
+
+ /** An ordered collection of listeners waiting to be notified. */
+ private List<C> mCallbacks = new ArrayList<C>();
+
+ /**
+ * A bit flag for the first 64 listeners that are removed during notification.
+ * The lowest significant bit corresponds to the 0th index into mCallbacks.
+ * For a small number of callbacks, no additional array of objects needs to
+ * be allocated.
+ */
+ private long mFirst64Removed = 0x0;
+
+ /**
+ * Bit flags for the remaining callbacks that are removed during notification.
+ * When there are more than 64 callbacks and one is marked for removal, a dynamic
+ * array of bits are allocated for the callbacks.
+ */
+ private long[] mRemainderRemoved;
+
+ /**
+ * The reentrancy level of the notification. When we notify a callback, it may cause
+ * further notifications. The reentrancy level must be tracked to let us clean up
+ * the callback state when all notifications have been processed.
+ */
+ private int mNotificationLevel;
+
+ /** The notification mechanism for notifying an event. */
+ private final NotifierCallback<C, T, A> mNotifier;
+
+ /**
+ * Creates an EventRegistry that notifies the event with notifier.
+ * @param notifier The class to use to notify events.
+ */
+ public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
+ mNotifier = notifier;
+ }
+
+ /**
+ * Notify all callbacks.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
+ mNotificationLevel++;
+ notifyRecurseLocked(sender, arg, arg2);
+ mNotificationLevel--;
+ if (mNotificationLevel == 0) {
+ if (mRemainderRemoved != null) {
+ for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
+ final long removedBits = mRemainderRemoved[i];
+ if (removedBits != 0) {
+ removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
+ mRemainderRemoved[i] = 0;
+ }
+ }
+ }
+ if (mFirst64Removed != 0) {
+ removeRemovedCallbacks(0, mFirst64Removed);
+ mFirst64Removed = 0;
+ }
+ }
+ }
+
+ /**
+ * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ private void notifyFirst64Locked(T sender, int arg, A arg2) {
+ final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
+ notifyCallbacksLocked(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
+ }
+
+ /**
+ * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
+ * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
+ * removal before it recurses into {@link #notifyRemainderLocked(Object, int, A, int)}.
+ * <p>
+ * Recursion is used to avoid allocating temporary state on the heap. Each stack has one
+ * long (64 callbacks) worth of information of which has been removed.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ private void notifyRecurseLocked(T sender, int arg, A arg2) {
+ final int callbackCount = mCallbacks.size();
+ final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
+
+ // Now we've got all callbacks that have no mRemainderRemoved value, so notify the
+ // others.
+ notifyRemainderLocked(sender, arg, arg2, remainderIndex);
+
+ // notifyRemainderLocked notifies all at maxIndex, so we'd normally start at maxIndex + 1
+ // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
+ final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
+
+ // The remaining have no bit set
+ notifyCallbacksLocked(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
+ }
+
+ /**
+ * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
+ * remainderIndex is -1, the first 64 will be notified instead.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param remainderIndex The index into mRemainderRemoved that should be notified.
+ */
+ private void notifyRemainderLocked(T sender, int arg, A arg2, int remainderIndex) {
+ if (remainderIndex < 0) {
+ notifyFirst64Locked(sender, arg, arg2);
+ } else {
+ final long bits = mRemainderRemoved[remainderIndex];
+ final int startIndex = (remainderIndex + 1) * Long.SIZE;
+ final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
+ notifyRemainderLocked(sender, arg, arg2, remainderIndex - 1);
+ notifyCallbacksLocked(sender, arg, arg2, startIndex, endIndex, bits);
+ }
+ }
+
+ /**
+ * Notify callbacks from startIndex to endIndex, using bits as the bit status
+ * for whether they have been removed or not. bits should be from mRemainderRemoved or
+ * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
+ * endIndex should be notified.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param startIndex The index into the mCallbacks to start notifying.
+ * @param endIndex One past the last index into mCallbacks to notify.
+ * @param bits A bit field indicating which callbacks have been removed and shouldn't
+ * be notified.
+ */
+ private void notifyCallbacksLocked(T sender, int arg, A arg2, final int startIndex,
+ final int endIndex, final long bits) {
+ long bitMask = 1;
+ for (int i = startIndex; i < endIndex; i++) {
+ if ((bits & bitMask) == 0) {
+ mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
+ }
+ bitMask <<= 1;
+ }
+ }
+
+ /**
+ * Add a callback to be notified. If the callback is already in the list, another won't
+ * be added. This does not affect current notifications.
+ * @param callback The callback to add.
+ */
+ public synchronized void add(C callback) {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index < 0 || isRemovedLocked(index)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Returns true if the callback at index has been marked for removal.
+ *
+ * @param index The index into mCallbacks to check.
+ * @return true if the callback at index has been marked for removal.
+ */
+ private boolean isRemovedLocked(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ return (mFirst64Removed & bitMask) != 0;
+ } else if (mRemainderRemoved == null) {
+ // It is after the first 64 callbacks, but nothing else was marked for removal.
+ return false;
+ } else {
+ final int maskIndex = (index / Long.SIZE) - 1;
+ if (maskIndex >= mRemainderRemoved.length) {
+ // There are some items in mRemainderRemoved, but nothing at the given index.
+ return false;
+ } else {
+ // There is something marked for removal, so we have to check the bit.
+ final long bits = mRemainderRemoved[maskIndex];
+ final long bitMask = 1L << (index % Long.SIZE);
+ return (bits & bitMask) != 0;
+ }
+ }
+ }
+
+ /**
+ * Removes callbacks from startIndex to startIndex + Long.SIZE, based
+ * on the bits set in removed.
+ * @param startIndex The index into the mCallbacks to start removing callbacks.
+ * @param removed The bits indicating removal, where each bit is set for one callback
+ * to be removed.
+ */
+ private void removeRemovedCallbacks(int startIndex, long removed) {
+ // The naive approach should be fine. There may be a better bit-twiddling approach.
+ final int endIndex = startIndex + Long.SIZE;
+
+ long bitMask = 1L << (Long.SIZE - 1);
+ for (int i = endIndex - 1; i >= startIndex; i--) {
+ if ((removed & bitMask) != 0) {
+ mCallbacks.remove(i);
+ }
+ bitMask >>>= 1;
+ }
+ }
+
+ /**
+ * Remove a callback. This callback won't be notified after this call completes.
+ * @param callback The callback to remove.
+ */
+ public synchronized void remove(C callback) {
+ if (mNotificationLevel == 0) {
+ mCallbacks.remove(callback);
+ } else {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index >= 0) {
+ setRemovalBitLocked(index);
+ }
+ }
+ }
+
+ private void setRemovalBitLocked(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ mFirst64Removed |= bitMask;
+ } else {
+ final int remainderIndex = (index / Long.SIZE) - 1;
+ if (mRemainderRemoved == null) {
+ mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
+ } else if (mRemainderRemoved.length < remainderIndex) {
+ // need to make it bigger
+ long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
+ System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
+ mRemainderRemoved = newRemainders;
+ }
+ final long bitMask = 1L << (index % Long.SIZE);
+ mRemainderRemoved[remainderIndex] |= bitMask;
+ }
+ }
+
+ /**
+ * Makes a copy of the registered callbacks and returns it.
+ *
+ * @return a copy of the registered callbacks.
+ */
+ public synchronized ArrayList<C> copyListeners() {
+ ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ callbacks.add(mCallbacks.get(i));
+ }
+ }
+ return callbacks;
+ }
+
+ /**
+ * Returns true if there are no registered callbacks or false otherwise.
+ *
+ * @return true if there are no registered callbacks or false otherwise.
+ */
+ public synchronized boolean isEmpty() {
+ if (mCallbacks.isEmpty()) {
+ return true;
+ } else if (mNotificationLevel == 0) {
+ return false;
+ } else {
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Removes all callbacks from the list.
+ */
+ public synchronized void clear() {
+ if (mNotificationLevel == 0) {
+ mCallbacks.clear();
+ } else if (!mCallbacks.isEmpty()) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ setRemovalBitLocked(i);
+ }
+ }
+ }
+
+ public synchronized CallbackRegistry<C, T, A> clone() {
+ CallbackRegistry<C, T, A> clone = null;
+ try {
+ clone = (CallbackRegistry<C, T, A>) super.clone();
+ clone.mFirst64Removed = 0;
+ clone.mRemainderRemoved = null;
+ clone.mNotificationLevel = 0;
+ clone.mCallbacks = new ArrayList<C>();
+ final int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ clone.mCallbacks.add(mCallbacks.get(i));
+ }
+ }
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ return clone;
+ }
+
+ /**
+ * Class used to notify events from CallbackRegistry.
+ *
+ * @param <C> The callback type.
+ * @param <T> The notification sender type. Typically this is the containing class.
+ * @param <A> An opaque argument to pass to the notifier
+ */
+ public abstract static class NotifierCallback<C, T, A> {
+ /**
+ * Used to notify the callback.
+ *
+ * @param callback The callback to notify.
+ * @param sender The opaque sender object.
+ * @param arg The opaque notification parameter.
+ * @param arg2 An opaque argument passed in
+ * {@link CallbackRegistry#notifyCallbacks}
+ * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
+ */
+ public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 6f104dd..60c5e42 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -59,7 +59,8 @@ interface IInputMethodManager {
int controlFlags, int softInputMode, int windowFlags,
in EditorInfo attribute, IInputContext inputContext);
- void showInputMethodPickerFromClient(in IInputMethodClient client);
+ void showInputMethodPickerFromClient(in IInputMethodClient client,
+ int auxiliarySubtypeMode);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
void setInputMethod(in IBinder token, String id);
void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bbdd860..5448214 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -132,6 +132,7 @@ LOCAL_SRC_FILES:= \
android_media_AudioRecord.cpp \
android_media_AudioSystem.cpp \
android_media_AudioTrack.cpp \
+ android_media_DeviceCallback.cpp \
android_media_JetPlayer.cpp \
android_media_RemoteDisplay.cpp \
android_media_ToneGenerator.cpp \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8ae2e3b..9babe82 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -188,18 +188,43 @@ size_t Bitmap::rowBytes() const {
return mPixelRef->rowBytes();
}
-SkPixelRef* Bitmap::pixelRef() const {
+SkPixelRef* Bitmap::peekAtPixelRef() const {
assertValid();
return mPixelRef.get();
}
+SkPixelRef* Bitmap::refPixelRef() {
+ assertValid();
+ android::AutoMutex _lock(mLock);
+ return refPixelRefLocked();
+}
+
+SkPixelRef* Bitmap::refPixelRefLocked() {
+ mPixelRef->ref();
+ if (mPixelRef->unique()) {
+ // We just restored this from 0, pin the pixels and inc the strong count
+ // Note that there *might be* an incoming onStrongRefDestroyed from whatever
+ // last unref'd
+ pinPixelsLocked();
+ mPinnedRefCount++;
+ }
+ return mPixelRef.get();
+}
+
void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes,
SkColorTable* ctable) {
+ {
+ android::AutoMutex _lock(mLock);
+ if (mPinnedRefCount) {
+ ALOGW("Called reconfigure on a bitmap that is in use! This may"
+ " cause graphical corruption!");
+ }
+ }
mPixelRef->reconfigure(info, rowBytes, ctable);
}
void Bitmap::reconfigure(const SkImageInfo& info) {
- mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable());
+ reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable());
}
void Bitmap::detachFromJava() {
@@ -287,18 +312,10 @@ void Bitmap::unpinPixelsLocked() {
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
assertValid();
android::AutoMutex _lock(mLock);
- mPixelRef->ref();
- if (mPixelRef->unique()) {
- // We just restored this from 0, pin the pixels and inc the strong count
- // Note that there *might be* an incoming onStrongRefDestroyed from whatever
- // last unref'd
- pinPixelsLocked();
- mPinnedRefCount++;
- }
// Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes()
// would require locking the pixels first.
outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
- outBitmap->setPixelRef(mPixelRef.get())->unref();
+ outBitmap->setPixelRef(refPixelRefLocked())->unref();
outBitmap->setHasHardwareMipMap(hasHardwareMipMap());
}
@@ -323,7 +340,7 @@ public:
}
void* pixels() {
- return mBitmap->pixelRef()->pixels();
+ return mBitmap->peekAtPixelRef()->pixels();
}
bool valid() {
@@ -780,7 +797,7 @@ static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- return static_cast<jint>(bitmap->pixelRef()->getGenerationID());
+ return static_cast<jint>(bitmap->peekAtPixelRef()->getGenerationID());
}
static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -800,10 +817,10 @@ static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle,
jboolean hasAlpha, jboolean requestPremul) {
LocalScopedBitmap bitmap(bitmapHandle);
if (hasAlpha) {
- bitmap->pixelRef()->changeAlphaType(
+ bitmap->peekAtPixelRef()->changeAlphaType(
requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
} else {
- bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kOpaque_SkAlphaType);
}
}
@@ -812,9 +829,9 @@ static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
LocalScopedBitmap bitmap(bitmapHandle);
if (!bitmap->info().isOpaque()) {
if (isPremul) {
- bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kPremul_SkAlphaType);
} else {
- bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kUnpremul_SkAlphaType);
}
}
}
@@ -1164,7 +1181,7 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr;
+ SkPixelRef* pixelRef = bitmap.valid() ? bitmap->peekAtPixelRef() : nullptr;
SkSafeRef(pixelRef);
return reinterpret_cast<jlong>(pixelRef);
}
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index d6e5c61..efeb898 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -62,7 +62,8 @@ public:
int width() const { return info().width(); }
int height() const { return info().height(); }
size_t rowBytes() const;
- SkPixelRef* pixelRef() const;
+ SkPixelRef* peekAtPixelRef() const;
+ SkPixelRef* refPixelRef();
bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; }
void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
@@ -88,6 +89,7 @@ private:
JNIEnv* jniEnv();
bool shouldDisposeSelfLocked();
void assertValid() const;
+ SkPixelRef* refPixelRefLocked();
android::Mutex mLock;
int mPinnedRefCount = 0;
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index cdd397d..3ca4e72 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -184,7 +184,7 @@ public:
}
mBitmap->reconfigure(info, bitmap->rowBytes(), ctable);
- bitmap->setPixelRef(mBitmap->pixelRef());
+ bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
// since we're already allocated, we lockPixels right away
// HeapAllocator/JavaPixelAllocator behaves this way too
@@ -258,7 +258,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
- if (reuseBitmap->pixelRef()->isImmutable()) {
+ if (reuseBitmap->peekAtPixelRef()->isImmutable()) {
ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
javaBitmap = NULL;
reuseBitmap = nullptr;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 0deb8cc..1c6f7de 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -352,8 +352,8 @@ void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap)
getBitmap(env, bitmap)->getSkBitmap(outBitmap);
}
-SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) {
- return getBitmap(env, bitmap)->pixelRef();
+SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) {
+ return getBitmap(env, bitmap)->refPixelRef();
}
SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index e748bac..ef9c2a9 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -52,7 +52,7 @@ public:
static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap);
static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap);
- static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap);
+ static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
// Given the 'native' long held by the Rasterizer.java object, return a
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 8b65fd1..87b81d5 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -30,6 +30,7 @@
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
+#include "android_media_DeviceCallback.h"
// ----------------------------------------------------------------------------
@@ -44,6 +45,7 @@ struct audio_record_fields_t {
jmethodID postNativeEventInJava; //... event post callback method
jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
+ jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance
};
struct audio_attributes_fields_t {
jfieldID fieldRecSource; // AudioAttributes.mSource
@@ -120,6 +122,33 @@ static void recorderCallback(int event, void* user, void *info) {
}
}
+static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
+{
+ Mutex::Autolock l(sLock);
+ JNIDeviceCallback* const cb =
+ (JNIDeviceCallback*)env->GetLongField(thiz,
+ javaAudioRecordFields.nativeDeviceCallback);
+ return sp<JNIDeviceCallback>(cb);
+}
+
+static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
+ jobject thiz,
+ const sp<JNIDeviceCallback>& cb)
+{
+ Mutex::Autolock l(sLock);
+ sp<JNIDeviceCallback> old =
+ (JNIDeviceCallback*)env->GetLongField(thiz,
+ javaAudioRecordFields.nativeDeviceCallback);
+ if (cb.get()) {
+ cb->incStrong((void*)setJniDeviceCallback);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setJniDeviceCallback);
+ }
+ env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
+ return old;
+}
+
// ----------------------------------------------------------------------------
static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
{
@@ -593,9 +622,63 @@ static jboolean android_media_AudioRecord_setInputDevice(
JNIEnv *env, jobject thiz, jint device_id) {
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == 0) {
+ return 0;
+ }
return lpRecorder->setInputDevice(device_id) == NO_ERROR;
}
+static jint android_media_AudioRecord_getRoutedDeviceId(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == 0) {
+ return 0;
+ }
+ return (jint)lpRecorder->getRoutedDeviceId();
+}
+
+static void android_media_AudioRecord_enableDeviceCallback(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == 0) {
+ return;
+ }
+ sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
+ if (cb != 0) {
+ return;
+ }
+ audiorecord_callback_cookie *cookie =
+ (audiorecord_callback_cookie *)env->GetLongField(thiz,
+ javaAudioRecordFields.nativeCallbackCookie);
+ if (cookie == NULL) {
+ return;
+ }
+
+ cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
+ javaAudioRecordFields.postNativeEventInJava);
+ status_t status = lpRecorder->addAudioDeviceCallback(cb);
+ if (status == NO_ERROR) {
+ setJniDeviceCallback(env, thiz, cb);
+ }
+}
+
+static void android_media_AudioRecord_disableDeviceCallback(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == 0) {
+ return;
+ }
+ sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
+ if (cb != 0) {
+ lpRecorder->removeAudioDeviceCallback(cb);
+ }
+}
+
+
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -628,12 +711,17 @@ static JNINativeMethod gMethods[] = {
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
{"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
+ {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
+ {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
+ {"native_disableDeviceCallback", "()V",
+ (void *)android_media_AudioRecord_disableDeviceCallback},
};
// field names found in android/media/AudioRecord.java
#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
+#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback"
// ----------------------------------------------------------------------------
int register_android_media_AudioRecord(JNIEnv *env)
@@ -641,6 +729,7 @@ int register_android_media_AudioRecord(JNIEnv *env)
javaAudioRecordFields.postNativeEventInJava = NULL;
javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
javaAudioRecordFields.nativeCallbackCookie = NULL;
+ javaAudioRecordFields.nativeDeviceCallback = NULL;
// Get the AudioRecord class
@@ -658,6 +747,9 @@ int register_android_media_AudioRecord(JNIEnv *env)
javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
+ javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
+ audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
+
// Get the AudioAttributes class and fields
jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3655adc..eab5668 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -937,7 +937,8 @@ static jint convertAudioPortFromNative(JNIEnv *env,
} else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
ALOGV("convertAudioPortFromNative is a mix");
*jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
- jHandle, nAudioPort->role, jDeviceName,
+ jHandle, nAudioPort->ext.mix.handle,
+ nAudioPort->role, jDeviceName,
jSamplingRates, jChannelMasks,
jFormats, jGains);
} else {
@@ -1670,7 +1671,7 @@ int register_android_media_AudioSystem(JNIEnv *env)
jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
- "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
+ "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain");
gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 26b82c5..662ecd3 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -36,6 +36,7 @@
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_PlaybackSettings.h"
+#include "android_media_DeviceCallback.h"
// ----------------------------------------------------------------------------
@@ -79,6 +80,7 @@ class AudioTrackJniStorage {
sp<MemoryHeapBase> mMemHeap;
sp<MemoryBase> mMemBase;
audiotrack_callback_cookie mCallbackData;
+ sp<JNIDeviceCallback> mDeviceCallback;
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
@@ -977,6 +979,51 @@ static jboolean android_media_AudioTrack_setOutputDevice(
return lpTrack->setOutputDevice(device_id) == NO_ERROR;
}
+static jint android_media_AudioTrack_getRoutedDeviceId(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ return 0;
+ }
+ return (jint)lpTrack->getRoutedDeviceId();
+}
+
+static void android_media_AudioTrack_enableDeviceCallback(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ return;
+ }
+ AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
+ thiz, javaAudioTrackFields.jniData);
+ if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
+ return;
+ }
+ pJniStorage->mDeviceCallback =
+ new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
+ javaAudioTrackFields.postNativeEventInJava);
+ lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
+}
+
+static void android_media_AudioTrack_disableDeviceCallback(
+ JNIEnv *env, jobject thiz) {
+
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ return;
+ }
+ AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
+ thiz, javaAudioTrackFields.jniData);
+ if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
+ return;
+ }
+ lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
+ pJniStorage->mDeviceCallback.clear();
+}
+
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -1030,6 +1077,9 @@ static JNINativeMethod gMethods[] = {
"(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
{"native_setOutputDevice", "(I)Z",
(void *)android_media_AudioTrack_setOutputDevice},
+ {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
+ {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
+ {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
};
diff --git a/core/jni/android_media_DeviceCallback.cpp b/core/jni/android_media_DeviceCallback.cpp
new file mode 100644
index 0000000..e159373
--- /dev/null
+++ b/core/jni/android_media_DeviceCallback.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 "AudioDeviceCallback-JNI"
+
+#include <utils/Log.h>
+#include <JNIHelp.h>
+#include <JniConstants.h>
+#include "core_jni_helpers.h"
+#include <media/AudioSystem.h>
+
+#include "android_media_DeviceCallback.h"
+
+
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+JNIDeviceCallback::JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz,
+ jmethodID postEventFromNative)
+{
+
+ // Hold onto the AudioTrack/AudioRecord class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the AudioTrack/AudioRecord object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+
+ mPostEventFromNative = postEventFromNative;
+}
+
+JNIDeviceCallback::~JNIDeviceCallback()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ return;
+ }
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ return;
+ }
+
+ ALOGV("%s audioIo %d deviceId %d", __FUNCTION__, audioIo, deviceId);
+ env->CallStaticVoidMethod(mClass,
+ mPostEventFromNative,
+ mObject,
+ AUDIO_NATIVE_EVENT_ROUTING_CHANGE, deviceId, 0, NULL);
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ env->ExceptionClear();
+ }
+}
+
diff --git a/core/jni/android_media_DeviceCallback.h b/core/jni/android_media_DeviceCallback.h
new file mode 100644
index 0000000..7ae788e
--- /dev/null
+++ b/core/jni/android_media_DeviceCallback.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_MEDIA_DEVICE_CALLBACK_H
+#define ANDROID_MEDIA_DEVICE_CALLBACK_H
+
+#include <system/audio.h>
+#include <media/AudioSystem.h>
+
+namespace android {
+
+// keep in sync with AudioSystem.java
+#define AUDIO_NATIVE_EVENT_ROUTING_CHANGE 1000
+
+class JNIDeviceCallback: public AudioSystem::AudioDeviceCallback
+{
+public:
+ JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, jmethodID postEventFromNative);
+ ~JNIDeviceCallback();
+
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId);
+
+private:
+ void sendEvent(int event);
+
+ jclass mClass; // Reference to AudioTrack/AudioRecord class
+ jobject mObject; // Weak ref to AudioTrack/AudioRecord Java object to call on
+ jmethodID mPostEventFromNative; // postEventFromNative method ID.
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_DEVICE_CALLBACK_H
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1965cd3..77af341 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -180,7 +180,7 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
(void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
screenshotInfo, rowBytes, nullptr);
screenshot.detach();
- bitmap->pixelRef()->setImmutable();
+ bitmap->peekAtPixelRef()->setImmutable();
return GraphicsJNI::createBitmap(env, bitmap,
GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e3930cd..5669b91 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2352,11 +2352,6 @@
<permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a {@link android.media.routing.MediaRouteService},
- to ensure that only the system can bind to it. -->
- <permission android:name="android.permission.BIND_MEDIA_ROUTE_SERVICE"
- android:protectionLevel="signature" />
-
<!-- Must be required by an {@link android.service.dreams.DreamService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_DREAM_SERVICE"
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 89c3749..4b544d2 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -16,7 +16,6 @@
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -27,8 +26,7 @@
android:layout_column="0"
android:layout_row="0"
android:layout_rowSpan="3"
- android:layout_gravity="center|fill"
- tools:background="@color/accent_material_light" />
+ android:layout_gravity="center|fill" />
<RelativeLayout
android:layout_width="wrap_content"
@@ -56,20 +54,14 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
@@ -80,10 +72,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
</LinearLayout>
<!-- The layout alignment of this view will switch between toRightOf
@@ -106,10 +95,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
<CheckedTextView
android:id="@+id/pm_label"
@@ -121,10 +107,7 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 2150341..a4388f6 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -16,17 +16,13 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/date_picker_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="18dp"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
- android:orientation="vertical"
- tools:background="@color/accent_material_light"
- tools:paddingStart="24dp"
- tools:paddingEnd="24dp">
+ android:orientation="vertical">
<!-- Top padding should stay on this view so that
the touch target is a bit larger. -->
@@ -35,10 +31,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
- android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
- tools:text="2015"
- tools:textSize="@dimen/date_picker_year_label_size"
- tools:textColor="@color/white" />
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
<TextView
android:id="@+id/date_picker_header_date"
@@ -47,9 +40,6 @@
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:gravity="start"
android:maxLines="2"
- android:ellipsize="none"
- tools:text="Thu, Sep 30"
- tools:textSize="@dimen/date_picker_date_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index be9e443..3f5e300 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -18,13 +18,11 @@
<!-- This layout is duplicated in land/time_picker_material.xml, so any
changes made here need to be manually copied over. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/time_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:padding="@dimen/timepicker_separator_padding"
- tools:background="@color/accent_material_light">
+ android:padding="@dimen/timepicker_separator_padding">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -37,10 +35,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
@@ -50,10 +45,7 @@
android:layout_marginRight="@dimen/timepicker_separator_padding"
android:layout_centerInParent="true"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -66,10 +58,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
<!-- The layout alignment of this view will switch between toRightOf
@id/minutes and toLeftOf @id/hours depending on the locale. -->
@@ -90,10 +79,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
<CheckedTextView
android:id="@+id/pm_label"
android:layout_width="wrap_content"
@@ -103,9 +89,6 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
index 38c41f0..c4c8dac 100644
--- a/core/res/res/transition/popup_window_enter.xml
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -16,17 +16,14 @@
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
- <!-- Start from location of epicenter, move to popup location. -->
- <transition
- class="com.android.internal.transition.EpicenterTranslate"
- android:duration="300" />
-
<!-- Start from size of epicenter, expand to full width/height. -->
<transition
- class="com.android.internal.transition.EpicenterClipReveal"
- android:centerClipBounds="true"
- android:duration="300" />
+ class="com.android.internal.transition.EpicenterTranslateClipReveal"
+ android:duration="250" />
<!-- Quickly fade in. -->
- <fade android:duration="100" />
+ <fade
+ android:duration="100"
+ android:fromAlpha="0.1"
+ android:toAlpha="1.0" />
</transitionSet>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1c4b5f7..eaa6278 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5877,16 +5877,9 @@
</declare-styleable>
<!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterClipReveal">
- <attr name="centerClipBounds" format="boolean" />
+ <declare-styleable name="EpicenterTranslateClipReveal">
<attr name="interpolatorX" format="reference" />
<attr name="interpolatorY" format="reference" />
- </declare-styleable>
-
- <!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterTranslate">
- <attr name="interpolatorX" />
- <attr name="interpolatorY" />
<attr name="interpolatorZ" format="reference" />
</declare-styleable>
diff --git a/core/tests/coretests/src/android/util/FloatMathTest.java b/core/tests/coretests/src/android/util/FloatMathTest.java
deleted file mode 100644
index f479e2b..0000000
--- a/core/tests/coretests/src/android/util/FloatMathTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-public class FloatMathTest extends TestCase {
-
- @SmallTest
- public void testSqrt() {
- assertEquals(7, FloatMath.sqrt(49), 0);
- assertEquals(10, FloatMath.sqrt(100), 0);
- assertEquals(0, FloatMath.sqrt(0), 0);
- assertEquals(1, FloatMath.sqrt(1), 0);
- }
-
- @SmallTest
- public void testFloor() {
- assertEquals(78, FloatMath.floor(78.89f), 0);
- assertEquals(-79, FloatMath.floor(-78.89f), 0);
- }
-
- @SmallTest
- public void testCeil() {
- assertEquals(79, FloatMath.ceil(78.89f), 0);
- assertEquals(-78, FloatMath.ceil(-78.89f), 0);
- }
-
- @SmallTest
- public void testSin() {
- assertEquals(0.0, FloatMath.sin(0), 0);
- assertEquals(0.8414709848078965f, FloatMath.sin(1), 0);
- }
-
- @SmallTest
- public void testCos() {
- assertEquals(1.0f, FloatMath.cos(0), 0);
- assertEquals(0.5403023058681398f, FloatMath.cos(1), 0);
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java
new file mode 100644
index 0000000..c53f4cc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2015 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 junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class CallbackRegistryTest extends TestCase {
+
+ final Integer callback1 = 1;
+ final Integer callback2 = 2;
+ final Integer callback3 = 3;
+ CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry;
+ int notify1;
+ int notify2;
+ int notify3;
+ int[] deepNotifyCount = new int[300];
+ Integer argValue;
+
+ private void addNotifyCount(Integer callback) {
+ if (callback == callback1) {
+ notify1++;
+ } else if (callback == callback2) {
+ notify2++;
+ } else if (callback == callback3) {
+ notify3++;
+ }
+ deepNotifyCount[callback]++;
+ }
+
+ public void testAddListener() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ Integer callback = 0;
+
+ assertNotNull(registry.copyListeners());
+ assertEquals(0, registry.copyListeners().size());
+
+ registry.add(callback);
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+
+ registry.add(callback);
+ callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+
+ Integer otherListener = 1;
+ registry.add(otherListener);
+ callbacks = registry.copyListeners();
+ assertEquals(2, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+ assertEquals(otherListener, callbacks.get(1));
+
+ registry.remove(callback);
+ registry.add(callback);
+ callbacks = registry.copyListeners();
+ assertEquals(2, callbacks.size());
+ assertEquals(callback, callbacks.get(1));
+ assertEquals(otherListener, callbacks.get(0));
+ }
+
+ public void testSimpleNotify() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ assertEquals(arg1, (int) arg);
+ addNotifyCount(callback);
+ argValue = arg;
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback2);
+ Integer arg = 1;
+ registry.notifyCallbacks(this, arg, arg);
+ assertEquals(arg, argValue);
+ assertEquals(1, notify2);
+ }
+
+ public void testRemoveWhileNotifying() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ if (callback == callback1) {
+ registry.remove(callback1);
+ registry.remove(callback2);
+ }
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+ assertEquals(1, notify1);
+ assertEquals(1, notify2);
+ assertEquals(1, notify3);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback3, callbacks.get(0));
+ }
+
+ public void testDeepRemoveWhileNotifying() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.remove(callback);
+ registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+ assertEquals(1, notify1);
+ assertEquals(2, notify2);
+ assertEquals(3, notify3);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(0, callbacks.size());
+ }
+
+ public void testAddRemovedListener() {
+
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ if (callback == callback1) {
+ registry.remove(callback2);
+ } else if (callback == callback3) {
+ registry.add(callback2);
+ }
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(3, callbacks.size());
+ assertEquals(callback1, callbacks.get(0));
+ assertEquals(callback3, callbacks.get(1));
+ assertEquals(callback2, callbacks.get(2));
+ assertEquals(1, notify1);
+ assertEquals(1, notify2);
+ assertEquals(1, notify3);
+ }
+
+ public void testVeryDeepRemoveWhileNotifying() {
+ final Integer[] callbacks = new Integer[deepNotifyCount.length];
+ for (int i = 0; i < callbacks.length; i++) {
+ callbacks[i] = i;
+ }
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.remove(callback);
+ registry.remove(callbacks[callbacks.length - callback - 1]);
+ registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < callbacks.length; i++) {
+ registry.add(callbacks[i]);
+ }
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ int expectedCount = Math.min(i + 1, deepNotifyCount.length - i);
+ assertEquals(expectedCount, deepNotifyCount[i]);
+ }
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+ }
+
+ public void testClear() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ registry.add(i);
+ }
+ registry.clear();
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ assertEquals(0, deepNotifyCount[i]);
+ }
+ }
+
+ public void testNestedClear() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.clear();
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ registry.add(i);
+ }
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ assertEquals(1, deepNotifyCount[i]);
+ }
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+ }
+
+ public void testIsEmpty() throws Exception {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ Integer callback = 0;
+
+ assertTrue(registry.isEmpty());
+ registry.add(callback);
+ assertFalse(registry.isEmpty());
+ }
+
+ public void testClone() throws Exception {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+ assertTrue(registry.isEmpty());
+ CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry2 = registry.clone();
+ Integer callback = 0;
+ registry.add(callback);
+ assertFalse(registry.isEmpty());
+ assertTrue(registry2.isEmpty());
+ registry2 = registry.clone();
+ assertFalse(registry2.isEmpty());
+ }
+}
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index b28f978..8538671 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -1221,6 +1221,14 @@ var RESOURCE_COLLECTIONS = {
"https://support.google.com/googleplay/answer/2651410"
]
},
+ "preview/landing/resources": {
+ "title": "",
+ "resources": [
+ "preview/api-overview.html",
+ "preview/setup-sdk.html",
+ "preview/samples.html"
+ ]
+ },
"autolanding": {
"title": "",
"resources": [
diff --git a/docs/html/jd_tag_helpers.js b/docs/html/jd_tag_helpers.js
index 7538e4d..f03b1d7 100644
--- a/docs/html/jd_tag_helpers.js
+++ b/docs/html/jd_tag_helpers.js
@@ -13,6 +13,7 @@ var ALL_RESOURCES = mergeArrays(
GOOGLE_RESOURCES,
GUIDE_RESOURCES,
SAMPLES_RESOURCES,
+ PREVIEW_RESOURCES,
TOOLS_RESOURCES,
TRAINING_RESOURCES,
YOUTUBE_RESOURCES,
@@ -70,6 +71,7 @@ var ALL_RESOURCES_BY_TYPE = {
'google': GOOGLE_RESOURCES,
'guide': GUIDE_RESOURCES,
'samples': SAMPLES_RESOURCES,
+ 'preview': PREVIEW_RESOURCES,
'tools': TOOLS_RESOURCES,
'training': TRAINING_RESOURCES,
'youtube': YOUTUBE_RESOURCES,
@@ -86,6 +88,7 @@ var ALL_RESOURCES_BY_TAG = mergeMaps(
{map:GOOGLE_BY_TAG,arr:GOOGLE_RESOURCES},
{map:GUIDE_BY_TAG,arr:GUIDE_RESOURCES},
{map:SAMPLES_BY_TAG,arr:SAMPLES_RESOURCES},
+ {map:PREVIEW_BY_TAG,arr:PREVIEW_RESOURCES},
{map:TOOLS_BY_TAG,arr:TOOLS_RESOURCES},
{map:TRAINING_BY_TAG,arr:TRAINING_RESOURCES},
{map:YOUTUBE_BY_TAG,arr:YOUTUBE_RESOURCES},
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index f72ffbb..dde3c7b 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -1,5 +1,5 @@
page.title=API Overview
-excludeFromSuggestions=true
+page.keywords=preview,sdk,compatibility
sdk.platform.apiLevel=22
@jd:body
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
new file mode 100644
index 0000000..a2d0b18
--- /dev/null
+++ b/docs/html/preview/index.jd
@@ -0,0 +1,60 @@
+page.title=M Developer Preview
+page.tags=preview
+meta.tags="preview"
+fullpage=true
+page.viewport_width=970
+section.landing=true
+header.hide=1
+footer.hide=1
+@jd:body
+
+<section class="dac-expand dac-hero dac-light">
+ <div class="wrap">
+ <div class="cols dac-hero-content">
+ <div class="col-1of2 col-push-1of2 dac-hero-figure">
+ <img class="dac-hero-image" src="/design/media/hero-material-design.png">
+ </div>
+ <div class="col-1of2 col-pull-1of2">
+ <h1 class="dac-hero-title">M Developer Preview</h1>
+ <p class="dac-hero-description">
+ Get ready for the next official release of the platform. Test your apps
+ and give us feedback!
+ </p>
+ <a class="dac-hero-cta" href="{@docRoot}preview/setup-sdk.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Set up the Preview SDK
+ </a><br>
+ <a class="dac-hero-cta" href="{@docRoot}preview/api-overview.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Review the API changes
+ </a><br>
+ <a class="dac-hero-cta" href="https://code.google.com/p/android-developer-preview/">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Report issues
+ </a><br>
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="dac-section dac-gray dac-small dac-invert"><div class="wrap">
+ <h2 class="norule">Latest</h2>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:develop/landing/latest"
+ data-cardSizes="6x6"
+ data-maxResults="3"></div>
+</div></section>
+
+
+<section class="dac-section"><div class="wrap">
+ <h1 class="dac-section-title">Resources</h1>
+ <div class="dac-section-subtitle">
+ Check out these resources to help you get started with the M Developer Preview.
+ </div>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:preview/landing/resources"
+ data-cardSizes="6x6"
+ data-maxResults="6"></div>
+</div></section>
+
+
diff --git a/docs/html/preview/overview.jd b/docs/html/preview/overview.jd
new file mode 100644
index 0000000..00f1cfe
--- /dev/null
+++ b/docs/html/preview/overview.jd
@@ -0,0 +1,7 @@
+page.title=Preview Program Overview
+
+@jd:body
+
+<p>
+ This is an overview of the program. Bacon.
+</p> \ No newline at end of file
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index bea4914..fbf73f6 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -1,6 +1,11 @@
<ul id="nav">
<li class="nav-section">
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/overview.html">
+ Program Overview</a></div>
+ </li>
+
+ <li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/setup-sdk.html">
Set up the SDK</a></div>
</li>
@@ -29,9 +34,4 @@
License Agreement</a></div>
</li>
- <li class="nav-section" style="margin: 20px 0 0 -10px;">
- <div class="nav-section-header empty"><a href="<?cs var:toroot ?>index.html" class="back-link">
- Developer Home</a></div>
- </li>
-
</ul>
diff --git a/docs/html/preview/reference.jd b/docs/html/preview/reference.jd
index ee1f24d..2d30c62 100644
--- a/docs/html/preview/reference.jd
+++ b/docs/html/preview/reference.jd
@@ -9,7 +9,7 @@ page.title=Reference
<ul>
<li>
- <a href="http://storage.googleapis.com/androiddevelopers/preview/l-developer-preview-reference.zip">
+ <a href="http://storage.googleapis.com/androiddevelopers/preview/m-developer-preview-reference.zip">
M Developer Preview reference</a>
</li>
</ul> \ No newline at end of file
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 3b25ba6..3f29c6a 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -54,13 +54,13 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
public static class RSA extends AndroidKeyPairGenerator {
public RSA() {
- super("RSA");
+ super(KeyStoreKeyProperties.Algorithm.RSA);
}
}
public static class EC extends AndroidKeyPairGenerator {
public EC() {
- super("EC");
+ super(KeyStoreKeyProperties.Algorithm.EC);
}
}
@@ -83,15 +83,15 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
private android.security.KeyStore mKeyStore;
private KeyPairGeneratorSpec mSpec;
- private String mKeyAlgorithm;
+ private @KeyStoreKeyProperties.AlgorithmEnum String mKeyAlgorithm;
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGenerator(String algorithm) {
+ protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
- public String getAlgorithm() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getAlgorithm() {
return mAlgorithm;
}
@@ -197,7 +197,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
return certGen.generate(privateKey);
}
- private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
+ private @KeyStoreKeyProperties.AlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
String result = spec.getKeyType();
if (result != null) {
return result;
@@ -248,10 +248,11 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
}
}
- private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
- if ("RSA".equalsIgnoreCase(algorithm)) {
+ private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(algorithm)) {
return "sha256WithRSA";
- } else if ("EC".equalsIgnoreCase(algorithm)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(algorithm)) {
return "sha256WithECDSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + algorithm);
@@ -287,7 +288,7 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
}
KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
- String keyAlgorithm = getKeyAlgorithm(spec);
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec);
int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
if (keyType == -1) {
throw new InvalidAlgorithmParameterException(
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 72cb062..e82ff6a 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -128,10 +128,11 @@ public class AndroidKeyStore extends KeyStoreSpi {
keymasterDigest = keymasterDigests.get(0);
}
- String keyAlgorithmString;
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmString;
try {
- keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
- keymasterAlgorithm, keymasterDigest);
+ keyAlgorithmString =
+ KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -451,10 +452,10 @@ public class AndroidKeyStore extends KeyStoreSpi {
int keymasterAlgorithm;
int keymasterDigest;
try {
- keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
+ keymasterAlgorithm = KeyStoreKeyProperties.Algorithm.toKeymasterSecretKeyAlgorithm(
keyAlgorithmString);
keymasterDigest =
- KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
+ KeyStoreKeyProperties.Algorithm.toKeymasterDigest(keyAlgorithmString);
} catch (IllegalArgumentException e) {
throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
}
@@ -465,8 +466,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
int[] keymasterDigests;
if (params.isDigestsSpecified()) {
// Digest(s) specified in parameters
- keymasterDigests =
- KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
+ keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests());
if (keymasterDigest != -1) {
// Digest also specified in the JCA key algorithm name.
if (!com.android.internal.util.ArrayUtils.contains(
@@ -494,8 +494,8 @@ public class AndroidKeyStore extends KeyStoreSpi {
}
@KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
- int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
- params.getBlockModes());
+ int[] keymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
@@ -503,8 +503,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
- + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
- keymasterBlockMode)
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ ". See KeyStoreParameter documentation.");
}
}
@@ -513,11 +512,11 @@ public class AndroidKeyStore extends KeyStoreSpi {
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- int[] keymasterPaddings = ArrayUtils.concat(
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
- params.getEncryptionPaddings()),
- KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
- params.getSignaturePaddings()));
+ if (params.getSignaturePaddings().length > 0) {
+ throw new KeyStoreException("Signature paddings not supported for symmetric keys");
+ }
+ int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ params.getEncryptionPaddings());
args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
params.getContext(),
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index e9c24dd..8e27dc3 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -262,7 +262,8 @@ public final class KeyChain {
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers, String host, int port, String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String alias) {
choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
}
@@ -306,9 +307,8 @@ public final class KeyChain {
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers,
- String host, int port, String url,
- String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String url, String alias) {
/*
* TODO currently keyTypes, issuers are unused. They are meant
* to follow the semantics and purpose of X509KeyManager
@@ -431,9 +431,11 @@ public final class KeyChain {
* specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
* "RSA").
*/
- public static boolean isKeyAlgorithmSupported(String algorithm) {
+ public static boolean isKeyAlgorithmSupported(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
final String algUpper = algorithm.toUpperCase(Locale.US);
- return "EC".equals(algUpper) || "RSA".equals(algUpper);
+ return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper)
+ || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper);
}
/**
@@ -443,7 +445,8 @@ public final class KeyChain {
* hardware support that can be used to bind keys to the device in a way
* that makes it non-exportable.
*/
- public static boolean isBoundKeyAlgorithm(String algorithm) {
+ public static boolean isBoundKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
if (!isKeyAlgorithmSupported(algorithm)) {
return false;
}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 8f135a6..729646d 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -48,8 +48,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -63,8 +63,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -160,14 +160,14 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -195,10 +195,12 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -220,8 +222,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -346,7 +348,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for keys which are used for encryption/decryption.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -357,7 +360,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for encryption/decryption keys.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -425,7 +428,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index d6d3789..9dde386 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -85,13 +85,13 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mDigests;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private final String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
@@ -138,10 +138,10 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] digests,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -246,7 +246,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Returns the key type (e.g., "EC", "RSA") specified by this parameter.
*/
- public String getKeyType() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() {
return mKeyType;
}
@@ -352,28 +352,28 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding schemes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -403,14 +403,14 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
}
/**
- * Gets the duration of time (seconds) for which the private key can be used after the user
- * is successfully authenticated.
+ * Gets the duration of time (seconds) for which this key can be used after the user is
+ * successfully authenticated. This has effect only if user authentication is required.
*
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
@@ -468,13 +468,13 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mDigests;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
@@ -511,7 +511,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
/**
* Sets the key type (e.g., EC, RSA) of the keypair to be created.
*/
- public Builder setKeyType(String keyType) throws NoSuchAlgorithmException {
+ public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType)
+ throws NoSuchAlgorithmException {
if (keyType == null) {
throw new NullPointerException("keyType == null");
} else {
@@ -691,7 +692,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for keys which are used for signing/verification.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -703,7 +704,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for keys which are used for encryption/decryption.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -715,7 +717,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for RSA keys which are used for signing/verification.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -726,7 +729,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for encryption/decryption keys.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -788,7 +791,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 82d328b..304d277 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -115,10 +115,10 @@ public class KeyStore {
return mToken;
}
- static int getKeyTypeForAlgorithm(String keyType) {
- if ("RSA".equalsIgnoreCase(keyType)) {
+ static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.AlgorithmEnum String keyType) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_RSA;
- } else if ("EC".equalsIgnoreCase(keyType)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index ed450b8..bd601bc 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -495,7 +495,8 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
}
if ((mIv != null) && (mIv.length > 0)) {
try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+ AlgorithmParameters params =
+ AlgorithmParameters.getInstance(KeyStoreKeyProperties.Algorithm.AES);
params.init(new IvParameterSpec(mIv));
return params;
} catch (NoSuchAlgorithmException e) {
@@ -634,10 +635,9 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
if ((mIv == null) && (mEncrypting)) {
// IV was not provided by the caller and thus will be generated by keymaster.
// Mix in some additional entropy from the provided SecureRandom.
- if (mRng != null) {
- mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
- mRng.nextBytes(mAdditionalEntropyForBegin);
- }
+ mAdditionalEntropyForBegin =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, mBlockSizeBytes);
}
}
}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
index e5933ad..311278b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
@@ -21,6 +21,7 @@ import android.security.keymaster.KeymasterDefs;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.SecureRandom;
/**
* Assorted utility methods for implementing crypto operations on top of KeyStore.
@@ -28,6 +29,9 @@ import java.security.InvalidKeyException;
* @hide
*/
abstract class KeyStoreCryptoOperationUtils {
+
+ private static volatile SecureRandom sRng;
+
private KeyStoreCryptoOperationUtils() {}
/**
@@ -81,4 +85,28 @@ abstract class KeyStoreCryptoOperationUtils {
// General cases
return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode);
}
+
+ /**
+ * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
+ *
+ * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
+ * RNG.
+ */
+ static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+ if (rng == null) {
+ rng = getRng();
+ }
+ byte[] result = new byte[sizeBytes];
+ rng.nextBytes(result);
+ return result;
+ }
+
+ private static SecureRandom getRng() {
+ // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
+ // required to be thread-safe.
+ if (sRng == null) {
+ sRng = new SecureRandom();
+ }
+ return sRng;
+ }
}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 68b5751..c265c46 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -129,8 +129,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
@KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes();
- int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
- spec.getBlockModes());
+ int[] keymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
@@ -138,8 +138,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
throw new IllegalStateException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
- + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
- keymasterBlockMode)
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ ". See KeyGeneratorSpec documentation.");
}
}
@@ -152,7 +151,7 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
args.addInts(
KeymasterDefs.KM_TAG_PADDING,
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
+ KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings()));
KeymasterUtils.addUserAuthArgs(args,
spec.getContext(),
@@ -174,12 +173,9 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
}
- byte[] additionalEntropy = null;
- SecureRandom rng = mRng;
- if (rng != null) {
- additionalEntropy = new byte[(keySizeBits + 7) / 8];
- rng.nextBytes(additionalEntropy);
- }
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, (keySizeBits + 7) / 8);
int flags = spec.getFlags();
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
@@ -189,8 +185,9 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
throw new IllegalStateException(
"Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
- String keyAlgorithmJCA =
- KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmJCA =
+ KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ mKeymasterAlgorithm, mKeymasterDigest);
return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
}
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index b85ec53..1c3e300 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -17,13 +17,23 @@
package android.security;
import android.annotation.IntDef;
+import android.annotation.StringDef;
import android.security.keymaster.KeymasterDefs;
import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
import java.util.Collection;
+import java.util.Locale;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
/**
* Properties of {@code AndroidKeyStore} keys.
@@ -37,7 +47,7 @@ public abstract class KeyStoreKeyProperties {
public @interface PurposeEnum {}
/**
- * Purpose of key.
+ * Purposes of key.
*/
public static abstract class Purpose {
private Purpose() {}
@@ -122,6 +132,514 @@ public abstract class KeyStoreKeyProperties {
}
@Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Algorithm.RSA,
+ Algorithm.EC,
+ Algorithm.AES,
+ Algorithm.HMAC_SHA1,
+ Algorithm.HMAC_SHA224,
+ Algorithm.HMAC_SHA256,
+ Algorithm.HMAC_SHA384,
+ Algorithm.HMAC_SHA512,
+ })
+ public @interface AlgorithmEnum {}
+
+ /**
+ * Key algorithms.
+ *
+ * <p>These are standard names which can be used to obtain instances of {@link KeyGenerator},
+ * {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac},
+ * {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by
+ * {@link Key#getAlgorithm()}.
+ */
+ public static abstract class Algorithm {
+ private Algorithm() {}
+
+ /** Rivest Shamir Adleman (RSA) key. */
+ public static final String RSA = "RSA";
+
+ /** Elliptic Curve (EC) key. */
+ public static final String EC = "EC";
+
+ /** Advanced Encryption Standard (AES) key. */
+ public static final String AES = "AES";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
+ public static final String HMAC_SHA1 = "HmacSHA1";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
+ public static final String HMAC_SHA224 = "HmacSHA224";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
+ public static final String HMAC_SHA256 = "HmacSHA256";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
+ public static final String HMAC_SHA384 = "HmacSHA384";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
+ public static final String HMAC_SHA512 = "HmacSHA512";
+
+ /**
+ * @hide
+ */
+ static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
+ if (AES.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_AES;
+ } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
+ return KeymasterDefs.KM_ALGORITHM_HMAC;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported secret key algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
+ int keymasterAlgorithm, int keymasterDigest) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_AES:
+ if (keymasterDigest != -1) {
+ throw new IllegalArgumentException("Digest not supported for AES key: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ return AES;
+ case KeymasterDefs.KM_ALGORITHM_HMAC:
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return HMAC_SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return HMAC_SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return HMAC_SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return HMAC_SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return HMAC_SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported HMAC digest: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported algorithm: " + keymasterAlgorithm);
+ }
+ }
+
+ /**
+ * @hide
+ *
+ * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
+ */
+ static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
+ String algorithmUpper = algorithm.toUpperCase(Locale.US);
+ if (algorithmUpper.startsWith("HMAC")) {
+ String digestUpper = algorithmUpper.substring("HMAC".length());
+ switch (digestUpper) {
+ case "SHA1":
+ return KeymasterDefs.KM_DIGEST_SHA1;
+ case "SHA224":
+ return KeymasterDefs.KM_DIGEST_SHA_2_224;
+ case "SHA256":
+ return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ case "SHA384":
+ return KeymasterDefs.KM_DIGEST_SHA_2_384;
+ case "SHA512":
+ return KeymasterDefs.KM_DIGEST_SHA_2_512;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported HMAC digest: " + digestUpper);
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ BlockMode.ECB,
+ BlockMode.CBC,
+ BlockMode.CTR,
+ BlockMode.GCM,
+ })
+ public @interface BlockModeEnum {}
+
+ /**
+ * Block modes that can be used when encrypting/decrypting using a key.
+ */
+ public static abstract class BlockMode {
+ private BlockMode() {}
+
+ /** Electronic Codebook (ECB) block mode. */
+ public static final String ECB = "ECB";
+
+ /** Cipher Block Chaining (CBC) block mode. */
+ public static final String CBC = "CBC";
+
+ /** Counter (CTR) block mode. */
+ public static final String CTR = "CTR";
+
+ /** Galois/Counter Mode (GCM) block mode. */
+ public static final String GCM = "GCM";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@BlockModeEnum String blockMode) {
+ if (ECB.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_ECB;
+ } else if (CBC.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CBC;
+ } else if (CTR.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CTR;
+ } else if (GCM.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_GCM;
+ } else {
+ throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String fromKeymaster(int blockMode) {
+ switch (blockMode) {
+ case KeymasterDefs.KM_MODE_ECB:
+ return ECB;
+ case KeymasterDefs.KM_MODE_CBC:
+ return CBC;
+ case KeymasterDefs.KM_MODE_CTR:
+ return CTR;
+ case KeymasterDefs.KM_MODE_GCM:
+ return GCM;
+ default:
+ throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
+ if ((blockModes == null) || (blockModes.isEmpty())) {
+ return EmptyArray.STRING;
+ }
+ @BlockModeEnum String[] result = new String[blockModes.size()];
+ int offset = 0;
+ for (int blockMode : blockModes) {
+ result[offset] = fromKeymaster(blockMode);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
+ if ((blockModes == null) || (blockModes.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[blockModes.length];
+ for (int i = 0; i < blockModes.length; i++) {
+ result[i] = toKeymaster(blockModes[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ EncryptionPadding.NONE,
+ EncryptionPadding.PKCS7,
+ EncryptionPadding.RSA_PKCS1,
+ EncryptionPadding.RSA_OAEP,
+ })
+ public @interface EncryptionPaddingEnum {}
+
+ /**
+ * Padding schemes for encryption/decryption.
+ */
+ public static abstract class EncryptionPadding {
+ private EncryptionPadding() {}
+
+ /**
+ * No padding.
+ */
+ public static final String NONE = "NoPadding";
+
+ /**
+ * PKCS#7 padding.
+ */
+ public static final String PKCS7 = "PKCS7Padding";
+
+ /**
+ * RSA PKCS#1 v1.5 padding for encryption/decryption.
+ */
+ public static final String RSA_PKCS1 = "PKCS1Padding";
+
+ /**
+ * RSA Optimal Asymmetric Encryption Padding (OAEP).
+ */
+ public static final String RSA_OAEP = "OAEPPadding";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@EncryptionPaddingEnum String padding) {
+ if (NONE.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_NONE;
+ } else if (PKCS7.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_PKCS7;
+ } else if (RSA_PKCS1.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+ } else if (RSA_OAEP.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_OAEP;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @EncryptionPaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return NONE;
+ case KeymasterDefs.KM_PAD_PKCS7:
+ return PKCS7;
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ return RSA_OAEP;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ SignaturePadding.RSA_PKCS1,
+ SignaturePadding.RSA_PSS,
+ })
+ public @interface SignaturePaddingEnum {}
+
+ /**
+ * Padding schemes for signing/verification.
+ */
+ public static abstract class SignaturePadding {
+ private SignaturePadding() {}
+
+ /**
+ * RSA PKCS#1 v1.5 padding for signatures.
+ */
+ public static final String RSA_PKCS1 = "PKCS1";
+
+ /**
+ * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
+ */
+ public static final String RSA_PSS = "PSS";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@SignaturePaddingEnum String padding) {
+ switch (padding.toUpperCase(Locale.US)) {
+ case RSA_PKCS1:
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
+ case RSA_PSS:
+ return KeymasterDefs.KM_PAD_RSA_PSS;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported signature padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @SignaturePaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_PSS:
+ return RSA_PSS;
+ default:
+ throw new IllegalArgumentException("Unsupported signature padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Digest.NONE,
+ Digest.MD5,
+ Digest.SHA1,
+ Digest.SHA224,
+ Digest.SHA256,
+ Digest.SHA384,
+ Digest.SHA512,
+ })
+ public @interface DigestEnum {}
+
+ /**
+ * Digests that can be used with a key when signing or generating Message Authentication
+ * Codes (MACs).
+ */
+ public static abstract class Digest {
+ private Digest() {}
+
+ /**
+ * No digest: sign/authenticate the raw message.
+ */
+ public static final String NONE = "NONE";
+
+ /**
+ * MD5 digest.
+ */
+ public static final String MD5 = "MD5";
+
+ /**
+ * SHA-1 digest.
+ */
+ public static final String SHA1 = "SHA-1";
+
+ /**
+ * SHA-2 224 (aka SHA-224) digest.
+ */
+ public static final String SHA224 = "SHA-224";
+
+ /**
+ * SHA-2 256 (aka SHA-256) digest.
+ */
+ public static final String SHA256 = "SHA-256";
+
+ /**
+ * SHA-2 384 (aka SHA-384) digest.
+ */
+ public static final String SHA384 = "SHA-384";
+
+ /**
+ * SHA-2 512 (aka SHA-512) digest.
+ */
+ public static final String SHA512 = "SHA-512";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@DigestEnum String digest) {
+ switch (digest.toUpperCase(Locale.US)) {
+ case SHA1:
+ return KeymasterDefs.KM_DIGEST_SHA1;
+ case SHA224:
+ return KeymasterDefs.KM_DIGEST_SHA_2_224;
+ case SHA256:
+ return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ case SHA384:
+ return KeymasterDefs.KM_DIGEST_SHA_2_384;
+ case SHA512:
+ return KeymasterDefs.KM_DIGEST_SHA_2_512;
+ case NONE:
+ return KeymasterDefs.KM_DIGEST_NONE;
+ case MD5:
+ return KeymasterDefs.KM_DIGEST_MD5;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String fromKeymaster(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return NONE;
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return MD5;
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
+ if (digests.isEmpty()) {
+ return EmptyArray.STRING;
+ }
+ String[] result = new String[digests.size()];
+ int offset = 0;
+ for (int digest : digests) {
+ result[offset] = fromKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@DigestEnum String[] digests) {
+ if ((digests == null) || (digests.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[digests.length];
+ int offset = 0;
+ for (@DigestEnum String digest : digests) {
+ result[offset] = toKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
public @interface OriginEnum {}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index 96d58d8..7533bdc 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -32,10 +32,10 @@ public class KeyStoreKeySpec implements KeySpec {
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
- private final String[] mDigests;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserAuthenticationRequirementTeeEnforced;
@@ -51,10 +51,10 @@ public class KeyStoreKeySpec implements KeySpec {
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
boolean userAuthenticationRequirementTeeEnforced) {
@@ -143,28 +143,28 @@ public class KeyStoreKeySpec implements KeySpec {
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
/**
* Gets the set of padding modes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding modes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
@@ -179,10 +179,10 @@ public class KeyStoreKeySpec implements KeySpec {
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index b4747e9..46d60f4 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -45,10 +45,10 @@ public final class KeyStoreParameter implements ProtectionParameter {
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
- private final String[] mDigests;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -60,10 +60,10 @@ public final class KeyStoreParameter implements ProtectionParameter {
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -151,7 +151,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
@@ -159,7 +159,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
* Gets the set of padding schemes with which the key can be used when signing or verifying
* signatures.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
@@ -170,7 +170,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* @see #isDigestsSpecified()
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
if (mDigests == null) {
throw new IllegalStateException("Digests not specified");
}
@@ -190,7 +190,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -218,10 +218,12 @@ public final class KeyStoreParameter implements ProtectionParameter {
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -251,10 +253,10 @@ public final class KeyStoreParameter implements ProtectionParameter {
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
- private String[] mDigests;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -356,7 +358,8 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* <p>This must be specified for keys which are used for encryption/decryption.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -368,7 +371,8 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* <p>This must be specified for RSA keys which are used for signing/verification.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -381,7 +385,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
* <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For
* asymmetric signing keys this constraint must be specified.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -392,7 +396,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* <p>This must be specified for encryption/decryption keys.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -462,7 +466,7 @@ public final class KeyStoreParameter implements ProtectionParameter {
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index bfe09e3..ff79b7a 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -79,8 +79,8 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
int keySize;
@KeyStoreKeyProperties.PurposeEnum int purposes;
String[] encryptionPaddings;
- String[] digests;
- String[] blockModes;
+ @KeyStoreKeyProperties.DigestEnum String[] digests;
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes;
int keymasterSwEnforcedUserAuthenticators;
int keymasterHwEnforcedUserAuthenticators;
try {
@@ -105,10 +105,10 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
List<String> encryptionPaddingsList = new ArrayList<String>();
for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
- String jcaPadding;
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding;
try {
- jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding(
- keymasterPadding);
+ jcaPadding =
+ KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException(
"Unsupported encryption padding: " + keymasterPadding);
@@ -118,9 +118,9 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
encryptionPaddings =
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
- digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests(
+ digests = KeyStoreKeyProperties.Digest.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
- blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes(
+ blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
keymasterSwEnforcedUserAuthenticators =
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index aa44ecd..df67ae7 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -21,11 +21,6 @@ import android.hardware.fingerprint.FingerprintManager;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
-import libcore.util.EmptyArray;
-
-import java.util.Collection;
-import java.util.Locale;
-
/**
* @hide
*/
@@ -33,152 +28,6 @@ public abstract class KeymasterUtils {
private KeymasterUtils() {}
- public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
- if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) {
- return KeymasterDefs.KM_ALGORITHM_AES;
- } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
- return KeymasterDefs.KM_ALGORITHM_HMAC;
- } else {
- throw new IllegalArgumentException(
- "Unsupported secret key algorithm: " + jcaKeyAlgorithm);
- }
- }
-
- public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_AES:
- if (keymasterDigest != -1) {
- throw new IllegalArgumentException(
- "Digest not supported for AES key: " + keymasterDigest);
- }
- return "AES";
- case KeymasterDefs.KM_ALGORITHM_HMAC:
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_SHA1:
- return "HmacSHA1";
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- return "HmacSHA224";
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- return "HmacSHA256";
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- return "HmacSHA384";
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- return "HmacSHA512";
- default:
- throw new IllegalArgumentException(
- "Unsupported HMAC digest: " + keymasterDigest);
- }
- default:
- throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_RSA:
- return "RSA";
- case KeymasterDefs.KM_ALGORITHM_EC:
- return "EC";
- default:
- throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
- String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US);
- if (algorithmUpper.startsWith("HMAC")) {
- String digestUpper = algorithmUpper.substring("HMAC".length());
- switch (digestUpper) {
- case "MD5":
- return KeymasterDefs.KM_DIGEST_MD5;
- case "SHA1":
- return KeymasterDefs.KM_DIGEST_SHA1;
- case "SHA224":
- return KeymasterDefs.KM_DIGEST_SHA_2_224;
- case "SHA256":
- return KeymasterDefs.KM_DIGEST_SHA_2_256;
- case "SHA384":
- return KeymasterDefs.KM_DIGEST_SHA_2_384;
- case "SHA512":
- return KeymasterDefs.KM_DIGEST_SHA_2_512;
- default:
- throw new IllegalArgumentException("Unsupported HMAC digest: " + digestUpper);
- }
- } else {
- return -1;
- }
- }
-
- public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) {
- if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) {
- return KeymasterDefs.KM_DIGEST_SHA1;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_224;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_256;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_384;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_512;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) {
- return KeymasterDefs.KM_DIGEST_NONE;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) {
- return KeymasterDefs.KM_DIGEST_MD5;
- } else {
- throw new IllegalArgumentException(
- "Unsupported digest algorithm: " + jcaDigestAlgorithm);
- }
- }
-
- public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) {
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_NONE:
- return "NONE";
- case KeymasterDefs.KM_DIGEST_MD5:
- return "MD5";
- case KeymasterDefs.KM_DIGEST_SHA1:
- return "SHA-1";
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- return "SHA-224";
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- return "SHA-256";
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- return "SHA-384";
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- return "SHA-512";
- default:
- throw new IllegalArgumentException(
- "Unsupported digest algorithm: " + keymasterDigest);
- }
- }
-
- public static String[] getJcaDigestAlgorithmsFromKeymasterDigests(
- Collection<Integer> keymasterDigests) {
- if (keymasterDigests.isEmpty()) {
- return EmptyArray.STRING;
- }
- String[] result = new String[keymasterDigests.size()];
- int offset = 0;
- for (int keymasterDigest : keymasterDigests) {
- result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest);
- offset++;
- }
- return result;
- }
-
- public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) {
- if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaDigestAlgorithms.length];
- int offset = 0;
- for (String jcaDigestAlgorithm : jcaDigestAlgorithms) {
- result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm);
- offset++;
- }
- return result;
- }
-
public static int getDigestOutputSizeBits(int keymasterDigest) {
switch (keymasterDigest) {
case KeymasterDefs.KM_DIGEST_NONE:
@@ -200,60 +49,6 @@ public abstract class KeymasterUtils {
}
}
- public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) {
- if ("ECB".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_ECB;
- } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_CBC;
- } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_CTR;
- } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_GCM;
- } else {
- throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode);
- }
- }
-
- public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) {
- switch (keymasterBlockMode) {
- case KeymasterDefs.KM_MODE_ECB:
- return "ECB";
- case KeymasterDefs.KM_MODE_CBC:
- return "CBC";
- case KeymasterDefs.KM_MODE_CTR:
- return "CTR";
- case KeymasterDefs.KM_MODE_GCM:
- return "GCM";
- default:
- throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
- }
- }
-
- public static String[] getJcaBlockModesFromKeymasterBlockModes(
- Collection<Integer> keymasterBlockModes) {
- if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) {
- return EmptyArray.STRING;
- }
- String[] result = new String[keymasterBlockModes.size()];
- int offset = 0;
- for (int keymasterBlockMode : keymasterBlockModes) {
- result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode);
- offset++;
- }
- return result;
- }
-
- public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) {
- if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaBlockModes.length];
- for (int i = 0; i < jcaBlockModes.length; i++) {
- result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]);
- }
- return result;
- }
-
public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
switch (keymasterBlockMode) {
case KeymasterDefs.KM_MODE_ECB:
@@ -267,82 +62,6 @@ public abstract class KeymasterUtils {
}
}
- public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) {
- if ("NoPadding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_NONE;
- } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_PKCS7;
- } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
- } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_OAEP;
- } else {
- throw new IllegalArgumentException(
- "Unsupported encryption padding scheme: " + jcaPadding);
- }
- }
-
- public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) {
- switch (keymasterPadding) {
- case KeymasterDefs.KM_PAD_NONE:
- return "NoPadding";
- case KeymasterDefs.KM_PAD_PKCS7:
- return "PKCS7Padding";
- case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
- return "PKCS1Padding";
- case KeymasterDefs.KM_PAD_RSA_OAEP:
- return "OEAPPadding";
- default:
- throw new IllegalArgumentException(
- "Unsupported encryption padding: " + keymasterPadding);
- }
- }
-
- public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) {
- if ("PKCS#1".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
- } if ("PSS".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PSS;
- } else {
- throw new IllegalArgumentException(
- "Unsupported signature padding scheme: " + jcaPadding);
- }
- }
-
- public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) {
- switch (keymasterPadding) {
- case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
- return "PKCS#1";
- case KeymasterDefs.KM_PAD_RSA_PSS:
- return "PSS";
- default:
- throw new IllegalArgumentException(
- "Unsupported signature padding: " + keymasterPadding);
- }
- }
-
- public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) {
- if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaPaddings.length];
- for (int i = 0; i < jcaPaddings.length; i++) {
- result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]);
- }
- return result;
- }
-
- public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) {
- if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaPaddings.length];
- for (int i = 0; i < jcaPaddings.length; i++) {
- result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]);
- }
- return result;
- }
-
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index aca7c52..2f79c58 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -58,13 +58,20 @@ static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
class TreeContentAnimation {
public:
virtual ~TreeContentAnimation() {}
- virtual int getFrameCount() { return 150; }
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
virtual void doFrame(int frameNr) = 0;
template <class T>
- static void run() {
+ static void run(int frameCount) {
T animation;
+ animation.setFrameCount(frameCount);
TestContext testContext;
@@ -137,9 +144,10 @@ public:
renderer->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(frameNr);
- cards[ci]->mutateStagingProperties().setTranslationY(frameNr);
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
}
@@ -159,6 +167,47 @@ private:
}
};
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+
class RectGridAnimation : public TreeContentAnimation {
public:
sp<RenderNode> card;
@@ -172,8 +221,9 @@ public:
renderer->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
- card->mutateStagingProperties().setTranslationX(frameNr);
- card->mutateStagingProperties().setTranslationY(frameNr);
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
private:
@@ -220,8 +270,9 @@ public:
}
void doFrame(int frameNr) override {
- card->mutateStagingProperties().setTranslationX(frameNr);
- card->mutateStagingProperties().setTranslationY(frameNr);
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
private:
@@ -248,10 +299,11 @@ struct cstr_cmp {
}
};
-typedef void (*testProc)();
+typedef void (*testProc)(int);
std::map<const char*, testProc, cstr_cmp> gTestMap {
{"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
+ {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
{"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
{"oval", TreeContentAnimation::run<OvalAnimation> },
};
@@ -263,7 +315,28 @@ int main(int argc, char* argv[]) {
printf("Error: couldn't find test %s\n", testName);
return 1;
}
- proc();
+ int loopCount = 1;
+ if (argc > 2) {
+ loopCount = atoi(argv[2]);
+ if (!loopCount) {
+ printf("Invalid loop count!\n");
+ return 1;
+ }
+ }
+ int frameCount = 150;
+ if (argc > 3) {
+ frameCount = atoi(argv[3]);
+ if (frameCount < 1) {
+ printf("Invalid frame count!\n");
+ return 1;
+ }
+ }
+ if (loopCount < 0) {
+ loopCount = INT_MAX;
+ }
+ for (int i = 0; i < loopCount; i++) {
+ proc(frameCount);
+ }
printf("Success!\n");
return 0;
}
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 82da27d..c078260 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -83,6 +83,16 @@ public class AudioDevicePort extends AudioPort {
if (o == null || !(o instanceof AudioDevicePort)) {
return false;
}
+ AudioDevicePort other = (AudioDevicePort)o;
+ if (mType != other.type()) {
+ return false;
+ }
+ if (mAddress == null && other.address() != null) {
+ return false;
+ }
+ if (!mAddress.equals(other.address())) {
+ return false;
+ }
return super.equals(o);
}
diff --git a/media/java/android/media/AudioDevicesManager.java b/media/java/android/media/AudioDevicesManager.java
index ca238d7..8b83c17 100644
--- a/media/java/android/media/AudioDevicesManager.java
+++ b/media/java/android/media/AudioDevicesManager.java
@@ -96,7 +96,7 @@ public class AudioDevicesManager {
* @param flags A set of bitflags specifying the criteria to test.
* @see {@link LIST_DEVICES_OUTPUTS} and {@link LIST_DEVICES_INPUTS}
**/
- private boolean checkFlags(AudioDevicePort port, int flags) {
+ private static boolean checkFlags(AudioDevicePort port, int flags) {
return port.role() == AudioPort.ROLE_SINK && (flags & LIST_DEVICES_OUTPUTS) != 0 ||
port.role() == AudioPort.ROLE_SOURCE && (flags & LIST_DEVICES_INPUTS) != 0;
}
@@ -110,8 +110,21 @@ public class AudioDevicesManager {
* @return A (possibly zero-length) array of AudioDeviceInfo objects.
*/
public AudioDeviceInfo[] listDevices(int flags) {
+ return listDevicesStatic(flags);
+ }
+
+ /**
+ * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
+ * connected to the system and meeting the criteria specified in the <code>flags</code>
+ * parameter.
+ * @param flags A set of bitflags specifying the criteria to test.
+ * @see {@link LIST_DEVICES_OUTPUTS}, {@link LIST_DEVICES_INPUTS} and {@link LIST_DEVICES_ALL}.
+ * @return A (possibly zero-length) array of AudioDeviceInfo objects.
+ * @hide
+ */
+ public static AudioDeviceInfo[] listDevicesStatic(int flags) {
ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
- int status = mAudioManager.listAudioDevicePorts(ports);
+ int status = AudioManager.listAudioDevicePorts(ports);
if (status != AudioManager.SUCCESS) {
// fail and bail!
return new AudioDeviceInfo[0];
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6eaf812..19900d0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3401,7 +3401,7 @@ public class AudioManager {
* @param ports An AudioPort ArrayList where the list will be returned.
* @hide
*/
- public int listAudioPorts(ArrayList<AudioPort> ports) {
+ public static int listAudioPorts(ArrayList<AudioPort> ports) {
return updateAudioPortCache(ports, null);
}
@@ -3410,7 +3410,7 @@ public class AudioManager {
* @see listAudioPorts(ArrayList<AudioPort>)
* @hide
*/
- public int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
+ public static int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
int status = updateAudioPortCache(ports, null);
if (status == SUCCESS) {
@@ -3447,7 +3447,7 @@ public class AudioManager {
* patch[0] contains the newly created patch
* @hide
*/
- public int createAudioPatch(AudioPatch[] patch,
+ public static int createAudioPatch(AudioPatch[] patch,
AudioPortConfig[] sources,
AudioPortConfig[] sinks) {
return AudioSystem.createAudioPatch(patch, sources, sinks);
@@ -3464,7 +3464,7 @@ public class AudioManager {
* - {@link #ERROR} if patch cannot be released for any other reason.
* @hide
*/
- public int releaseAudioPatch(AudioPatch patch) {
+ public static int releaseAudioPatch(AudioPatch patch) {
return AudioSystem.releaseAudioPatch(patch);
}
@@ -3473,7 +3473,7 @@ public class AudioManager {
* @param patches An AudioPatch array where the list will be returned.
* @hide
*/
- public int listAudioPatches(ArrayList<AudioPatch> patches) {
+ public static int listAudioPatches(ArrayList<AudioPatch> patches) {
return updateAudioPortCache(null, patches);
}
@@ -3482,7 +3482,7 @@ public class AudioManager {
* AudioGain.buildConfig()
* @hide
*/
- public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
+ public static int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
if (port == null || gain == null) {
return ERROR_BAD_VALUE;
}
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
index 9fac8d1..ab55c8d 100644
--- a/media/java/android/media/AudioMixPort.java
+++ b/media/java/android/media/AudioMixPort.java
@@ -20,16 +20,21 @@ package android.media;
* The AudioMixPort is a specialized type of AudioPort
* describing an audio mix or stream at an input or output stream of the audio
* framework.
+ * In addition to base audio port attributes, the mix descriptor contains:
+ * - the unique audio I/O handle assigned by AudioFlinger to this mix.
* @see AudioPort
* @hide
*/
public class AudioMixPort extends AudioPort {
- AudioMixPort(AudioHandle handle, int role, String deviceName,
+ private final int mIoHandle;
+
+ AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName,
int[] samplingRates, int[] channelMasks,
int[] formats, AudioGain[] gains) {
super(handle, role, deviceName, samplingRates, channelMasks, formats, gains);
+ mIoHandle = ioHandle;
}
/**
@@ -41,11 +46,23 @@ public class AudioMixPort extends AudioPort {
return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain);
}
+ /**
+ * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+ */
+ public int ioHandle() {
+ return mIoHandle;
+ }
+
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof AudioMixPort)) {
return false;
}
+ AudioMixPort other = (AudioMixPort)o;
+ if (mIoHandle != other.ioHandle()) {
+ return false;
+ }
+
return super.equals(o);
}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 88e784a..7328d7a 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -93,6 +93,14 @@ public class AudioPort {
}
/**
+ * Get the system unique device ID.
+ */
+ public int id() {
+ return mHandle.id();
+ }
+
+
+ /**
* Get the audio port role
*/
public int role() {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 472da02..11671d8 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -116,11 +116,6 @@ public class AudioRecord
*/
private static final int NATIVE_EVENT_NEW_POS = 3;
- /**
- * Event id denotes when the routing changes.
- */
- private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
-
private final static String TAG = "android.media.AudioRecord";
/** @hide */
@@ -161,6 +156,12 @@ public class AudioRecord
@SuppressWarnings("unused")
private long mNativeCallbackCookie;
+ /**
+ * Accessed by native methods: provides access to the JNIDeviceCallback instance.
+ */
+ @SuppressWarnings("unused")
+ private long mNativeDeviceCallback;
+
//---------------------------------------------------------
// Member variables
@@ -1205,6 +1206,17 @@ public class AudioRecord
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
*/
public AudioDeviceInfo getRoutedDevice() {
+ int deviceId = native_getRoutedDeviceId();
+ if (deviceId == 0) {
+ return null;
+ }
+ AudioDeviceInfo[] devices =
+ AudioDevicesManager.listDevicesStatic(AudioDevicesManager.LIST_DEVICES_INPUTS);
+ for (int i = 0; i < devices.length; i++) {
+ if (devices[i].getId() == deviceId) {
+ return devices[i];
+ }
+ }
return null;
}
@@ -1224,6 +1236,9 @@ public class AudioRecord
android.os.Handler handler) {
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.size() == 0) {
+ native_enableDeviceCallback();
+ }
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
}
@@ -1238,6 +1253,9 @@ public class AudioRecord
synchronized (mRoutingChangeListeners) {
if (mRoutingChangeListeners.containsKey(listener)) {
mRoutingChangeListeners.remove(listener);
+ if (mRoutingChangeListeners.size() == 0) {
+ native_disableDeviceCallback();
+ }
}
}
}
@@ -1271,7 +1289,7 @@ public class AudioRecord
return;
}
switch(msg.what) {
- case NATIVE_EVENT_ROUTING_CHANGE:
+ case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
if (listener != null) {
listener.onAudioRecordRouting(record);
}
@@ -1299,10 +1317,11 @@ public class AudioRecord
synchronized (mRoutingChangeListeners) {
values = mRoutingChangeListeners.values();
}
+ AudioManager.resetAudioPortGeneration();
for(NativeRoutingEventHandlerDelegate delegate : values) {
Handler handler = delegate.getHandler();
if (handler != null) {
- handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
}
}
}
@@ -1341,10 +1360,14 @@ public class AudioRecord
return false;
}
- mPreferredDevice = deviceInfo;
- int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
-
- return native_setInputDevice(preferredDeviceId);
+ int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0;
+ boolean status = native_setInputDevice(preferredDeviceId);
+ if (status == true) {
+ synchronized (this) {
+ mPreferredDevice = deviceInfo;
+ }
+ }
+ return status;
}
/**
@@ -1352,7 +1375,9 @@ public class AudioRecord
* is not guarenteed to correspond to the actual device being used for recording.
*/
public AudioDeviceInfo getPreferredInputDevice() {
- return mPreferredDevice;
+ synchronized (this) {
+ return mPreferredDevice;
+ }
}
//---------------------------------------------------------
@@ -1435,6 +1460,11 @@ public class AudioRecord
return;
}
+ if (what == AudioSystem.NATIVE_EVENT_ROUTING_CHANGE) {
+ recorder.broadcastRoutingChange();
+ return;
+ }
+
if (recorder.mEventHandler != null) {
Message m =
recorder.mEventHandler.obtainMessage(what, arg1, arg2, obj);
@@ -1486,7 +1516,9 @@ public class AudioRecord
int sampleRateInHz, int channelCount, int audioFormat);
private native final boolean native_setInputDevice(int deviceId);
-
+ private native final int native_getRoutedDeviceId();
+ private native final void native_enableDeviceCallback();
+ private native final void native_disableDeviceCallback();
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3dae543..ee12374 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -721,5 +721,11 @@ public class AudioSystem
(1 << STREAM_RING) |
(1 << STREAM_NOTIFICATION) |
(1 << STREAM_SYSTEM);
+
+ /**
+ * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
+ * Keep in sync with core/jni/android_media_DeviceCallback.h.
+ */
+ final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cb05cc5..a66a1e5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -178,12 +178,6 @@ public class AudioTrack
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
- /**
- * Event id denotes when the routing changes.
- */
- private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
-
-
private final static String TAG = "android.media.AudioTrack";
@@ -2057,11 +2051,14 @@ public class AudioTrack
if (deviceInfo != null && !deviceInfo.isSink()) {
return false;
}
-
- mPreferredDevice = deviceInfo;
- int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
-
- return native_setOutputDevice(preferredDeviceId);
+ int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0;
+ boolean status = native_setOutputDevice(preferredDeviceId);
+ if (status == true) {
+ synchronized (this) {
+ mPreferredDevice = deviceInfo;
+ }
+ }
+ return status;
}
/**
@@ -2069,7 +2066,9 @@ public class AudioTrack
* is not guaranteed to correspond to the actual device being used for playback.
*/
public AudioDeviceInfo getPreferredOutputDevice() {
- return mPreferredDevice;
+ synchronized (this) {
+ return mPreferredDevice;
+ }
}
//--------------------------------------------------------------------------
@@ -2079,6 +2078,17 @@ public class AudioTrack
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
*/
public AudioDeviceInfo getRoutedDevice() {
+ int deviceId = native_getRoutedDeviceId();
+ if (deviceId == 0) {
+ return null;
+ }
+ AudioDeviceInfo[] devices =
+ AudioDevicesManager.listDevicesStatic(AudioDevicesManager.LIST_DEVICES_OUTPUTS);
+ for (int i = 0; i < devices.length; i++) {
+ if (devices[i].getId() == deviceId) {
+ return devices[i];
+ }
+ }
return null;
}
@@ -2098,6 +2108,9 @@ public class AudioTrack
android.os.Handler handler) {
if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.size() == 0) {
+ native_enableDeviceCallback();
+ }
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
}
@@ -2113,6 +2126,9 @@ public class AudioTrack
if (mRoutingChangeListeners.containsKey(listener)) {
mRoutingChangeListeners.remove(listener);
}
+ if (mRoutingChangeListeners.size() == 0) {
+ native_disableDeviceCallback();
+ }
}
}
@@ -2124,10 +2140,11 @@ public class AudioTrack
synchronized (mRoutingChangeListeners) {
values = mRoutingChangeListeners.values();
}
+ AudioManager.resetAudioPortGeneration();
for(NativeRoutingEventHandlerDelegate delegate : values) {
Handler handler = delegate.getHandler();
if (handler != null) {
- handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
}
}
}
@@ -2240,7 +2257,7 @@ public class AudioTrack
return;
}
switch(msg.what) {
- case NATIVE_EVENT_ROUTING_CHANGE:
+ case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
if (listener != null) {
listener.onAudioTrackRouting(track);
}
@@ -2273,6 +2290,10 @@ public class AudioTrack
return;
}
+ if (what == AudioSystem.NATIVE_EVENT_ROUTING_CHANGE) {
+ track.broadcastRoutingChange();
+ return;
+ }
NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
@@ -2281,7 +2302,6 @@ public class AudioTrack
handler.sendMessage(m);
}
}
-
}
@@ -2362,6 +2382,9 @@ public class AudioTrack
private native final int native_setAuxEffectSendLevel(float level);
private native final boolean native_setOutputDevice(int deviceId);
+ private native final int native_getRoutedDeviceId();
+ private native final void native_enableDeviceCallback();
+ private native final void native_disableDeviceCallback();
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/routing/IMediaRouteClientCallback.aidl b/media/java/android/media/routing/IMediaRouteClientCallback.aidl
deleted file mode 100644
index d90ea3b..0000000
--- a/media/java/android/media/routing/IMediaRouteClientCallback.aidl
+++ /dev/null
@@ -1,41 +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.
- */
-
-package android.media.routing;
-
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.ParcelableConnectionInfo;
-import android.media.routing.ParcelableDestinationInfo;
-import android.media.routing.ParcelableRouteInfo;
-import android.os.IBinder;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-oneway interface IMediaRouteClientCallback {
- void onDestinationFound(int seq, in ParcelableDestinationInfo destination,
- in ParcelableRouteInfo[] routes);
-
- void onDestinationLost(int seq, String id);
-
- void onDiscoveryFailed(int seq, int error, in CharSequence message, in Bundle extras);
-
- void onConnected(int seq, in ParcelableConnectionInfo connection);
-
- void onDisconnected(int seq);
-
- void onConnectionFailed(int seq, int error, in CharSequence message, in Bundle extras);
-}
diff --git a/media/java/android/media/routing/IMediaRouteService.aidl b/media/java/android/media/routing/IMediaRouteService.aidl
deleted file mode 100644
index 493ab6d..0000000
--- a/media/java/android/media/routing/IMediaRouteService.aidl
+++ /dev/null
@@ -1,46 +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.
- */
-
-package android.media.routing;
-
-import android.media.routing.IMediaRouteClientCallback;
-import android.media.routing.MediaRouteSelector;
-import android.os.Bundle;
-
-/**
- * Interface to an app's MediaRouteService.
- * @hide
- */
-oneway interface IMediaRouteService {
- void registerClient(int clientUid, String clientPackageName,
- in IMediaRouteClientCallback callback);
-
- void unregisterClient(in IMediaRouteClientCallback callback);
-
- void startDiscovery(in IMediaRouteClientCallback callback, int seq,
- in List<MediaRouteSelector> selectors, int flags);
-
- void stopDiscovery(in IMediaRouteClientCallback callback);
-
- void connect(in IMediaRouteClientCallback callback, int seq,
- String destinationId, String routeId, int flags, in Bundle extras);
-
- void disconnect(in IMediaRouteClientCallback callback);
-
- void pauseStream(in IMediaRouteClientCallback callback);
-
- void resumeStream(in IMediaRouteClientCallback callback);
-}
-
diff --git a/media/java/android/media/routing/IMediaRouter.aidl b/media/java/android/media/routing/IMediaRouter.aidl
deleted file mode 100644
index 0abb258..0000000
--- a/media/java/android/media/routing/IMediaRouter.aidl
+++ /dev/null
@@ -1,22 +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.
- */
-
-package android.media.routing;
-
-/** @hide */
-interface IMediaRouter {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterDelegate.aidl b/media/java/android/media/routing/IMediaRouterDelegate.aidl
deleted file mode 100644
index 35f84c8..0000000
--- a/media/java/android/media/routing/IMediaRouterDelegate.aidl
+++ /dev/null
@@ -1,22 +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.
- */
-
-package android.media.routing;
-
-/** @hide */
-interface IMediaRouterDelegate {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl b/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
deleted file mode 100644
index 173ae55..0000000
--- a/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
+++ /dev/null
@@ -1,22 +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.
- */
-
-package android.media.routing;
-
-/** @hide */
-interface IMediaRouterRoutingCallback {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterStateCallback.aidl b/media/java/android/media/routing/IMediaRouterStateCallback.aidl
deleted file mode 100644
index 0299904..0000000
--- a/media/java/android/media/routing/IMediaRouterStateCallback.aidl
+++ /dev/null
@@ -1,22 +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.
- */
-
-package android.media.routing;
-
-/** @hide */
-interface IMediaRouterStateCallback {
-
-}
-
diff --git a/media/java/android/media/routing/MediaRouteSelector.aidl b/media/java/android/media/routing/MediaRouteSelector.aidl
deleted file mode 100644
index 37bfa4a..0000000
--- a/media/java/android/media/routing/MediaRouteSelector.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.
-*/
-
-package android.media.routing;
-
-parcelable MediaRouteSelector;
diff --git a/media/java/android/media/routing/MediaRouteSelector.java b/media/java/android/media/routing/MediaRouteSelector.java
deleted file mode 100644
index 0bfc796..0000000
--- a/media/java/android/media/routing/MediaRouteSelector.java
+++ /dev/null
@@ -1,356 +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.
- */
-package android.media.routing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.routing.MediaRouter.RouteFeatures;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A media route selector consists of a set of constraints that are used to select
- * the routes to which an application would like to connect. The constraints consist
- * of a set of required or optional features and protocols. The constraints may also
- * require the use of a specific media route service package or additional characteristics
- * that are described by a bundle of extra parameters.
- * <p>
- * The application will typically create several different selectors that express
- * various combinations of characteristics that it would like to use together when
- * it connects to a destination media device. For each destination that is discovered,
- * media route services will publish some number of routes and include information
- * about which selector each route matches. The application will then choose among
- * these routes to determine which best satisfies its desired purpose and connect to it.
- * </p>
- */
-public final class MediaRouteSelector implements Parcelable {
- private final int mRequiredFeatures;
- private final int mOptionalFeatures;
- private final List<String> mRequiredProtocols;
- private final List<String> mOptionalProtocols;
- private final String mServicePackageName;
- private final Bundle mExtras;
-
- MediaRouteSelector(int requiredFeatures, int optionalFeatures,
- List<String> requiredProtocols, List<String> optionalProtocols,
- String servicePackageName, Bundle extras) {
- mRequiredFeatures = requiredFeatures;
- mOptionalFeatures = optionalFeatures;
- mRequiredProtocols = requiredProtocols;
- mOptionalProtocols = optionalProtocols;
- mServicePackageName = servicePackageName;
- mExtras = extras;
- }
-
- /**
- * Gets the set of required route features.
- *
- * @return A set of required route feature flags.
- */
- public @RouteFeatures int getRequiredFeatures() {
- return mRequiredFeatures;
- }
-
- /**
- * Gets the set of optional route features.
- *
- * @return A set of optional route feature flags.
- */
- public @RouteFeatures int getOptionalFeatures() {
- return mOptionalFeatures;
- }
-
- /**
- * Gets the list of route protocols that a route must support in order to be selected.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @return The list of fully qualified route protocol names.
- */
- public @NonNull List<String> getRequiredProtocols() {
- return mRequiredProtocols;
- }
-
- /**
- * Gets the list of optional route protocols that a client may use if they are available.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @return The list of optional fully qualified route protocol names.
- */
- public @NonNull List<String> getOptionalProtocols() {
- return mOptionalProtocols;
- }
-
- /**
- * Returns true if the selector includes a required or optional request for
- * the specified protocol using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return True if the protocol was requested.
- */
- public boolean containsProtocol(@NonNull Class<?> clazz) {
- return containsProtocol(clazz.getName());
- }
-
- /**
- * Returns true if the selector includes a required or optional request for
- * the specified protocol.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The name of the protocol.
- * @return True if the protocol was requested.
- */
- public boolean containsProtocol(@NonNull String name) {
- return mRequiredProtocols.contains(name)
- || mOptionalProtocols.contains(name);
- }
-
- /**
- * Gets the package name of a specific media route service that this route selector
- * requires.
- *
- * @return The required media route service package name, or null if none.
- */
- public @Nullable String getServicePackageName() {
- return mServicePackageName;
- }
-
- /**
- * Gets optional extras that may be used to select or configure routes for a
- * particular purpose. Some extras may be used by media route services to apply
- * additional constraints or parameters for the routes to be discovered.
- *
- * @return The optional extras, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public String toString() {
- return "MediaRouteSelector{ "
- + ", requiredFeatures=0x" + Integer.toHexString(mRequiredFeatures)
- + ", optionalFeatures=0x" + Integer.toHexString(mOptionalFeatures)
- + ", requiredProtocols=" + mRequiredProtocols
- + ", optionalProtocols=" + mOptionalProtocols
- + ", servicePackageName=" + mServicePackageName
- + ", extras=" + mExtras + " }";
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRequiredFeatures);
- dest.writeInt(mOptionalFeatures);
- dest.writeStringList(mRequiredProtocols);
- dest.writeStringList(mOptionalProtocols);
- dest.writeString(mServicePackageName);
- dest.writeBundle(mExtras);
- }
-
- public static final Parcelable.Creator<MediaRouteSelector> CREATOR =
- new Parcelable.Creator<MediaRouteSelector>() {
- @Override
- public MediaRouteSelector createFromParcel(Parcel source) {
- int requiredFeatures = source.readInt();
- int optionalFeatures = source.readInt();
- ArrayList<String> requiredProtocols = new ArrayList<String>();
- ArrayList<String> optionalProtocols = new ArrayList<String>();
- source.readStringList(requiredProtocols);
- source.readStringList(optionalProtocols);
- return new MediaRouteSelector(requiredFeatures, optionalFeatures,
- requiredProtocols, optionalProtocols,
- source.readString(), source.readBundle());
- }
-
- @Override
- public MediaRouteSelector[] newArray(int size) {
- return new MediaRouteSelector[size];
- }
- };
-
- /**
- * Builder for {@link MediaRouteSelector} objects.
- */
- public static final class Builder {
- private int mRequiredFeatures;
- private int mOptionalFeatures;
- private final ArrayList<String> mRequiredProtocols = new ArrayList<String>();
- private final ArrayList<String> mOptionalProtocols = new ArrayList<String>();
- private String mServicePackageName;
- private Bundle mExtras;
-
- /**
- * Creates an initially empty selector builder.
- */
- public Builder() {
- }
-
- /**
- * Sets the set of required route features.
- *
- * @param features A set of required route feature flags.
- */
- public @NonNull Builder setRequiredFeatures(@RouteFeatures int features) {
- mRequiredFeatures = features;
- return this;
- }
-
- /**
- * Sets the set of optional route features.
- *
- * @param features A set of optional route feature flags.
- */
- public @NonNull Builder setOptionalFeatures(@RouteFeatures int features) {
- mOptionalFeatures = features;
- return this;
- }
-
- /**
- * Adds a route protocol that a route must support in order to be selected
- * using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return this
- */
- public @NonNull Builder addRequiredProtocol(@NonNull Class<?> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addRequiredProtocol(clazz.getName());
- }
-
- /**
- * Adds a route protocol that a route must support in order to be selected.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The fully qualified name of the required protocol.
- * @return this
- */
- public @NonNull Builder addRequiredProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mRequiredProtocols.add(name);
- return this;
- }
-
- /**
- * Adds an optional route protocol that a client may use if available
- * using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return this
- */
- public @NonNull Builder addOptionalProtocol(@NonNull Class<?> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addOptionalProtocol(clazz.getName());
- }
-
- /**
- * Adds an optional route protocol that a client may use if available.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The fully qualified name of the optional protocol.
- * @return this
- */
- public @NonNull Builder addOptionalProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mOptionalProtocols.add(name);
- return this;
- }
-
- /**
- * Sets the package name of the media route service to which this selector
- * appertains.
- * <p>
- * If a package name is specified here then this selector will only be
- * passed to media route services from that package. This has the effect
- * of restricting the set of matching routes to just those that are offered
- * by that package.
- * </p>
- *
- * @param packageName The required service package name, or null if none.
- * @return this
- */
- public @NonNull Builder setServicePackageName(@Nullable String packageName) {
- mServicePackageName = packageName;
- return this;
- }
-
- /**
- * Sets optional extras that may be used to select or configure routes for a
- * particular purpose. Some extras may be used by route services to specify
- * additional constraints or parameters for the routes to be discovered.
- *
- * @param extras The optional extras, or null if none.
- * @return this
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link MediaRouteSelector} object.
- *
- * @return The new media route selector instance.
- */
- public @NonNull MediaRouteSelector build() {
- return new MediaRouteSelector(mRequiredFeatures, mOptionalFeatures,
- mRequiredProtocols, mOptionalProtocols, mServicePackageName, mExtras);
- }
- }
-}
diff --git a/media/java/android/media/routing/MediaRouteService.java b/media/java/android/media/routing/MediaRouteService.java
deleted file mode 100644
index 4d5a8a9..0000000
--- a/media/java/android/media/routing/MediaRouteService.java
+++ /dev/null
@@ -1,1023 +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.
- */
-package android.media.routing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.media.routing.MediaRouter.ConnectionError;
-import android.media.routing.MediaRouter.ConnectionInfo;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.DiscoveryError;
-import android.media.routing.MediaRouter.DiscoveryRequest;
-import android.media.routing.MediaRouter.RouteInfo;
-import android.media.routing.MediaRouter.ServiceMetadata;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Media route services implement strategies for discovering
- * and establishing connections to media devices and their routes. These services
- * are also known as media route providers.
- * <p>
- * Each media route service subclass is responsible for enabling applications
- * and the system to interact with media devices of some kind.
- * For example, one particular media route service implementation might
- * offer support for discovering nearby wireless display devices and streaming
- * video contents to them; another media route service implementation might
- * offer support for discovering nearby speakers and streaming media appliances
- * and sending commands to play content on request.
- * </p><p>
- * Subclasses must override the {@link #onCreateClientSession} method to return
- * a {@link ClientSession} object that implements the {@link ClientSession#onStartDiscovery},
- * {@link ClientSession#onStopDiscovery}, and {@link ClientSession#onConnect} methods
- * to allow clients to discover and connect to media devices.
- * </p><p>
- * This object is not thread-safe. All callbacks are invoked on the main looper.
- * </p>
- *
- * <h3>Clients</h3>
- * <p>
- * The clients of this API are media applications that would like to discover
- * and connect to media devices. The client may also be the system, such as
- * when the user initiates display mirroring via the Cast Screen function.
- * </p><p>
- * There may be multiple client sessions active at the same time. Each client
- * session can request discovery and connect to routes independently of any
- * other client. It is the responsibility of the media route service to maintain
- * separate state for each client session and to ensure that clients cannot interfere
- * with one another in harmful ways.
- * </p><p>
- * Notwithstanding the requirement to support any number of concurrent client
- * sessions, the media route service may impose constraints on how many clients
- * can connect to the same media device in a particular mode at the same time.
- * In some cases, media devices may support connections from an arbitrary number
- * of clients simultaneously but often it may be necessary to ensure that only
- * one client is in control. When this happens, the media route service should
- * report a connection error unless the connection request specifies that the
- * client should take control of the media device (and forcibly disconnect other
- * clients that may be using it).
- * </p>
- *
- * <h3>Destinations</h3>
- * <p>
- * The media devices to which an application may send media content are referred
- * to in the API as destinations. Each destination therefore represents a single
- * independent device such as a speaker or TV set. Destinations are given meaningful
- * names and descriptions to help the user associate them with devices in their
- * environment.
- * </p><p>
- * Destinations may be local or remote and may be accessed through various means,
- * often wirelessly. The user may install media route services to enable
- * media applications to connect to a variety of destinations with different
- * capabilities.
- * </p>
- *
- * <h3>Routes</h3>
- * <p>
- * Routes represent possible usages or means of reaching and interacting with
- * a destination. Since destinations may support many different features, they may
- * each offer multiple routes for applications to choose from based on their needs.
- * For example, one route might express the ability to stream locally rendered audio
- * and video to the device; another route might express the ability to send a URL for
- * the destination to download from the network and play all by itself.
- * </p><p>
- * Routes are discovered according to the set of capabilities that
- * an application or the system is seeking to use at a particular time. For example,
- * if an application wants to stream music to a destination then it will ask the
- * {@link MediaRouter} to find routes to destinations can stream music and ignore
- * all other destinations that cannot.
- * </p><p>
- * In general, the application will inspect the set of routes that have been
- * offered then connect to the most appropriate route for its desired purpose.
- * </p>
- *
- * <h3>Discovery</h3>
- * <p>
- * Discovery is the process of finding destinations based on a description of the
- * kinds of routes that an application or the system would like to use.
- * </p><p>
- * Discovery begins when {@link ClientSession#onStartDiscovery} is called and ends when
- * {@link ClientSession#onStopDiscovery} is called. There may be multiple simultaneous
- * discovery requests in progress at the same time from different clients. It is up to
- * the media route service to perform these requests in parallel or multiplex them
- * as required.
- * </p><p>
- * Media route services are <em>strongly encouraged</em> to use the information
- * in the discovery request to optimize discovery and avoid redundant work.
- * In the case where no media device supported by the media route service
- * could possibly offer the requested capabilities, the
- * {@link ClientSession#onStartDiscovery} method should return <code>false</code> to
- * let the system know that it can unbind from the media route service and
- * release its resources.
- * </p>
- *
- * <h3>Settings</h3>
- * <p>
- * Many kinds of devices can be discovered on demand simply by scanning the local network
- * or using wireless protocols such as Bluetooth to find them. However, in some cases
- * it may be necessary for the user to manually configure destinations before they
- * can be used (or to adjust settings later). Actual user configuration of destinations
- * is beyond the scope of this API but media route services may specify an activity
- * in their manifest that the user can launch to perform these tasks.
- * </p><p>
- * Note that media route services that are installed from the store must be enabled
- * by the user before they become available for applications to use.
- * The {@link android.provider.Settings#ACTION_CAST_SETTINGS Settings.ACTION_CAST_SETTINGS}
- * settings activity provides the ability for the user to configure media route services.
- * </p>
- *
- * <h3>Manifest Declaration</h3>
- * <p>
- * Media route services must be declared in the manifest along with meta-data
- * about the kinds of routes that they are capable of discovering. The system
- * uses this information to optimize the set of services to which it binds in
- * order to satisfy a particular discovery request.
- * </p><p>
- * To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_MEDIA_ROUTE_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. You must
- * also add meta-data to describe the kinds of routes that your service is capable
- * of discovering.
- * </p><p>
- * For example:
- * </p><pre>
- * &lt;service android:name=".MediaRouteProvider"
- * android:label="&#64;string/service_name"
- * android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE">
- * &lt;intent-filter>
- * &lt;action android:name="android.media.routing.MediaRouteService" />
- * &lt;/intent-filter>
- *
- * TODO: INSERT METADATA DECLARATIONS HERE
- *
- * &lt;/service>
- * </pre>
- */
-public abstract class MediaRouteService extends Service {
- private static final String TAG = "MediaRouteService";
-
- private static final boolean DEBUG = true;
-
- private final Handler mHandler;
- private final BinderService mService;
- private final ArrayMap<IBinder, ClientRecord> mClientRecords =
- new ArrayMap<IBinder, ClientRecord>();
-
- private ServiceMetadata mMetadata;
-
- /**
- * The {@link Intent} that must be declared as handled by the service.
- */
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.media.routing.MediaRouteService";
-
- /**
- * Creates a media route service.
- */
- public MediaRouteService() {
- mHandler = new Handler(true);
- mService = new BinderService();
- }
-
- @Override
- public @Nullable IBinder onBind(Intent intent) {
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mService;
- }
- return null;
- }
-
- /**
- * Creates a new client session on behalf of a client.
- * <p>
- * The implementation should return a {@link ClientSession} for the client
- * to use. The media route service must take care to manage the state of
- * each client session independently from any others that might also be
- * in use at the same time.
- * </p>
- *
- * @param client Information about the client.
- * @return The client session object, or null if the client is not allowed
- * to interact with this media route service.
- */
- public abstract @Nullable ClientSession onCreateClientSession(@NonNull ClientInfo client);
-
- /**
- * Gets metadata about this service.
- * <p>
- * Use this method to obtain a {@link ServiceMetadata} object to provide when creating
- * a {@link android.media.routing.MediaRouter.DestinationInfo.Builder}.
- * </p>
- *
- * @return Metadata about this service.
- */
- public @NonNull ServiceMetadata getServiceMetadata() {
- if (mMetadata == null) {
- try {
- mMetadata = new ServiceMetadata(this);
- } catch (NameNotFoundException ex) {
- Log.wtf(TAG, "Could not retrieve own service metadata!");
- }
- }
- return mMetadata;
- }
-
- /**
- * Enables a single client to access the functionality of the media route service.
- */
- public static abstract class ClientSession {
- /**
- * Starts discovery.
- * <p>
- * If the media route service is capable of discovering routes that satisfy
- * the request then this method should start discovery and return true.
- * Otherwise, this method should return false. If false is returned,
- * then the framework will not call {@link #onStopDiscovery} since discovery
- * was never actually started.
- * </p><p>
- * There may already be other discovery requests in progress at the same time
- * for other clients; the media route service must keep track of them all.
- * </p>
- *
- * @param req The discovery request to start.
- * @param callback A callback to receive discovery events related to this
- * particular request. The events that the service sends to this callback
- * will be sent to the client that initiated the discovery request.
- * @return True if discovery has started. False if the media route service
- * is unable to discover routes that satisfy the request.
- */
- public abstract boolean onStartDiscovery(@NonNull DiscoveryRequest req,
- @NonNull DiscoveryCallback callback);
-
- /**
- * Stops discovery.
- * <p>
- * If {@link #onStartDiscovery} returned true, then this method will eventually
- * be called when the framework no longer requires this discovery request
- * to be performed.
- * </p><p>
- * There may still be other discovery requests in progress for other clients;
- * they must keep working until they have each been stopped by their client.
- * </p>
- */
- public abstract void onStopDiscovery();
-
- /**
- * Starts connecting to a route.
- *
- * @param req The connection request.
- * @param callback A callback to receive events connection events related
- * to this particular request. The events that the service sends to this callback
- * will be sent to the client that initiated the discovery request.
- * @return True if the connection is in progress, or false if the client
- * unable to connect to the requested route.
- */
- public abstract boolean onConnect(@NonNull ConnectionRequest req,
- @NonNull ConnectionCallback callback);
-
- /**
- * Called when the client requests to disconnect from the route
- * or abort a connection attempt in progress.
- */
- public abstract void onDisconnect();
-
- /**
- * Called when the client requests to pause streaming of content to
- * live audio/video routes such as when it goes into the background.
- * <p>
- * The default implementation does nothing.
- * </p>
- */
- public void onPauseStream() { }
-
- /**
- * Called when the application requests to resume streaming of content to
- * live audio/video routes such as when it returns to the foreground.
- * <p>
- * The default implementation does nothing.
- * </p>
- */
- public void onResumeStream() { }
-
- /**
- * Called when the client is releasing the session.
- * <p>
- * The framework automatically takes care of stopping discovery and
- * terminating the connection politely before calling this method to release
- * the session.
- * </p><p>
- * The default implementation does nothing.
- * </p>
- */
- public void onRelease() { }
- }
-
- /**
- * Provides events in response to a discovery request.
- */
- public final class DiscoveryCallback {
- private final ClientRecord mRecord;
-
- DiscoveryCallback(ClientRecord record) {
- mRecord = record;
- }
-
- /**
- * Called by the service when a destination is found that
- * offers one or more routes that satisfy the discovery request.
- * <p>
- * This method should be called whenever the list of available routes
- * at a destination changes or whenever the properties of the destination
- * itself change.
- * </p>
- *
- * @param destination The destination that was found.
- * @param routes The list of that destination's routes that satisfy the
- * discovery request.
- */
- public void onDestinationFound(final @NonNull DestinationInfo destination,
- final @NonNull List<RouteInfo> routes) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- if (routes == null) {
- throw new IllegalArgumentException("routes must not be null");
- }
- for (int i = 0; i < routes.size(); i++) {
- if (routes.get(i).getDestination() != destination) {
- throw new IllegalArgumentException("routes must refer to the "
- + "destination");
- }
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDestinationFound(DiscoveryCallback.this,
- destination, routes);
- }
- });
- }
-
- /**
- * Called by the service when a destination is no longer
- * reachable or is no longer offering any routes that satisfy
- * the discovery request.
- *
- * @param destination The destination that went away.
- */
- public void onDestinationLost(final @NonNull DestinationInfo destination) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDestinationLost(DiscoveryCallback.this, destination);
- }
- });
- }
-
- /**
- * Called by the service when a discovery has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
- * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
- * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onDiscoveryFailed(final @DiscoveryError int error,
- final @Nullable CharSequence message, final @Nullable Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDiscoveryFailed(DiscoveryCallback.this,
- error, message, extras);
- }
- });
- }
- }
-
- /**
- * Provides events in response to a connection request.
- */
- public final class ConnectionCallback {
- private final ClientRecord mRecord;
-
- ConnectionCallback(ClientRecord record) {
- mRecord = record;
- }
-
- /**
- * Called by the service when the connection succeeds.
- *
- * @param connection Immutable information about the connection.
- */
- public void onConnected(final @NonNull ConnectionInfo connection) {
- if (connection == null) {
- throw new IllegalArgumentException("connection must not be null");
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchConnected(ConnectionCallback.this, connection);
- }
- });
- }
-
- /**
- * Called by the service when the connection is terminated normally.
- * <p>
- * Abnormal termination is reported via {@link #onConnectionFailed}.
- * </p>
- */
- public void onDisconnected() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDisconnected(ConnectionCallback.this);
- }
- });
- }
-
- /**
- * Called by the service when a connection attempt or connection in
- * progress has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
- * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
- * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
- * {@link MediaRouter#CONNECTION_ERROR_BUSY},
- * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
- * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
- * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onConnectionFailed(final @ConnectionError int error,
- final @Nullable CharSequence message, final @Nullable Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchConnectionFailed(ConnectionCallback.this,
- error, message, extras);
- }
- });
- }
- }
-
- /**
- * Identifies a client of the media route service.
- */
- public static final class ClientInfo {
- private final int mUid;
- private final String mPackageName;
-
- ClientInfo(int uid, String packageName) {
- mUid = uid;
- mPackageName = packageName;
- }
-
- /**
- * Gets the UID of the client application.
- */
- public int getUid() {
- return mUid;
- }
-
- /**
- * Gets the package name of the client application.
- */
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- @Override
- public @NonNull String toString() {
- return "ClientInfo{ uid=" + mUid + ", package=" + mPackageName + " }";
- }
- }
-
- private final class BinderService extends IMediaRouteService.Stub {
- @Override
- public void registerClient(final int clientUid, final String clientPackageName,
- final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientInfo client = new ClientInfo(clientUid, clientPackageName);
- if (DEBUG) {
- Log.d(TAG, "registerClient: client=" + client);
- }
-
- ClientSession session = onCreateClientSession(client);
- if (session == null) {
- // request refused by service
- Log.w(TAG, "Media route service refused to create session for client: "
- + "client=" + client);
- return;
- }
-
- ClientRecord record = new ClientRecord(callback, client, session);
- try {
- callback.asBinder().linkToDeath(record, 0);
- } catch (RemoteException ex) {
- // client died prematurely
- Log.w(TAG, "Client died prematurely while creating session: "
- + "client=" + client);
- record.release();
- return;
- }
-
- mClientRecords.put(callback.asBinder(), record);
- }
- });
- }
-
- @Override
- public void unregisterClient(IMediaRouteClientCallback callback) {
- unregisterClient(callback, false);
- }
-
- void unregisterClient(final IMediaRouteClientCallback callback,
- final boolean died) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.remove(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "unregisterClient: client=" + record.getClientInfo()
- + ", died=" + died);
- }
-
- record.release();
- callback.asBinder().unlinkToDeath(record, 0);
- }
- });
- }
-
- @Override
- public void startDiscovery(final IMediaRouteClientCallback callback,
- final int seq, final List<MediaRouteSelector> selectors,
- final int flags) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "startDiscovery: client=" + record.getClientInfo()
- + ", seq=" + seq + ", selectors=" + selectors
- + ", flags=0x" + Integer.toHexString(flags));
- }
- record.startDiscovery(seq, selectors, flags);
- }
- });
- }
-
- @Override
- public void stopDiscovery(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "stopDiscovery: client=" + record.getClientInfo());
- }
- record.stopDiscovery();
- }
- });
- }
-
- @Override
- public void connect(final IMediaRouteClientCallback callback,
- final int seq, final String destinationId, final String routeId,
- final int flags, final Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "connect: client=" + record.getClientInfo()
- + ", seq=" + seq + ", destinationId=" + destinationId
- + ", routeId=" + routeId
- + ", flags=0x" + Integer.toHexString(flags)
- + ", extras=" + extras);
- }
- record.connect(seq, destinationId, routeId, flags, extras);
- }
- });
- }
-
- @Override
- public void disconnect(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "disconnect: client=" + record.getClientInfo());
- }
- record.disconnect();
- }
- });
- }
-
- @Override
- public void pauseStream(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "pauseStream: client=" + record.getClientInfo());
- }
- record.pauseStream();
- }
- });
- }
-
- @Override
- public void resumeStream(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "resumeStream: client=" + record.getClientInfo());
- }
- record.resumeStream();
- }
- });
- }
- }
-
- // Must be accessed on handler
- private final class ClientRecord implements IBinder.DeathRecipient {
- private final IMediaRouteClientCallback mClientCallback;
- private final ClientInfo mClient;
- private final ClientSession mSession;
-
- private int mDiscoverySeq;
- private DiscoveryRequest mDiscoveryRequest;
- private DiscoveryCallback mDiscoveryCallback;
- private final ArrayMap<String, DestinationRecord> mDestinations =
- new ArrayMap<String, DestinationRecord>();
-
- private int mConnectionSeq;
- private ConnectionRequest mConnectionRequest;
- private ConnectionCallback mConnectionCallback;
- private ConnectionInfo mConnection;
- private boolean mConnectionPaused;
-
- public ClientRecord(IMediaRouteClientCallback callback,
- ClientInfo client, ClientSession session) {
- mClientCallback = callback;
- mClient = client;
- mSession = session;
- }
-
- // Invoked on binder thread unlike all other methods in this class.
- @Override
- public void binderDied() {
- mService.unregisterClient(mClientCallback, true);
- }
-
- public ClientInfo getClientInfo() {
- return mClient;
- }
-
- public void release() {
- stopDiscovery();
- disconnect();
- }
-
- public void startDiscovery(int seq, List<MediaRouteSelector> selectors,
- int flags) {
- stopDiscovery();
-
- mDiscoverySeq = seq;
- mDiscoveryRequest = new DiscoveryRequest(selectors);
- mDiscoveryRequest.setFlags(flags);
- mDiscoveryCallback = new DiscoveryCallback(this);
- boolean started = mSession.onStartDiscovery(mDiscoveryRequest, mDiscoveryCallback);
- if (!started) {
- dispatchDiscoveryFailed(mDiscoveryCallback,
- MediaRouter.DISCOVERY_ERROR_ABORTED, null, null);
- clearDiscovery();
- }
- }
-
- public void stopDiscovery() {
- if (mDiscoveryRequest != null) {
- mSession.onStopDiscovery();
- clearDiscovery();
- }
- }
-
- private void clearDiscovery() {
- mDestinations.clear();
- mDiscoveryRequest = null;
- mDiscoveryCallback = null;
- }
-
- public void connect(int seq, String destinationId, String routeId,
- int flags, Bundle extras) {
- disconnect();
-
- mConnectionSeq = seq;
- mConnectionCallback = new ConnectionCallback(this);
-
- DestinationRecord destinationRecord = mDestinations.get(destinationId);
- if (destinationRecord == null) {
- Log.w(TAG, "Aborting connection to route since no matching destination "
- + "was found in the list of known destinations: "
- + "destinationId=" + destinationId);
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- return;
- }
-
- RouteInfo route = destinationRecord.getRoute(routeId);
- if (route == null) {
- Log.w(TAG, "Aborting connection to route since no matching route "
- + "was found in the list of known routes: "
- + "destination=" + destinationRecord.destination
- + ", routeId=" + routeId);
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- return;
- }
-
- mConnectionRequest = new ConnectionRequest(route);
- mConnectionRequest.setFlags(flags);
- mConnectionRequest.setExtras(extras);
- boolean started = mSession.onConnect(mConnectionRequest, mConnectionCallback);
- if (!started) {
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- }
- }
-
- public void disconnect() {
- if (mConnectionRequest != null) {
- mSession.onDisconnect();
- clearConnection();
- }
- }
-
- private void clearConnection() {
- mConnectionRequest = null;
- mConnectionCallback = null;
- if (mConnection != null) {
- mConnection.close();
- mConnection = null;
- }
- mConnectionPaused = false;
- }
-
- public void pauseStream() {
- if (mConnectionRequest != null && !mConnectionPaused) {
- mConnectionPaused = true;
- mSession.onPauseStream();
- }
- }
-
- public void resumeStream() {
- if (mConnectionRequest != null && mConnectionPaused) {
- mConnectionPaused = false;
- mSession.onResumeStream();
- }
- }
-
- public void dispatchDestinationFound(DiscoveryCallback callback,
- DestinationInfo destination, List<RouteInfo> routes) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "destinationFound: destination=" + destination
- + ", routes=" + routes);
- }
- mDestinations.put(destination.getId(),
- new DestinationRecord(destination, routes));
-
- ParcelableDestinationInfo pdi = new ParcelableDestinationInfo();
- pdi.id = destination.getId();
- pdi.name = destination.getName();
- pdi.description = destination.getDescription();
- pdi.iconResourceId = destination.getIconResourceId();
- pdi.extras = destination.getExtras();
- ArrayList<ParcelableRouteInfo> pris = new ArrayList<ParcelableRouteInfo>();
- for (RouteInfo route : routes) {
- int selectorIndex = mDiscoveryRequest.getSelectors().indexOf(
- route.getSelector());
- if (selectorIndex < 0) {
- Log.w(TAG, "Ignoring route because the selector does not match "
- + "any of those that were originally supplied by the "
- + "client's discovery request: destination=" + destination
- + ", route=" + route);
- continue;
- }
-
- ParcelableRouteInfo pri = new ParcelableRouteInfo();
- pri.id = route.getId();
- pri.selectorIndex = selectorIndex;
- pri.features = route.getFeatures();
- pri.protocols = route.getProtocols().toArray(
- new String[route.getProtocols().size()]);
- pri.extras = route.getExtras();
- pris.add(pri);
- }
- try {
- mClientCallback.onDestinationFound(mDiscoverySeq, pdi,
- pris.toArray(new ParcelableRouteInfo[pris.size()]));
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
-
- public void dispatchDestinationLost(DiscoveryCallback callback,
- DestinationInfo destination) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "destinationLost: destination=" + destination);
- }
-
- if (mDestinations.get(destination.getId()).destination == destination) {
- mDestinations.remove(destination.getId());
- try {
- mClientCallback.onDestinationLost(mDiscoverySeq, destination.getId());
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- public void dispatchDiscoveryFailed(DiscoveryCallback callback,
- int error, CharSequence message, Bundle extras) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "discoveryFailed: error=" + error + ", message=" + message
- + ", extras=" + extras);
- }
-
- try {
- mClientCallback.onDiscoveryFailed(mDiscoverySeq, error, message, extras);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
-
- public void dispatchConnected(ConnectionCallback callback, ConnectionInfo connection) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "connected: connection=" + connection);
- }
- if (mConnection == null) {
- mConnection = connection;
-
- ParcelableConnectionInfo pci = new ParcelableConnectionInfo();
- pci.audioAttributes = connection.getAudioAttributes();
- pci.presentationDisplayId = connection.getPresentationDisplay() != null ?
- connection.getPresentationDisplay().getDisplayId() : -1;
- pci.protocolBinders = new IBinder[connection.getProtocols().size()];
- for (int i = 0; i < pci.protocolBinders.length; i++) {
- pci.protocolBinders[i] = connection.getProtocolBinder(i);
- }
- pci.extras = connection.getExtras();
- try {
- mClientCallback.onConnected(mConnectionSeq, pci);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- } else {
- Log.w(TAG, "Media route service called onConnected() while already "
- + "connected.");
- }
- }
- }
-
- public void dispatchDisconnected(ConnectionCallback callback) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "disconnected");
- }
-
- if (mConnection != null) {
- mConnection.close();
- mConnection = null;
-
- try {
- mClientCallback.onDisconnected(mConnectionSeq);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- public void dispatchConnectionFailed(ConnectionCallback callback,
- int error, CharSequence message, Bundle extras) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "connectionFailed: error=" + error + ", message=" + message
- + ", extras=" + extras);
- }
-
- try {
- mClientCallback.onConnectionFailed(mConnectionSeq, error, message, extras);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- private static final class DestinationRecord {
- public final DestinationInfo destination;
- public final List<RouteInfo> routes;
-
- public DestinationRecord(DestinationInfo destination, List<RouteInfo> routes) {
- this.destination = destination;
- this.routes = routes;
- }
-
- public RouteInfo getRoute(String routeId) {
- final int count = routes.size();
- for (int i = 0; i < count; i++) {
- RouteInfo route = routes.get(i);
- if (route.getId().equals(routeId)) {
- return route;
- }
- }
- return null;
- }
- }
-}
diff --git a/media/java/android/media/routing/MediaRouter.java b/media/java/android/media/routing/MediaRouter.java
deleted file mode 100644
index 4f6d324..0000000
--- a/media/java/android/media/routing/MediaRouter.java
+++ /dev/null
@@ -1,1886 +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.
- */
-package android.media.routing;
-
-import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Presentation;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
-import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.AudioTrack;
-import android.media.VolumeProvider;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.view.Display;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Media router allows applications to discover, connect to, control,
- * and send content to nearby media devices known as destinations.
- * <p>
- * There are generally two participants involved in media routing: an
- * application that wants to send media content to a destination and a
- * {@link MediaRouteService media route service} that provides the
- * service of transporting that content where it needs to go on behalf of the
- * application.
- * </p><p>
- * To send media content to a destination, the application must ask the system
- * to discover available routes to destinations that provide certain capabilities,
- * establish a connection to a route, then send messages through the connection to
- * control the routing of audio and video streams, launch remote applications,
- * and invoke other functions of the destination.
- * </p><p>
- * Media router objects are thread-safe.
- * </p>
- *
- * <h3>Destinations</h3>
- * <p>
- * The media devices to which an application may send media content are referred
- * to in the API as destinations. Each destination therefore represents a single
- * independent device such as a speaker or TV set. Destinations are given meaningful
- * names and descriptions to help the user associate them with devices in their
- * environment.
- * </p><p>
- * Destinations may be local or remote and may be accessed through various means,
- * often wirelessly. The user may install media route services to enable
- * media applications to connect to a variety of destinations with different
- * capabilities.
- * </p>
- *
- * <h3>Routes</h3>
- * <p>
- * Routes represent possible usages or means of reaching and interacting with
- * a destination. Since destinations may support many different features, they may
- * each offer multiple routes for applications to choose from based on their needs.
- * For example, one route might express the ability to stream locally rendered audio
- * and video to the device; another route might express the ability to send a URL for
- * the destination to download from the network and play all by itself.
- * </p><p>
- * Routes are discovered according to the set of capabilities that
- * an application or the system is seeking to use at a particular time. For example,
- * if an application wants to stream music to a destination then it will ask the
- * {@link MediaRouter} to find routes to destinations can stream music and ignore
- * all other destinations that cannot.
- * </p><p>
- * In general, the application will inspect the set of routes that have been
- * offered then connect to the most appropriate route for its desired purpose.
- * </p>
- *
- * <h3>Route Selection</h3>
- * <p>
- * When the user open the media route chooser activity, the system will display
- * a list of nearby media destinations which have been discovered. After the
- * choice is made the application may connect to one of the routes offered by
- * this destination and begin communicating with the destination.
- * </p><p>
- * Destinations are located through a process called discovery. During discovery,
- * the system will start installed {@link MediaRouteService media route services}
- * to scan the network for nearby devices that offer the kinds of capabilities that the
- * application is seeking to use. The application specifies the capabilities it requires by
- * adding {@link MediaRouteSelector media route selectors} to the media router
- * using the {@link #addSelector} method. Only destinations that provide routes
- * which satisfy at least one of these media route selectors will be discovered.
- * </p><p>
- * Once the user has selected a destination, the application will be given a chance
- * to choose one of the routes to which it would like to connect. The application
- * may switch to a different route from the same destination at a later time but
- * in order to connect to a new destination, the application must once again launch
- * the media route chooser activity to ask the user to choose a destination.
- * </p>
- *
- * <h3>Route Protocols</h3>
- * <p>
- * Route protocols express capabilities offered by routes. Each media route selector
- * must specify at least one required protocol by which the routes will be selected.
- * </p><p>
- * The framework provides several predefined <code>MediaRouteProtocols</code> which are
- * defined in the <code>android-support-media-protocols.jar</code> support library.
- * Applications must statically link this library to make use of these protocols.
- * </p><p>
- * The static library approach is used to enable ongoing extension and refinement
- * of protocols in the SDK and interoperability with the media router implementation
- * for older platform versions which is offered by the framework support library.
- * </p><p>
- * Media route services may also define custom media route protocols of their own
- * to enable applications to access specialized capabilities of certain destinations
- * assuming they have linked in the required protocol code.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code> for more information.
- * </p>
- *
- * <h3>Connections</h3>
- * <p>
- * After connecting to a media route, the application can send commands to
- * the route using any of the protocols that it requested. If the route supports live
- * audio or video streaming then the application can create an {@link AudioTrack} or
- * {@link Presentation} to route locally generated content to the destination.
- * </p>
- *
- * <h3>Delegation</h3>
- * <p>
- * The creator of the media router is responsible for establishing the policy for
- * discovering and connecting to destinations. UI components may observe the state
- * of the media router by {@link #createDelegate creating} a {@link Delegate}.
- * </p><p>
- * The media router should also be attached to the {@link MediaSession media session}
- * that is handling media playback lifecycle. This will allow
- * authorized {@link MediaController media controllers}, possibly running in other
- * processes, to provide UI to examine and change the media destination by
- * {@link MediaController#createMediaRouterDelegate creating} a {@link Delegate}
- * for the media router associated with the session.
- * </p>
- */
-public final class MediaRouter {
- private final DisplayManager mDisplayManager;
-
- private final Object mLock = new Object();
-
- private RoutingCallback mRoutingCallback;
- private Handler mRoutingCallbackHandler;
-
- private boolean mReleased;
- private int mDiscoveryState;
- private int mConnectionState;
- private final ArrayList<MediaRouteSelector> mSelectors =
- new ArrayList<MediaRouteSelector>();
- private final ArrayMap<DestinationInfo, List<RouteInfo>> mDiscoveredDestinations =
- new ArrayMap<DestinationInfo, List<RouteInfo>>();
- private RouteInfo mSelectedRoute;
- private ConnectionInfo mConnection;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCOVERY_STATE_STOPPED, DISCOVERY_STATE_STARTED })
- public @interface DiscoveryState { }
-
- /**
- * Discovery state: Discovery is not currently in progress.
- */
- public static final int DISCOVERY_STATE_STOPPED = 0;
-
- /**
- * Discovery state: Discovery is being performed.
- */
- public static final int DISCOVERY_STATE_STARTED = 1;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { DISCOVERY_FLAG_BACKGROUND })
- public @interface DiscoveryFlags { }
-
- /**
- * Discovery flag: Indicates that the client has requested passive discovery in
- * the background. The media route service should try to use less power and rely
- * more on its internal caches to minimize its impact.
- */
- public static final int DISCOVERY_FLAG_BACKGROUND = 1 << 0;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCOVERY_ERROR_UNKNOWN, DISCOVERY_ERROR_ABORTED,
- DISCOVERY_ERROR_NO_CONNECTIVITY })
- public @interface DiscoveryError { }
-
- /**
- * Discovery error: Unknown error; refer to the error message for details.
- */
- public static final int DISCOVERY_ERROR_UNKNOWN = 0;
-
- /**
- * Discovery error: The media router or media route service has decided not to
- * handle the discovery request for some reason.
- */
- public static final int DISCOVERY_ERROR_ABORTED = 1;
-
- /**
- * Discovery error: The media route service is unable to perform discovery
- * due to a lack of connectivity such as because the radio is disabled.
- */
- public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
- CONNECTION_STATE_CONNECTED })
- public @interface ConnectionState { }
-
- /**
- * Connection state: No destination has been selected. Media content should
- * be sent to the default output.
- */
- public static final int CONNECTION_STATE_DISCONNECTED = 0;
-
- /**
- * Connection state: The application is in the process of connecting to
- * a route offered by the selected destination.
- */
- public static final int CONNECTION_STATE_CONNECTING = 1;
-
- /**
- * Connection state: The application has connected to a route offered by
- * the selected destination.
- */
- public static final int CONNECTION_STATE_CONNECTED = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { CONNECTION_FLAG_BARGE })
- public @interface ConnectionFlags { }
-
- /**
- * Connection flag: Indicates that the client has requested to barge in and evict
- * other clients that might have already connected to the destination and that
- * would otherwise prevent this client from connecting. When this flag is not
- * set, the media route service should be polite and report
- * {@link MediaRouter#CONNECTION_ERROR_BUSY} in case the destination is
- * already occupied and cannot accept additional connections.
- */
- public static final int CONNECTION_FLAG_BARGE = 1 << 0;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { CONNECTION_ERROR_UNKNOWN, CONNECTION_ERROR_ABORTED,
- CONNECTION_ERROR_UNAUTHORIZED, CONNECTION_ERROR_UNAUTHORIZED,
- CONNECTION_ERROR_BUSY, CONNECTION_ERROR_TIMEOUT, CONNECTION_ERROR_BROKEN })
- public @interface ConnectionError { }
-
- /**
- * Connection error: Unknown error; refer to the error message for details.
- */
- public static final int CONNECTION_ERROR_UNKNOWN = 0;
-
- /**
- * Connection error: The media router or media route service has decided not to
- * handle the connection request for some reason.
- */
- public static final int CONNECTION_ERROR_ABORTED = 1;
-
- /**
- * Connection error: The device has refused the connection from this client.
- * This error should be avoided because the media route service should attempt
- * to filter out devices that the client cannot access as it performs discovery
- * on behalf of that client.
- */
- public static final int CONNECTION_ERROR_UNAUTHORIZED = 2;
-
- /**
- * Connection error: The device is unreachable over the network.
- */
- public static final int CONNECTION_ERROR_UNREACHABLE = 3;
-
- /**
- * Connection error: The device is already busy serving another client and
- * the connection request did not ask to barge in.
- */
- public static final int CONNECTION_ERROR_BUSY = 4;
-
- /**
- * Connection error: A timeout occurred during connection.
- */
- public static final int CONNECTION_ERROR_TIMEOUT = 5;
-
- /**
- * Connection error: The connection to the device was severed unexpectedly.
- */
- public static final int CONNECTION_ERROR_BROKEN = 6;
-
- /**
- * Connection error: The connection was terminated because a different client barged
- * in and took control of the destination.
- */
- public static final int CONNECTION_ERROR_BARGED = 7;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCONNECTION_REASON_APPLICATION_REQUEST,
- DISCONNECTION_REASON_USER_REQUEST, DISCONNECTION_REASON_ERROR })
- public @interface DisconnectionReason { }
-
- /**
- * Disconnection reason: The application requested disconnection itself.
- */
- public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0;
-
- /**
- * Disconnection reason: The user requested disconnection.
- */
- public static final int DISCONNECTION_REASON_USER_REQUEST = 1;
-
- /**
- * Disconnection reason: An error occurred.
- */
- public static final int DISCONNECTION_REASON_ERROR = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { ROUTE_FEATURE_LIVE_AUDIO, ROUTE_FEATURE_LIVE_VIDEO })
- public @interface RouteFeatures { }
-
- /**
- * Route feature: Live audio.
- * <p>
- * A route that supports live audio streams audio rendered by the application
- * to the destination.
- * </p><p>
- * To take advantage of live audio routing, the application must render its
- * media using the audio attributes specified by {@link #getPreferredAudioAttributes}.
- * </p>
- *
- * @see #getPreferredAudioAttributes
- * @see android.media.AudioAttributes
- */
- public static final int ROUTE_FEATURE_LIVE_AUDIO = 1 << 0;
-
- /**
- * Route feature: Live video.
- * <p>
- * A route that supports live video streams video rendered by the application
- * to the destination.
- * </p><p>
- * To take advantage of live video routing, the application must render its
- * media to a {@link android.app.Presentation presentation window} on the
- * display specified by {@link #getPreferredPresentationDisplay}.
- * </p>
- *
- * @see #getPreferredPresentationDisplay
- * @see android.app.Presentation
- */
- public static final int ROUTE_FEATURE_LIVE_VIDEO = 1 << 1;
-
- /**
- * Creates a media router.
- *
- * @param context The context with which the router is associated.
- */
- public MediaRouter(@NonNull Context context) {
- if (context == null) {
- throw new IllegalArgumentException("context must not be null");
- }
-
- mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- }
-
- /** @hide */
- public IMediaRouter getBinder() {
- // todo
- return null;
- }
-
- /**
- * Disconnects from the selected destination and releases the media router.
- * <p>
- * This method should be called by the application when it no longer requires
- * the media router to ensure that all bound resources may be cleaned up.
- * </p>
- */
- public void release() {
- synchronized (mLock) {
- mReleased = true;
- // todo
- }
- }
-
- /**
- * Returns true if the media router has been released.
- */
- public boolean isReleased() {
- synchronized (mLock) {
- return mReleased;
- }
- }
-
- /**
- * Gets the current route discovery state.
- *
- * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
- * {@link #DISCOVERY_STATE_STARTED}.
- */
- public @DiscoveryState int getDiscoveryState() {
- synchronized (mLock) {
- return mDiscoveryState;
- }
- }
-
- /**
- * Gets the current route connection state.
- *
- * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
- * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public @ConnectionState int getConnectionState() {
- synchronized (mLock) {
- return mConnectionState;
- }
- }
-
- /**
- * Creates a media router delegate through which the destination of the media
- * router may be controlled.
- * <p>
- * This is the point of entry for UI code that initiates discovery and
- * connection to routes.
- * </p>
- */
- public @NonNull Delegate createDelegate() {
- return null; // todo
- }
-
- /**
- * Sets a callback to participate in route discovery, filtering, and connection
- * establishment.
- *
- * @param callback The callback to set, or null if none.
- * @param handler The handler to receive callbacks, or null to use the current thread.
- */
- public void setRoutingCallback(@Nullable RoutingCallback callback,
- @Nullable Handler handler) {
- synchronized (mLock) {
- if (callback == null) {
- mRoutingCallback = null;
- mRoutingCallbackHandler = null;
- } else {
- mRoutingCallback = callback;
- mRoutingCallbackHandler = handler != null ? handler : new Handler();
- }
- }
- }
-
- /**
- * Adds a media route selector to use to find destinations that have
- * routes with the specified capabilities during route discovery.
- */
- public void addSelector(@NonNull MediaRouteSelector selector) {
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
-
- synchronized (mLock) {
- if (!mSelectors.contains(selector)) {
- mSelectors.add(selector);
- // todo
- }
- }
- }
-
- /**
- * Removes a media route selector.
- */
- public void removeSelector(@NonNull MediaRouteSelector selector) {
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
-
- synchronized (mLock) {
- if (mSelectors.remove(selector)) {
- // todo
- }
- }
- }
-
- /**
- * Removes all media route selectors.
- * <p>
- * Note that at least one selector must be added in order to perform discovery.
- * </p>
- */
- public void clearSelectors() {
- synchronized (mLock) {
- if (!mSelectors.isEmpty()) {
- mSelectors.clear();
- // todo
- }
- }
- }
-
- /**
- * Gets a list of all media route selectors to consider during discovery.
- */
- public @NonNull List<MediaRouteSelector> getSelectors() {
- synchronized (mLock) {
- return new ArrayList<MediaRouteSelector>(mSelectors);
- }
- }
-
- /**
- * Gets the connection to the currently selected route.
- *
- * @return The connection to the currently selected route, or null if not connected.
- */
- public @NonNull ConnectionInfo getConnection() {
- synchronized (mLock) {
- return mConnection;
- }
- }
-
- /**
- * Gets the list of discovered destinations.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @return The list of discovered destinations, or null if discovery is not running.
- */
- public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
- synchronized (mLock) {
- if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
- return new ArrayList<DestinationInfo>(mDiscoveredDestinations.keySet());
- }
- return null;
- }
- }
-
- /**
- * Gets the list of discovered routes for a particular destination.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @param destination The destination for which to get the list of discovered routes.
- * @return The list of discovered routes for the destination, or null if discovery
- * is not running.
- */
- public @NonNull List<RouteInfo> getDiscoveredRoutes(@NonNull DestinationInfo destination) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- synchronized (mLock) {
- if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
- List<RouteInfo> routes = mDiscoveredDestinations.get(destination);
- if (routes != null) {
- return new ArrayList<RouteInfo>(routes);
- }
- }
- return null;
- }
- }
-
- /**
- * Gets the destination that has been selected.
- *
- * @return The selected destination, or null if disconnected.
- */
- public @Nullable DestinationInfo getSelectedDestination() {
- synchronized (mLock) {
- return mSelectedRoute != null ? mSelectedRoute.getDestination() : null;
- }
- }
-
- /**
- * Gets the route that has been selected.
- *
- * @return The selected destination, or null if disconnected.
- */
- public @Nullable RouteInfo getSelectedRoute() {
- synchronized (mLock) {
- return mSelectedRoute;
- }
- }
-
- /**
- * Gets the preferred audio attributes that should be used to stream live audio content
- * based on the connected route.
- * <p>
- * Use an {@link AudioTrack} to send audio content to the destination with these
- * audio attributes.
- * </p><p>
- * The preferred audio attributes may change when a connection is established but it
- * will remain constant until disconnected.
- * </p>
- *
- * @return The preferred audio attributes to use. When connected, returns the
- * route's audio attributes or null if it does not support live audio streaming.
- * Otherwise returns audio attributes associated with {@link AudioAttributes#USAGE_MEDIA}.
- */
- public @Nullable AudioAttributes getPreferredAudioAttributes() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getAudioAttributes();
- }
- return new AudioAttributes.Builder()
- .setLegacyStreamType(AudioManager.STREAM_MUSIC)
- .build();
- }
- }
-
- /**
- * Gets the preferred presentation display that should be used to stream live video content
- * based on the connected route.
- * <p>
- * Use a {@link Presentation} to send video content to the destination with this display.
- * </p><p>
- * The preferred presentation display may change when a connection is established but it
- * will remain constant until disconnected.
- * </p>
- *
- * @return The preferred presentation display to use. When connected, returns
- * the route's presentation display or null if it does not support live video
- * streaming. Otherwise returns the first available
- * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION presentation display},
- * such as a mirrored wireless or HDMI display or null if none.
- */
- public @Nullable Display getPreferredPresentationDisplay() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getPresentationDisplay();
- }
- Display[] displays = mDisplayManager.getDisplays(
- DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
- return displays.length != 0 ? displays[0] : null;
- }
- }
-
- /**
- * Gets the preferred volume provider that should be used to control the volume
- * of content rendered on the currently selected route.
- * <p>
- * The preferred volume provider may change when a connection is established but it
- * will remain the same until disconnected.
- * </p>
- *
- * @return The preferred volume provider to use, or null if the currently
- * selected route does not support remote volume adjustment or if the connection
- * is not yet established. If no route is selected, returns null to indicate
- * that system volume control should be used.
- */
- public @Nullable VolumeProvider getPreferredVolumeProvider() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getVolumeProvider();
- }
- return null;
- }
- }
-
- /**
- * Requests to pause streaming of live audio or video routes.
- * Should be called when the application is going into the background and is
- * no longer rendering content locally.
- * <p>
- * This method does nothing unless a connection has been established.
- * </p>
- */
- public void pauseStream() {
- // todo
- }
-
- /**
- * Requests to resume streaming of live audio or video routes.
- * May be called when the application is returning to the foreground and is
- * about to resume rendering content locally.
- * <p>
- * This method does nothing unless a connection has been established.
- * </p>
- */
- public void resumeStream() {
- // todo
- }
-
- /**
- * This class is used by UI components to let the user discover and
- * select a destination to which the media router should connect.
- * <p>
- * This API has somewhat more limited functionality than the {@link MediaRouter}
- * itself because it is designed to allow applications to control
- * the destination of media router instances that belong to other processes.
- * </p><p>
- * To control the destination of your own media router, call
- * {@link #createDelegate} to obtain a local delegate object.
- * </p><p>
- * To control the destination of a media router that belongs to another process,
- * first obtain a {@link MediaController} that is associated with the media playback
- * that is occurring in that process, then call
- * {@link MediaController#createMediaRouterDelegate} to obtain an instance of
- * its destination controls. Note that special permissions may be required to
- * obtain the {@link MediaController} instance in the first place.
- * </p>
- */
- public static final class Delegate {
- /**
- * Returns true if the media router has been released.
- */
- public boolean isReleased() {
- // todo
- return false;
- }
-
- /**
- * Gets the current route discovery state.
- *
- * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
- * {@link #DISCOVERY_STATE_STARTED}.
- */
- public @DiscoveryState int getDiscoveryState() {
- // todo
- return -1;
- }
-
- /**
- * Gets the current route connection state.
- *
- * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
- * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public @ConnectionState int getConnectionState() {
- // todo
- return -1;
- }
-
- /**
- * Gets the currently selected destination.
- *
- * @return The destination information, or null if none.
- */
- public @Nullable DestinationInfo getSelectedDestination() {
- return null;
- }
-
- /**
- * Gets the list of discovered destinations.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @return The list of discovered destinations, or null if discovery is not running.
- */
- public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
- return null;
- }
-
- /**
- * Adds a callback to receive state changes.
- *
- * @param callback The callback to set, or null if none.
- * @param handler The handler to receive callbacks, or null to use the current thread.
- */
- public void addStateCallback(@Nullable StateCallback callback,
- @Nullable Handler handler) {
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null");
- }
- if (handler == null) {
- handler = new Handler();
- }
- // todo
- }
-
- /**
- * Removes a callback for state changes.
- *
- * @param callback The callback to set, or null if none.
- */
- public void removeStateCallback(@Nullable StateCallback callback) {
- // todo
- }
-
- /**
- * Starts performing discovery.
- * <p>
- * Performing discovery is expensive. Make sure to call {@link #stopDiscovery}
- * as soon as possible once a new destination has been selected to allow the system
- * to stop services associated with discovery.
- * </p>
- *
- * @param flags The discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public void startDiscovery(@DiscoveryFlags int flags) {
- // todo
- }
-
- /**
- * Stops performing discovery.
- */
- public void stopDiscovery() {
- // todo
- }
-
- /**
- * Connects to a destination during route discovery.
- * <p>
- * This method may only be called while route discovery is active and the
- * destination appears in the
- * {@link #getDiscoveredDestinations list of discovered destinations}.
- * If the media router is already connected to a route then it will first disconnect
- * from the current route then connect to the new route.
- * </p>
- *
- * @param destination The destination to which the media router should connect.
- * @param flags The connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public void connect(@NonNull DestinationInfo destination, @DiscoveryFlags int flags) {
- // todo
- }
-
- /**
- * Disconnects from the currently selected destination.
- * <p>
- * Does nothing if not currently connected.
- * </p>
- *
- * @param reason The reason for the disconnection: one of
- * {@link #DISCONNECTION_REASON_APPLICATION_REQUEST},
- * {@link #DISCONNECTION_REASON_USER_REQUEST}, or {@link #DISCONNECTION_REASON_ERROR}.
- */
- public void disconnect(@DisconnectionReason int reason) {
- // todo
- }
- }
-
- /**
- * Describes immutable properties of a connection to a route.
- */
- public static final class ConnectionInfo {
- private final RouteInfo mRoute;
- private final AudioAttributes mAudioAttributes;
- private final Display mPresentationDisplay;
- private final VolumeProvider mVolumeProvider;
- private final IBinder[] mProtocolBinders;
- private final Object[] mProtocolInstances;
- private final Bundle mExtras;
- private final ArrayList<Closeable> mCloseables;
-
- private static final Class<?>[] MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS =
- new Class<?>[] { IBinder.class };
-
- ConnectionInfo(RouteInfo route,
- AudioAttributes audioAttributes, Display display,
- VolumeProvider volumeProvider, IBinder[] protocolBinders,
- Bundle extras, ArrayList<Closeable> closeables) {
- mRoute = route;
- mAudioAttributes = audioAttributes;
- mPresentationDisplay = display;
- mVolumeProvider = volumeProvider;
- mProtocolBinders = protocolBinders;
- mProtocolInstances = new Object[mProtocolBinders.length];
- mExtras = extras;
- mCloseables = closeables;
- }
-
- /**
- * Gets the route that is connected.
- */
- public @NonNull RouteInfo getRoute() {
- return mRoute;
- }
-
- /**
- * Gets the audio attributes which the client should use to stream audio
- * to the destination, or null if the route does not support live audio streaming.
- */
- public @Nullable AudioAttributes getAudioAttributes() {
- return mAudioAttributes;
- }
-
- /**
- * Gets the display which the client should use to stream video to the
- * destination using a {@link Presentation}, or null if the route does not
- * support live video streaming.
- */
- public @Nullable Display getPresentationDisplay() {
- return mPresentationDisplay;
- }
-
- /**
- * Gets the route's volume provider, or null if none.
- */
- public @Nullable VolumeProvider getVolumeProvider() {
- return mVolumeProvider;
- }
-
- /**
- * Gets the set of supported route features.
- */
- public @RouteFeatures int getFeatures() {
- return mRoute.getFeatures();
- }
-
- /**
- * Gets the list of supported route protocols.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull List<String> getProtocols() {
- return mRoute.getProtocols();
- }
-
- /**
- * Gets an instance of a route protocol object that wraps the protocol binder
- * and provides easy access to the protocol's functionality.
- * <p>
- * This is a convenience method which invokes {@link #getProtocolBinder(String)}
- * using the name of the provided class then passes the resulting {@link IBinder}
- * to a single-argument constructor of that class.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- @SuppressWarnings("unchecked")
- public @Nullable <T> T getProtocolObject(Class<T> clazz) {
- int index = getProtocols().indexOf(clazz.getName());
- if (index < 0) {
- return null;
- }
- if (mProtocolInstances[index] == null && mProtocolBinders[index] != null) {
- final Constructor<T> ctor;
- try {
- ctor = clazz.getConstructor(MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS);
- } catch (NoSuchMethodException ex) {
- throw new RuntimeException("Could not find public constructor "
- + "with IBinder argument in protocol class: " + clazz.getName(), ex);
- }
- try {
- mProtocolInstances[index] = ctor.newInstance(mProtocolBinders[index]);
- } catch (InstantiationException | IllegalAccessException
- | InvocationTargetException ex) {
- throw new RuntimeException("Could create instance of protocol class: "
- + clazz.getName(), ex);
- }
- }
- return (T)mProtocolInstances[index];
- }
-
- /**
- * Gets the {@link IBinder} that provides access to the specified route protocol
- * or null if the protocol is not supported.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @Nullable IBinder getProtocolBinder(@NonNull String name) {
- int index = getProtocols().indexOf(name);
- return index >= 0 ? mProtocolBinders[index] : null;
- }
-
- /**
- * Gets the {@link IBinder} that provides access to the specified route protocol
- * at the given index in the protocol list or null if the protocol is not supported.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @Nullable IBinder getProtocolBinder(int index) {
- return mProtocolBinders[index];
- }
-
- /**
- * Gets optional extra media route service or protocol specific information about
- * the connection. Use the service or protocol name as the prefix for
- * any extras to avoid namespace collisions.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Closes all closeables associated with the connection when the connection
- * is being torn down.
- */
- void close() {
- final int count = mCloseables.size();
- for (int i = 0; i < count; i++) {
- try {
- mCloseables.get(i).close();
- } catch (IOException ex) {
- }
- }
- }
-
- @Override
- public @NonNull String toString() {
- return "ConnectionInfo{ route=" + mRoute
- + ", audioAttributes=" + mAudioAttributes
- + ", presentationDisplay=" + mPresentationDisplay
- + ", volumeProvider=" + mVolumeProvider
- + ", protocolBinders=" + mProtocolBinders + " }";
- }
-
- /**
- * Builds {@link ConnectionInfo} objects.
- */
- public static final class Builder {
- private final RouteInfo mRoute;
- private AudioAttributes mAudioAttributes;
- private Display mPresentationDisplay;
- private VolumeProvider mVolumeProvider;
- private final IBinder[] mProtocols;
- private Bundle mExtras;
- private final ArrayList<Closeable> mCloseables = new ArrayList<Closeable>();
-
- /**
- * Creates a builder for connection information.
- *
- * @param route The route that is connected.
- */
- public Builder(@NonNull RouteInfo route) {
- if (route == null) {
- throw new IllegalArgumentException("route");
- }
- mRoute = route;
- mProtocols = new IBinder[route.getProtocols().size()];
- }
-
- /**
- * Sets the audio attributes which the client should use to stream audio
- * to the destination, or null if the route does not support live audio streaming.
- */
- public @NonNull Builder setAudioAttributes(
- @Nullable AudioAttributes audioAttributes) {
- mAudioAttributes = audioAttributes;
- return this;
- }
-
- /**
- * Sets the display which the client should use to stream video to the
- * destination using a {@link Presentation}, or null if the route does not
- * support live video streaming.
- */
- public @NonNull Builder setPresentationDisplay(@Nullable Display display) {
- mPresentationDisplay = display;
- return this;
- }
-
- /**
- * Sets the route's volume provider, or null if none.
- */
- public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider provider) {
- mVolumeProvider = provider;
- return this;
- }
-
- /**
- * Sets the binder stub of a supported route protocol using
- * the protocol's fully qualified class name. The protocol must be one
- * of those that was indicated as being supported by the route.
- * <p>
- * If the stub implements {@link Closeable} then it will automatically
- * be closed when the client disconnects from the route.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull Builder setProtocolStub(@NonNull Class<?> clazz,
- @NonNull IInterface stub) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- if (stub == null) {
- throw new IllegalArgumentException("stub must not be null");
- }
- if (stub instanceof Closeable) {
- mCloseables.add((Closeable)stub);
- }
- return setProtocolBinder(clazz.getName(), stub.asBinder());
- }
-
- /**
- * Sets the binder interface of a supported route protocol by name.
- * The protocol must be one of those that was indicated as being supported
- * by the route.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull Builder setProtocolBinder(@NonNull String name,
- @NonNull IBinder binder) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- if (binder == null) {
- throw new IllegalArgumentException("binder must not be null");
- }
- int index = mRoute.getProtocols().indexOf(name);
- if (index < 0) {
- throw new IllegalArgumentException("name must specify a protocol that "
- + "the route actually declared that it supports: "
- + "name=" + name + ", protocols=" + mRoute.getProtocols());
- }
- mProtocols[index] = binder;
- return this;
- }
-
- /**
- * Sets optional extra media route service or protocol specific information about
- * the connection. Use the service or protocol name as the prefix for
- * any extras to avoid namespace collisions.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link ConnectionInfo} object.
- */
- public @NonNull ConnectionInfo build() {
- return new ConnectionInfo(mRoute,
- mAudioAttributes, mPresentationDisplay,
- mVolumeProvider, mProtocols, mExtras, mCloseables);
- }
- }
- }
-
- /**
- * Describes one particular way of routing media content to a destination
- * according to the capabilities specified by a media route selector on behalf
- * of an application.
- */
- public static final class RouteInfo {
- private final String mId;
- private final DestinationInfo mDestination;
- private final MediaRouteSelector mSelector;
- private final int mFeatures;
- private final ArrayList<String> mProtocols;
- private final Bundle mExtras;
-
- RouteInfo(String id, DestinationInfo destination, MediaRouteSelector selector,
- int features, ArrayList<String> protocols, Bundle extras) {
- mId = id;
- mDestination = destination;
- mSelector = selector;
- mFeatures = features;
- mProtocols = protocols;
- mExtras = extras;
- }
-
- /**
- * Gets the route's stable identifier.
- * <p>
- * The id is intended to uniquely identify the route among all routes that
- * are offered by a particular destination in such a way that the client can
- * refer to it at a later time.
- * </p>
- */
- public @NonNull String getId() {
- return mId;
- }
-
- /**
- * Gets the destination that is offering this route.
- */
- public @NonNull DestinationInfo getDestination() {
- return mDestination;
- }
-
- /**
- * Gets the media route selector provided by the client for which this
- * route was created.
- * <p>
- * It is implied that this route supports all of the required capabilities
- * that were expressed in the selector.
- * </p>
- */
- public @NonNull MediaRouteSelector getSelector() {
- return mSelector;
- }
-
- /**
- * Gets the set of supported route features.
- */
- public @RouteFeatures int getFeatures() {
- return mFeatures;
- }
-
- /**
- * Gets the list of supported route protocols.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull List<String> getProtocols() {
- return mProtocols;
- }
-
- /**
- * Gets optional extra information about the route, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public @NonNull String toString() {
- return "RouteInfo{ id=" + mId + ", destination=" + mDestination
- + ", features=0x" + Integer.toHexString(mFeatures)
- + ", selector=" + mSelector + ", protocols=" + mProtocols
- + ", extras=" + mExtras + " }";
- }
-
- /**
- * Builds {@link RouteInfo} objects.
- */
- public static final class Builder {
- private final DestinationInfo mDestination;
- private final String mId;
- private final MediaRouteSelector mSelector;
- private int mFeatures;
- private final ArrayList<String> mProtocols = new ArrayList<String>();
- private Bundle mExtras;
-
- /**
- * Creates a builder for route information.
- *
- * @param id The route's stable identifier.
- * @param destination The destination of this route.
- * @param selector The media route selector provided by the client for which
- * this route was created. This must be one of the selectors that was
- * included in the discovery request.
- */
- public Builder(@NonNull String id, @NonNull DestinationInfo destination,
- @NonNull MediaRouteSelector selector) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id must not be null or empty");
- }
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
- mDestination = destination;
- mId = id;
- mSelector = selector;
- }
-
- /**
- * Sets the set of supported route features.
- */
- public @NonNull Builder setFeatures(@RouteFeatures int features) {
- mFeatures = features;
- return this;
- }
-
- /**
- * Adds a supported route protocol using its fully qualified class name.
- * <p>
- * If the protocol was not requested by the client in its selector
- * then it will be silently discarded.
- * </p>
- */
- public @NonNull <T extends IInterface> Builder addProtocol(@NonNull Class<T> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addProtocol(clazz.getName());
- }
-
- /**
- * Adds a supported route protocol by name.
- * <p>
- * If the protocol was not requested by the client in its selector
- * then it will be silently discarded.
- * </p>
- */
- public @NonNull Builder addProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null");
- }
- if (mSelector.containsProtocol(name)) {
- mProtocols.add(name);
- }
- return this;
- }
-
- /**
- * Sets optional extra information about the route, or null if none.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link RouteInfo} object.
- * <p>
- * Ensures that all required protocols have been supplied.
- * </p>
- */
- public @NonNull RouteInfo build() {
- int missingFeatures = mSelector.getRequiredFeatures() & ~mFeatures;
- if (missingFeatures != 0) {
- throw new IllegalStateException("The media route selector "
- + "specified required features which this route does "
- + "not appear to support so it should not have been published: "
- + "missing 0x" + Integer.toHexString(missingFeatures));
- }
- for (String protocol : mSelector.getRequiredProtocols()) {
- if (!mProtocols.contains(protocol)) {
- throw new IllegalStateException("The media route selector "
- + "specified required protocols which this route "
- + "does not appear to support so it should not have "
- + "been published: missing " + protocol);
- }
- }
- return new RouteInfo(mId, mDestination, mSelector,
- mFeatures, mProtocols, mExtras);
- }
- }
- }
-
- /**
- * Describes a destination for media content such as a device,
- * an individual port on a device, or a group of devices.
- */
- public static final class DestinationInfo {
- private final String mId;
- private final ServiceMetadata mService;
- private final CharSequence mName;
- private final CharSequence mDescription;
- private final int mIconResourceId;
- private final Bundle mExtras;
-
- DestinationInfo(String id, ServiceMetadata service,
- CharSequence name, CharSequence description,
- int iconResourceId, Bundle extras) {
- mId = id;
- mService = service;
- mName = name;
- mDescription = description;
- mIconResourceId = iconResourceId;
- mExtras = extras;
- }
-
- /**
- * Gets the destination's stable identifier.
- * <p>
- * The id is intended to uniquely identify the destination among all destinations
- * provided by the media route service in such a way that the client can
- * refer to it at a later time. Ideally, the id should be resilient to
- * user-initiated actions such as changes to the name or description
- * of the destination.
- * </p>
- */
- public @NonNull String getId() {
- return mId;
- }
-
- /**
- * Gets metadata about the service that is providing access to this destination.
- */
- public @NonNull ServiceMetadata getServiceMetadata() {
- return mService;
- }
-
- /**
- * Gets the destination's name for display to the user.
- */
- public @NonNull CharSequence getName() {
- return mName;
- }
-
- /**
- * Gets the destination's description for display to the user, or null if none.
- */
- public @Nullable CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Gets an icon resource from the service's package which is used
- * to identify the destination, or -1 if none.
- */
- public @DrawableRes int getIconResourceId() {
- return mIconResourceId;
- }
-
- /**
- * Loads the icon drawable, or null if none.
- */
- public @Nullable Drawable loadIcon(@NonNull PackageManager pm) {
- return mIconResourceId >= 0 ? mService.getDrawable(pm, mIconResourceId) : null;
- }
-
- /**
- * Gets optional extra information about the destination, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public @NonNull String toString() {
- return "DestinationInfo{ id=" + mId + ", service=" + mService + ", name=" + mName
- + ", description=" + mDescription + ", iconResourceId=" + mIconResourceId
- + ", extras=" + mExtras + " }";
- }
-
- /**
- * Builds {@link DestinationInfo} objects.
- */
- public static final class Builder {
- private final String mId;
- private final ServiceMetadata mService;
- private final CharSequence mName;
- private CharSequence mDescription;
- private int mIconResourceId = -1;
- private Bundle mExtras;
-
- /**
- * Creates a builder for destination information.
- *
- * @param id The destination's stable identifier.
- * @param service Metatada about the service that is providing access to
- * this destination.
- * @param name The destination's name for display to the user.
- */
- public Builder(@NonNull String id, @NonNull ServiceMetadata service,
- @NonNull CharSequence name) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id must not be null or empty");
- }
- if (service == null) {
- throw new IllegalArgumentException("service must not be null");
- }
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mId = id;
- mService = service;
- mName = name;
- }
-
- /**
- * Sets the destination's description for display to the user, or null if none.
- */
- public @NonNull Builder setDescription(@Nullable CharSequence description) {
- mDescription = description;
- return this;
- }
-
- /**
- * Sets an icon resource from this package used to identify the destination,
- * or -1 if none.
- */
- public @NonNull Builder setIconResourceId(@DrawableRes int resid) {
- mIconResourceId = resid;
- return this;
- }
-
- /**
- * Gets optional extra information about the destination, or null if none.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link DestinationInfo} object.
- */
- public @NonNull DestinationInfo build() {
- return new DestinationInfo(mId, mService, mName, mDescription,
- mIconResourceId, mExtras);
- }
- }
- }
-
- /**
- * Describes metadata about a {@link MediaRouteService} which is providing
- * access to certain kinds of destinations.
- */
- public static final class ServiceMetadata {
- private final ServiceInfo mService;
- private CharSequence mLabel;
- private Drawable mIcon;
-
- ServiceMetadata(Service service) throws NameNotFoundException {
- mService = service.getPackageManager().getServiceInfo(
- new ComponentName(service, service.getClass()),
- PackageManager.GET_META_DATA);
- }
-
- ServiceMetadata(ServiceInfo service) {
- mService = service;
- }
-
- /**
- * Gets the service's component information including it name, label and icon.
- */
- public @NonNull ServiceInfo getService() {
- return mService;
- }
-
- /**
- * Gets the service's component name.
- */
- public @NonNull ComponentName getComponentName() {
- return new ComponentName(mService.packageName, mService.name);
- }
-
- /**
- * Gets the service's package name.
- */
- public @NonNull String getPackageName() {
- return mService.packageName;
- }
-
- /**
- * Gets the service's name for display to the user, or null if none.
- */
- public @NonNull CharSequence getLabel(@NonNull PackageManager pm) {
- if (mLabel == null) {
- mLabel = mService.loadLabel(pm);
- }
- return mLabel;
- }
-
- /**
- * Gets the icon drawable, or null if none.
- */
- public @Nullable Drawable getIcon(@NonNull PackageManager pm) {
- if (mIcon == null) {
- mIcon = mService.loadIcon(pm);
- }
- return mIcon;
- }
-
- // TODO: add service metadata
-
- Drawable getDrawable(PackageManager pm, int resid) {
- return pm.getDrawable(getPackageName(), resid, mService.applicationInfo);
- }
-
- @Override
- public @NonNull String toString() {
- return "ServiceInfo{ service=" + getComponentName().toShortString() + " }";
- }
- }
-
- /**
- * Describes a request to discover routes on behalf of an application.
- */
- public static final class DiscoveryRequest {
- private final ArrayList<MediaRouteSelector> mSelectors =
- new ArrayList<MediaRouteSelector>();
- private int mFlags;
-
- DiscoveryRequest(@NonNull List<MediaRouteSelector> selectors) {
- setSelectors(selectors);
- }
-
- /**
- * Sets the list of media route selectors to consider during discovery.
- */
- public void setSelectors(@NonNull List<MediaRouteSelector> selectors) {
- if (selectors == null) {
- throw new IllegalArgumentException("selectors");
- }
- mSelectors.clear();
- mSelectors.addAll(selectors);
- }
-
- /**
- * Gets the list of media route selectors to consider during discovery.
- */
- public @NonNull List<MediaRouteSelector> getSelectors() {
- return mSelectors;
- }
-
- /**
- * Gets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public @DiscoveryFlags int getFlags() {
- return mFlags;
- }
-
- /**
- * Sets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public void setFlags(@DiscoveryFlags int flags) {
- mFlags = flags;
- }
-
- @Override
- public @NonNull String toString() {
- return "DiscoveryRequest{ selectors=" + mSelectors
- + ", flags=0x" + Integer.toHexString(mFlags)
- + " }";
- }
- }
-
- /**
- * Describes a request to connect to a previously discovered route on
- * behalf of an application.
- */
- public static final class ConnectionRequest {
- private RouteInfo mRoute;
- private int mFlags;
- private Bundle mExtras;
-
- ConnectionRequest(@NonNull RouteInfo route) {
- setRoute(route);
- }
-
- /**
- * Gets the route to which to connect.
- */
- public @NonNull RouteInfo getRoute() {
- return mRoute;
- }
-
- /**
- * Sets the route to which to connect.
- */
- public void setRoute(@NonNull RouteInfo route) {
- if (route == null) {
- throw new IllegalArgumentException("route must not be null");
- }
- mRoute = route;
- }
-
- /**
- * Gets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public @ConnectionFlags int getFlags() {
- return mFlags;
- }
-
- /**
- * Sets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public void setFlags(@ConnectionFlags int flags) {
- mFlags = flags;
- }
-
- /**
- * Gets optional extras supplied by the application as part of the call to
- * connect, or null if none. The media route service may use this
- * information to configure the route during connection.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Sets optional extras supplied by the application as part of the call to
- * connect, or null if none. The media route service may use this
- * information to configure the route during connection.
- */
- public void setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- }
-
- @Override
- public @NonNull String toString() {
- return "ConnectionRequest{ route=" + mRoute
- + ", flags=0x" + Integer.toHexString(mFlags)
- + ", extras=" + mExtras + " }";
- }
- }
-
- /**
- * Callback interface to specify policy for route discovery, filtering,
- * and connection establishment as well as observe media router state changes.
- */
- public static abstract class RoutingCallback extends StateCallback {
- /**
- * Called to prepare a discovery request object to specify the desired
- * media route selectors when the media router has been asked to start discovery.
- * <p>
- * By default, the discovery request contains all of the selectors which
- * have been added to the media router. Subclasses may override the list of
- * selectors by modifying the discovery request object before returning.
- * </p>
- *
- * @param request The discovery request object which may be modified by
- * this method to alter how discovery will be performed.
- * @param selectors The immutable list of media route selectors which were
- * added to the media router.
- * @return True to allow discovery to proceed or false to abort it.
- * By default, this methods returns true.
- */
- public boolean onPrepareDiscoveryRequest(@NonNull DiscoveryRequest request,
- @NonNull List<MediaRouteSelector> selectors) {
- return true;
- }
-
- /**
- * Called to prepare a connection request object to specify the desired
- * route and connection parameters when the media router has been asked to
- * connect to a particular destination.
- * <p>
- * By default, the connection request specifies the first available route
- * to the destination. Subclasses may override the route and destination
- * or set additional connection parameters by modifying the connection request
- * object before returning.
- * </p>
- *
- * @param request The connection request object which may be modified by
- * this method to alter how the connection will be established.
- * @param destination The destination to which the media router was asked
- * to connect.
- * @param routes The list of routes that belong to that destination sorted
- * in the same order as their matching media route selectors which were
- * used during discovery.
- * @return True to allow the connection to proceed or false to abort it.
- * By default, this methods returns true.
- */
- public boolean onPrepareConnectionRequest(
- @NonNull ConnectionRequest request,
- @NonNull DestinationInfo destination, @NonNull List<RouteInfo> routes) {
- return true;
- }
- }
-
- /**
- * Callback class to receive events from a {@link MediaRouter.Delegate}.
- */
- public static abstract class StateCallback {
- /**
- * Called when the media router has been released.
- */
- public void onReleased() { }
-
- /**
- * Called when the discovery state has changed.
- *
- * @param state The new discovery state: one of
- * {@link #DISCOVERY_STATE_STOPPED} or {@link #DISCOVERY_STATE_STARTED}.
- */
- public void onDiscoveryStateChanged(@DiscoveryState int state) { }
-
- /**
- * Called when the connection state has changed.
- *
- * @param state The new connection state: one of
- * {@link #CONNECTION_STATE_DISCONNECTED}, {@link #CONNECTION_STATE_CONNECTING}
- * or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public void onConnectionStateChanged(@ConnectionState int state) { }
-
- /**
- * Called when the selected destination has changed.
- *
- * @param destination The new selected destination, or null if none.
- */
- public void onSelectedDestinationChanged(@Nullable DestinationInfo destination) { }
-
- /**
- * Called when route discovery has started.
- */
- public void onDiscoveryStarted() { }
-
- /**
- * Called when route discovery has stopped normally.
- * <p>
- * Abnormal termination is reported via {@link #onDiscoveryFailed}.
- * </p>
- */
- public void onDiscoveryStopped() { }
-
- /**
- * Called when discovery has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
- * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
- * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onDiscoveryFailed(@DiscoveryError int error, @Nullable CharSequence message,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a new destination is found or has changed during discovery.
- * <p>
- * Certain destinations may be omitted because they have been filtered
- * out by the media router's routing callback.
- * </p>
- *
- * @param destination The destination that was found.
- */
- public void onDestinationFound(@NonNull DestinationInfo destination) { }
-
- /**
- * Called when a destination is no longer reachable or is no longer
- * offering any routes that satisfy the discovery request.
- *
- * @param destination The destination that went away.
- */
- public void onDestinationLost(@NonNull DestinationInfo destination) { }
-
- /**
- * Called when a connection attempt begins.
- */
- public void onConnecting() { }
-
- /**
- * Called when the connection succeeds.
- */
- public void onConnected() { }
-
- /**
- * Called when the connection is terminated normally.
- * <p>
- * Abnormal termination is reported via {@link #onConnectionFailed}.
- * </p>
- */
- public void onDisconnected() { }
-
- /**
- * Called when a connection attempt or connection in
- * progress has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
- * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
- * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
- * {@link MediaRouter#CONNECTION_ERROR_BUSY},
- * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
- * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
- * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onConnectionFailed(@ConnectionError int error,
- @Nullable CharSequence message, @Nullable Bundle extras) { }
- }
-}
diff --git a/media/java/android/media/routing/ParcelableConnectionInfo.aidl b/media/java/android/media/routing/ParcelableConnectionInfo.aidl
deleted file mode 100644
index 4a9ec94..0000000
--- a/media/java/android/media/routing/ParcelableConnectionInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.
-*/
-
-package android.media.routing;
-
-parcelable ParcelableConnectionInfo;
diff --git a/media/java/android/media/routing/ParcelableConnectionInfo.java b/media/java/android/media/routing/ParcelableConnectionInfo.java
deleted file mode 100644
index 45cfe9f..0000000
--- a/media/java/android/media/routing/ParcelableConnectionInfo.java
+++ /dev/null
@@ -1,71 +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.
- */
-package android.media.routing;
-
-import android.media.AudioAttributes;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Internal parcelable representation of a media route connection.
- */
-class ParcelableConnectionInfo implements Parcelable {
- public AudioAttributes audioAttributes;
- public int presentationDisplayId = -1;
- // todo: volume
- public IBinder[] protocolBinders;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableConnectionInfo> CREATOR =
- new Parcelable.Creator<ParcelableConnectionInfo>() {
- @Override
- public ParcelableConnectionInfo createFromParcel(Parcel source) {
- ParcelableConnectionInfo info = new ParcelableConnectionInfo();
- if (source.readInt() != 0) {
- info.audioAttributes = AudioAttributes.CREATOR.createFromParcel(source);
- }
- info.presentationDisplayId = source.readInt();
- info.protocolBinders = source.createBinderArray();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableConnectionInfo[] newArray(int size) {
- return new ParcelableConnectionInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- if (audioAttributes != null) {
- dest.writeInt(1);
- audioAttributes.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(presentationDisplayId);
- dest.writeBinderArray(protocolBinders);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/routing/ParcelableDestinationInfo.aidl b/media/java/android/media/routing/ParcelableDestinationInfo.aidl
deleted file mode 100644
index bf1c198..0000000
--- a/media/java/android/media/routing/ParcelableDestinationInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.
-*/
-
-package android.media.routing;
-
-parcelable ParcelableDestinationInfo;
diff --git a/media/java/android/media/routing/ParcelableDestinationInfo.java b/media/java/android/media/routing/ParcelableDestinationInfo.java
deleted file mode 100644
index eca5eec..0000000
--- a/media/java/android/media/routing/ParcelableDestinationInfo.java
+++ /dev/null
@@ -1,65 +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.
- */
-package android.media.routing;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-/**
- * Internal parcelable representation of a media destination.
- */
-class ParcelableDestinationInfo implements Parcelable {
- public String id;
- public CharSequence name;
- public CharSequence description;
- public int iconResourceId;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableDestinationInfo> CREATOR =
- new Parcelable.Creator<ParcelableDestinationInfo>() {
- @Override
- public ParcelableDestinationInfo createFromParcel(Parcel source) {
- ParcelableDestinationInfo info = new ParcelableDestinationInfo();
- info.id = source.readString();
- info.name = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- info.description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- info.iconResourceId = source.readInt();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableDestinationInfo[] newArray(int size) {
- return new ParcelableDestinationInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(id);
- TextUtils.writeToParcel(name, dest, flags);
- TextUtils.writeToParcel(description, dest, flags);
- dest.writeInt(iconResourceId);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/routing/ParcelableRouteInfo.aidl b/media/java/android/media/routing/ParcelableRouteInfo.aidl
deleted file mode 100644
index 126afaa..0000000
--- a/media/java/android/media/routing/ParcelableRouteInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.
-*/
-
-package android.media.routing;
-
-parcelable ParcelableRouteInfo;
diff --git a/media/java/android/media/routing/ParcelableRouteInfo.java b/media/java/android/media/routing/ParcelableRouteInfo.java
deleted file mode 100644
index fb1a547..0000000
--- a/media/java/android/media/routing/ParcelableRouteInfo.java
+++ /dev/null
@@ -1,64 +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.
- */
-package android.media.routing;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Internal parcelable representation of a media route.
- */
-class ParcelableRouteInfo implements Parcelable {
- public String id;
- public int selectorIndex; // index of selector within list used for discovery
- public int features;
- public String[] protocols;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableRouteInfo> CREATOR =
- new Parcelable.Creator<ParcelableRouteInfo>() {
- @Override
- public ParcelableRouteInfo createFromParcel(Parcel source) {
- ParcelableRouteInfo info = new ParcelableRouteInfo();
- info.id = source.readString();
- info.selectorIndex = source.readInt();
- info.features = source.readInt();
- info.protocols = source.createStringArray();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableRouteInfo[] newArray(int size) {
- return new ParcelableRouteInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(id);
- dest.writeInt(selectorIndex);
- dest.writeInt(features);
- dest.writeStringArray(protocols);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index af3b72e..bd0019f 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -19,7 +19,6 @@ import android.app.PendingIntent;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
-import android.media.routing.IMediaRouter;
import android.media.session.ISessionController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
@@ -35,7 +34,6 @@ interface ISession {
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaRouter(in IMediaRouter router);
void setMediaButtonReceiver(in PendingIntent mbr);
void setLaunchPendingIntent(in PendingIntent pi);
void destroy();
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 8d58a60..285e5f7 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,8 +20,6 @@ import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.Rating;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaSession;
import android.media.session.ParcelableVolumeInfo;
@@ -51,8 +49,6 @@ interface ISessionController {
void adjustVolume(int direction, int flags, String packageName);
void setVolumeTo(int value, int flags, String packageName);
- IMediaRouterDelegate createMediaRouterDelegate(IMediaRouterStateCallback callback);
-
// These commands are for the TransportControls
void play();
void playFromMediaId(String mediaId, in Bundle extras);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index dd81a22..2acee04 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -26,7 +26,6 @@ import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.MediaRouter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -120,17 +119,6 @@ public final class MediaController {
}
/**
- * Creates a media router delegate through which the destination of the media
- * router may be observed and controlled.
- *
- * @return The media router delegate, or null if the media session does
- * not support media routing.
- */
- public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
- return new MediaRouter.Delegate();
- }
-
- /**
* Send the specified media button event to the session. Only media keys can
* be sent by this method, other keys will be ignored.
*
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index cee82b4..e1e9b79 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -29,7 +29,6 @@ import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.MediaRouter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -225,23 +224,6 @@ public final class MediaSession {
}
/**
- * Associates a {@link MediaRouter} with this session to control the destination
- * of media content.
- * <p>
- * A media router may only be associated with at most one session at a time.
- * </p>
- *
- * @param router The media router, or null to remove the current association.
- */
- public void setMediaRouter(@Nullable MediaRouter router) {
- try {
- mBinder.setMediaRouter(router != null ? router.getBinder() : null);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
- }
- }
-
- /**
* Set a pending intent for your media button receiver to allow restarting
* playback after the session has been stopped. If your app is started in
* this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
index e2df77c..4d3edb8 100644
--- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
+++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
@@ -287,7 +287,7 @@ public abstract class RemoteDisplayProvider {
*/
public PendingIntent getSettingsPendingIntent() {
if (mSettingsPendingIntent == null) {
- Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ Intent settingsIntent = new Intent(Settings.ACTION_CAST_SETTINGS);
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 0521833..6d2de98 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -62,7 +62,7 @@ int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::getSkPixelRef(env, jbitmap);
+ SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
if (!pixelRef) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
@@ -71,9 +71,9 @@ int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
void* addr = pixelRef->pixels();
if (NULL == addr) {
pixelRef->unlockPixels();
+ pixelRef->unref();
return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
}
- pixelRef->ref();
if (addrPtr) {
*addrPtr = addr;
@@ -86,7 +86,7 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) {
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::getSkPixelRef(env, jbitmap);
+ SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
if (!pixelRef) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
@@ -98,6 +98,12 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) {
pixelRef->notifyPixelsChanged();
pixelRef->unlockPixels();
+ // Awkward in that we need to double-unref as the call to get the SkPixelRef
+ // did a ref(), so we need to unref() for the local ref and for the previous
+ // AndroidBitmap_lockPixels(). However this keeps GraphicsJNI a bit safer
+ // if others start using it without knowing about android::Bitmap's "fun"
+ // ref counting mechanism(s).
+ pixelRef->unref();
pixelRef->unref();
return ANDROID_BITMAP_RESULT_SUCCESS;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 845d53a..929258d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -162,7 +162,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
switchImeButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mCallback.userActivity(); // Leave the screen on a bit longer
- mImm.showInputMethodPicker();
+ // Do not show auxiliary subtypes in password lock screen.
+ mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
}
});
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4082bf5..b702e35 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -64,6 +64,8 @@
<string name="wifi_not_in_range">Not in range</string>
<!-- Summary for the remembered network but no internet connection was detected. -->
<string name="wifi_no_internet">No Internet Access Detected, won\'t automatically reconnect.</string>
+ <!-- Summary for saved networks -->
+ <string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
<!-- Status message of Wi-Fi when it is connected by a Wi-Fi assistant application. [CHAR LIMIT=NONE] -->
<string name="connected_via_wfa">Connected via Wi\u2011Fi assistant</string>
@@ -71,6 +73,10 @@
<string name="connected_via_passpoint">Connected via %1$s</string>
<!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
<string name="available_via_passpoint">Available via %1$s</string>
+ <!-- Package name for Settings app-->
+ <string name="settings_package" translatable="false">com.android.settings</string>
+ <!-- Package name for Certinstaller app-->
+ <string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
<!-- Summary for Connected wifi network without internet -->
<string name="wifi_connected_no_internet">Connected, no Internet</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 53e69e3..7eaa728 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -35,6 +35,12 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.LruCache;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.app.AppGlobals;
import com.android.settingslib.R;
@@ -288,12 +294,28 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
public String getSavedNetworkSummary() {
- // Update to new summary
- if (mConfig != null && mConfig.isPasspoint()) {
- return "";
- } else {
- return getSettingsSummary();
+ if (mConfig != null) {
+ PackageManager pm = mContext.getPackageManager();
+ String systemName = pm.getNameForUid(android.os.Process.SYSTEM_UID);
+ int userId = UserHandle.getUserId(mConfig.creatorUid);
+ ApplicationInfo appInfo = null;
+ if (mConfig.creatorName != null && mConfig.creatorName.equals(systemName)) {
+ appInfo = mContext.getApplicationInfo();
+ } else {
+ try {
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ appInfo = ipm.getApplicationInfo(mConfig.creatorName, 0 /* flags */, userId);
+ } catch (RemoteException rex) {
+ }
+ }
+ if (appInfo != null &&
+ !appInfo.packageName.equals(mContext.getString(R.string.settings_package)) &&
+ !appInfo.packageName.equals(
+ mContext.getString(R.string.certinstaller_package))) {
+ return mContext.getString(R.string.saved_network, appInfo.loadLabel(pm));
+ }
}
+ return "";
}
public String getSummary() {
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 595c9ed..0264f3d 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -52,7 +52,7 @@
android:layout_alignParentEnd="true"
android:background="@drawable/btn_borderless_rect"
android:clickable="true"
- android:contentDescription="@string/accessibility_desc_confirm"
+ android:contentDescription="@string/accessibility_desc_close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@android:color/white" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8466a5a..8606a59 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -424,8 +424,8 @@
<string name="accessibility_desc_settings">Settings</string>
<!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_recent_apps">Overview.</string>
- <!-- Content description for the confirm button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
- <string name="accessibility_desc_confirm">Confirm</string>
+ <!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_close">Close</string>
<!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
index c8af2d4..43a1be1 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
@@ -34,4 +34,10 @@ public class EventLogConstants {
public static final int SYSUI_LOCKSCREEN_GESTURE_TAP_LOCK = 6;
/** The user tapped a notification, needs to tap again to launch. */
public static final int SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE = 7;
+ /** The user swiped down to open quick settings, from keyguard. */
+ public static final int SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS = 8;
+ /** The user swiped down to open quick settings, from shade. */
+ public static final int SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS = 9;
+ /** The user tapped on the status bar to open quick settings, from shade. */
+ public static final int SYSUI_TAP_TO_OPEN_QS = 10;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index c62ad66..7077a17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -144,7 +144,7 @@ public class NavigationBarView extends LinearLayout {
@Override
public void onClick(View view) {
((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE))
- .showInputMethodPicker();
+ .showInputMethodPicker(true /* showAuxiliarySubtypes */);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c3ede75..f77ac4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,6 +39,7 @@ import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
@@ -73,6 +74,10 @@ public class NotificationPanelView extends PanelView implements
private static final float HEADER_RUBBERBAND_FACTOR = 2.05f;
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
+ private static final String COUNTER_PANEL_OPEN = "panel_open";
+ private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+ private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
+
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
@@ -541,6 +546,8 @@ public class NotificationPanelView extends PanelView implements
initDownStates(event);
if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
+ MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
+ MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
return true;
}
if (!isFullyCollapsed() && onQsIntercept(event)) {
@@ -617,7 +624,7 @@ public class NotificationPanelView extends PanelView implements
case MotionEvent.ACTION_UP:
trackMovement(event);
if (mQsTracking) {
- flingQsWithCurrentVelocity(
+ flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
mQsTracking = false;
}
@@ -655,9 +662,24 @@ public class NotificationPanelView extends PanelView implements
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
- private void flingQsWithCurrentVelocity(boolean isCancelMotionEvent) {
+ private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
+ float vel = getCurrentVelocity();
+ final boolean expandsQs = flingExpandsQs(vel);
+ if (expandsQs) {
+ logQsSwipeDown(y);
+ }
+ flingSettings(vel, expandsQs && !isCancelMotionEvent);
+ }
+
+ private void logQsSwipeDown(float y) {
float vel = getCurrentVelocity();
- flingSettings(vel, flingExpandsQs(vel) && !isCancelMotionEvent);
+ final int gesture = mStatusBarState == StatusBarState.KEYGUARD
+ ? EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS
+ : EventLogConstants.SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS;
+ EventLogTags.writeSysuiLockscreenGesture(
+ gesture,
+ (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
+ (int) (vel / mStatusBar.getDisplayDensity()));
}
private boolean flingExpandsQs(float vel) {
@@ -699,6 +721,7 @@ public class NotificationPanelView extends PanelView implements
return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+ MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
updateVerticalPanelPosition(event.getX());
}
super.onTouchEvent(event);
@@ -738,6 +761,7 @@ public class NotificationPanelView extends PanelView implements
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
&& event.getPointerCount() == 2
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
+ MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1);
mQsExpandImmediate = true;
requestPanelHeightUpdate();
@@ -799,6 +823,7 @@ public class NotificationPanelView extends PanelView implements
}
final float y = event.getY(pointerIndex);
final float x = event.getX(pointerIndex);
+ final float h = y - mInitialTouchY;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
@@ -826,7 +851,6 @@ public class NotificationPanelView extends PanelView implements
break;
case MotionEvent.ACTION_MOVE:
- final float h = y - mInitialTouchY;
setQsExpansion(h + mInitialHeightOnTouch);
if (h >= getFalsingThreshold()) {
mQsTouchAboveFalsingThreshold = true;
@@ -842,9 +866,10 @@ public class NotificationPanelView extends PanelView implements
float fraction = getQsExpansionFraction();
if ((fraction != 0f || y >= mInitialTouchY)
&& (fraction != 1f || y <= mInitialTouchY)) {
- flingQsWithCurrentVelocity(
+ flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
} else {
+ logQsSwipeDown(y);
mScrollYOverride = -1;
}
if (mVelocityTracker != null) {
@@ -1819,6 +1844,9 @@ public class NotificationPanelView extends PanelView implements
if (mQsExpanded) {
flingSettings(0 /* vel */, false /* expand */);
} else if (mQsExpansionEnabled) {
+ EventLogTags.writeSysuiLockscreenGesture(
+ EventLogConstants.SYSUI_TAP_TO_OPEN_QS,
+ 0, 0);
flingSettings(0 /* vel */, true /* expand */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index 23a65e8..ca32567 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -93,7 +93,7 @@ public class UsbResolverActivity extends ResolverActivity {
}
@Override
- protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) {
+ protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
final ResolveInfo ri = target.getResolveInfo();
try {
IBinder b = ServiceManager.getService(USB_SERVICE);
@@ -129,5 +129,6 @@ public class UsbResolverActivity extends ResolverActivity {
} catch (RemoteException e) {
Log.e(TAG, "onIntentSelected failed", e);
}
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 7115897..1e34663 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -788,6 +788,7 @@ public class VolumeDialog {
public void onConfigurationChanged() {
updateWindowWidthH();
mSpTexts.update();
+ mZenFooter.onConfigurationChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 8aded45..ccb2b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -36,10 +36,11 @@ public class ZenFooter extends LinearLayout {
private static final String TAG = Util.logTag(ZenFooter.class);
private final Context mContext;
+ private final SpTexts mSpTexts;
private TextView mSummaryLine1;
private TextView mSummaryLine2;
- private View mEndNowButton;
+ private TextView mEndNowButton;
private int mZen = -1;
private ZenModeConfig mConfig;
private ZenModeController mController;
@@ -47,6 +48,7 @@ public class ZenFooter extends LinearLayout {
public ZenFooter(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
+ mSpTexts = new SpTexts(mContext);
setLayoutTransition(new LayoutTransition());
}
@@ -55,7 +57,10 @@ public class ZenFooter extends LinearLayout {
super.onFinishInflate();
mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1);
mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2);
- mEndNowButton = findViewById(R.id.volume_zen_end_now);
+ mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now);
+ mSpTexts.add(mSummaryLine1);
+ mSpTexts.add(mSummaryLine2);
+ mSpTexts.add(mEndNowButton);
}
public void init(final ZenModeController controller) {
@@ -122,4 +127,8 @@ public class ZenFooter extends LinearLayout {
Util.setText(mSummaryLine2, line2);
}
+ public void onConfigurationChanged() {
+ mSpTexts.update();
+ }
+
}
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 6a1efee..7cd6d09 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -182,9 +182,9 @@ public class Script extends BaseObj {
mRS.validateObject(ain);
mRS.validateObject(aout);
- if (ain == null && aout == null) {
+ if (ain == null && aout == null && sc == null) {
throw new RSIllegalArgumentException(
- "At least one of ain or aout is required to be non-null.");
+ "At least one of input allocation, output allocation, or LaunchOptions is required to be non-null.");
}
long[] in_ids = null;
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 80d6515..58d0fce 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "libRS_jni"
+#define LOG_TAG "RenderScript_jni"
#include <stdlib.h>
#include <stdio.h>
@@ -1857,7 +1857,7 @@ nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
jintArray limits)
{
if (kLogApi) {
- ALOGD("nScriptForEach, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
+ ALOGD("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%lli)", (RsContext)con, (void *)script, slot, ains, aout);
}
jint in_len = 0;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index bfe8b5c..6c1023c 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -9368,44 +9368,47 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
throw new SecurityException("No permission to restore other packages");
}
- // So far so good; we're allowed to try to restore this package. Now
- // check whether there is data for it in the current dataset, falling back
- // to the ancestral dataset if not.
- long token = getAvailableRestoreToken(packageName);
- if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
- + " token=" + Long.toHexString(token));
-
- // If we didn't come up with a place to look -- no ancestral dataset and
- // the app has never been backed up from this device -- there's nothing
- // to do but return failure.
- if (token == 0) {
- if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
- return -1;
- }
-
- String dirName;
+ // So far so good; we're allowed to try to restore this package.
+ long oldId = Binder.clearCallingIdentity();
try {
- dirName = mRestoreTransport.transportDirName();
- } catch (RemoteException e) {
- // Transport went AWOL; fail.
- Slog.e(TAG, "Unable to contact transport for restore");
- return -1;
- }
+ // Check whether there is data for it in the current dataset, falling back
+ // to the ancestral dataset if not.
+ long token = getAvailableRestoreToken(packageName);
+ if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
+ + " token=" + Long.toHexString(token));
+
+ // If we didn't come up with a place to look -- no ancestral dataset and
+ // the app has never been backed up from this device -- there's nothing
+ // to do but return failure.
+ if (token == 0) {
+ if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
+ return -1;
+ }
- // Stop the session timeout until we finalize the restore
- mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+ String dirName;
+ try {
+ dirName = mRestoreTransport.transportDirName();
+ } catch (RemoteException e) {
+ // Transport went AWOL; fail.
+ Slog.e(TAG, "Unable to contact transport for restore");
+ return -1;
+ }
- // Ready to go: enqueue the restore request and claim success
- long oldId = Binder.clearCallingIdentity();
- mWakelock.acquire();
- if (MORE_DEBUG) {
- Slog.d(TAG, "restorePackage() : " + packageName);
+ // Stop the session timeout until we finalize the restore
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+
+ // Ready to go: enqueue the restore request and claim success
+ mWakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "restorePackage() : " + packageName);
+ }
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, dirName,
+ observer, token, app, 0);
+ mBackupHandler.sendMessage(msg);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, dirName,
- observer, token, app, 0);
- mBackupHandler.sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
return 0;
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 1019faa..32b91d2 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -512,7 +512,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private void onBluetoothGattServiceUp() {
if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
try{
- if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ if (isBleAppPresent() == false && mBluetooth != null
+ && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
mBluetooth.onLeServiceUp();
// waive WRITE_SECURE_SETTINGS permission check
@@ -531,32 +532,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
*/
private void sendBrEdrDownCallback() {
if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
- int n = mCallbacks.beginBroadcast();
+
+ if(mBluetooth == null) {
+ Log.w(TAG, "Bluetooth handle is null");
+ return;
+ }
if (isBleAppPresent() == false) {
try {
mBluetooth.onBrEdrDown();
} catch(RemoteException e) {
- Log.e(TAG,"Unable to call onBrEdrDown", e);
+ Log.e(TAG, "Call to onBrEdrDown() failed.", e);
}
- }
- else{//need to stay at BLE ON. disconnect all Gatt connections
+ } else {
+ // Need to stay at BLE ON. Disconnect all Gatt connections
try{
- mBluetoothGatt.unregAll();//disconnectAll();
+ mBluetoothGatt.unregAll();
} catch(RemoteException e) {
- Log.e(TAG,"Unable to disconn all", e);
- }
- }
-
- Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
- for (int i=0; i <n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBrEdrDown();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
+ Log.e(TAG, "Unable to disconnect all apps.", e);
}
}
- mCallbacks.finishBroadcast();
}
/** @hide*/
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 45909db..9511f54 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2038,11 +2038,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+ uid + ": " + client);
- setImeWindowVisibilityStatusHiddenLocked();
return false;
}
} catch (RemoteException e) {
- setImeWindowVisibilityStatusHiddenLocked();
return false;
}
}
@@ -2238,7 +2236,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void showInputMethodPickerFromClient(IInputMethodClient client) {
+ public void showInputMethodPickerFromClient(
+ IInputMethodClient client, int auxiliarySubtypeMode) {
if (!calledFromValidUser()) {
return;
}
@@ -2251,7 +2250,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Always call subtype picker, because subtype picker is a superset of input method
// picker.
- mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER);
+ mHandler.sendMessage(mCaller.obtainMessageI(
+ MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode));
}
}
@@ -2597,7 +2597,25 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
SomeArgs args;
switch (msg.what) {
case MSG_SHOW_IM_SUBTYPE_PICKER:
- showInputMethodMenu();
+ final boolean showAuxSubtypes;
+ switch (msg.arg1) {
+ case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
+ // This is undocumented so far, but IMM#showInputMethodPicker() has been
+ // implemented so that auxiliary subtypes will be excluded when the soft
+ // keyboard is invisible.
+ showAuxSubtypes = mInputShown;
+ break;
+ case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
+ showAuxSubtypes = true;
+ break;
+ case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES:
+ showAuxSubtypes = false;
+ break;
+ default:
+ Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
+ return false;
+ }
+ showInputMethodMenu(showAuxSubtypes);
return true;
case MSG_SHOW_IM_SUBTYPE_ENABLER:
@@ -2880,8 +2898,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
&& mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
}
- private void showInputMethodMenu() {
- if (DEBUG) Slog.v(TAG, "Show switching menu");
+ private void showInputMethodMenu(boolean showAuxSubtypes) {
+ if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
final Context context = mContext;
final boolean isScreenLocked = isScreenLocked();
@@ -2902,7 +2920,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final List<ImeSubtypeListItem> imList =
mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
- true /* showSubtypes */, mInputShown, isScreenLocked);
+ true /* showSubtypes */, showAuxSubtypes, isScreenLocked);
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a07591c9..ac55292 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -488,7 +488,8 @@ public class Vpn {
try {
// Restricted users are not allowed to create VPNs, they are tied to Owner
UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ new UserHandle(mUserHandle))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -896,7 +897,8 @@ public class Vpn {
}
UserManager mgr = UserManager.get(mContext);
UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ new UserHandle(mUserHandle))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7cccef2..3dc282b 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -52,6 +52,7 @@ import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.BatteryStats;
@@ -99,6 +100,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
@@ -157,7 +159,19 @@ public class SyncManager {
/**
* How long to wait before considering an active sync to have timed-out, and cancelling it.
*/
- private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins.
+ private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins
+
+ /**
+ * How long to delay each queued {@link SyncHandler} message that may have occurred before boot
+ * or befor the device became provisioned.
+ */
+ private static final long PER_SYNC_BOOT_DELAY_MILLIS = 3000L; // 3 seconds
+
+ /**
+ * The maximum amount of time we're willing to delay syncs out of boot, after device has been
+ * provisioned, etc.
+ */
+ private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 120000L; // 2 minutes
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
@@ -198,6 +212,9 @@ public class SyncManager {
// its accessor, getConnManager().
private ConnectivityManager mConnManagerDoNotUseDirectly;
+ /** Track whether the device has already been provisioned. */
+ private boolean mProvisioned;
+
protected SyncAdaptersCache mSyncAdapters;
private final AppIdleMonitor mAppIdleMonitor;
@@ -242,6 +259,7 @@ public class SyncManager {
private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ mBootCompleted = true;
mSyncHandler.onBootCompleted();
}
};
@@ -491,12 +509,41 @@ public class SyncManager {
mSyncStorageEngine.addStatusChangeListener(
ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
- @Override
- public void onStatusChanged(int which) {
- // force the sync loop to run if the settings change
- sendCheckAlarmsMessage();
+ @Override
+ public void onStatusChanged(int which) {
+ // force the sync loop to run if the settings change
+ sendCheckAlarmsMessage();
+ }
+ });
+
+ mProvisioned = isDeviceProvisioned();
+ if (!mProvisioned) {
+ final ContentResolver resolver = context.getContentResolver();
+ ContentObserver provisionedObserver =
+ new ContentObserver(null /* current thread */) {
+ public void onChange(boolean selfChange) {
+ mProvisioned |= isDeviceProvisioned();
+ if (mProvisioned) {
+ mSyncHandler.onDeviceProvisioned();
+ resolver.unregisterContentObserver(this);
+ }
+ }
+ };
+
+ synchronized (mSyncHandler) {
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false /* notifyForDescendents */,
+ provisionedObserver);
+
+ // The device *may* have been provisioned while we were registering above observer.
+ // Check again to make sure.
+ mProvisioned |= isDeviceProvisioned();
+ if (mProvisioned) {
+ resolver.unregisterContentObserver(provisionedObserver);
+ }
}
- });
+ }
if (!factoryTest) {
// Register for account list updates for all users
@@ -510,6 +557,10 @@ public class SyncManager {
mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
}
+ private boolean isDeviceProvisioned() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+ }
/**
* Return a random value v that satisfies minValue <= v < maxValue. The difference between
* maxValue and minValue must be less than Integer.MAX_VALUE.
@@ -2000,20 +2051,36 @@ public class SyncManager {
public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
- private List<Message> mBootQueue = new ArrayList<Message>();
+ private List<Message> mUnreadyQueue = new ArrayList<Message>();
- public void onBootCompleted() {
+ void onBootCompleted() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Boot completed, clearing boot queue.");
}
doDatabaseCleanup();
synchronized(this) {
// Dispatch any stashed messages.
- for (Message message : mBootQueue) {
- sendMessage(message);
+ maybeEmptyUnreadyQueueLocked();
+ }
+ }
+
+ void onDeviceProvisioned() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "mProvisioned=" + mProvisioned);
+ }
+ synchronized (this) {
+ maybeEmptyUnreadyQueueLocked();
+ }
+ }
+
+ private void maybeEmptyUnreadyQueueLocked() {
+ if (mProvisioned && mBootCompleted) {
+ // Dispatch any stashed messages.
+ for (int i=0; i<mUnreadyQueue.size(); i++) {
+ sendMessageDelayed(mUnreadyQueue.get(i),
+ Math.max(PER_SYNC_BOOT_DELAY_MILLIS * i, MAX_SYNC_BOOT_DELAY_MILLIS));
}
- mBootQueue = null;
- mBootCompleted = true;
+ mUnreadyQueue = null;
}
}
@@ -2030,20 +2097,23 @@ public class SyncManager {
}
/**
- * Stash any messages that come to the handler before boot is complete.
- * {@link #onBootCompleted()} will disable this and dispatch all the messages collected.
+ * Stash any messages that come to the handler before boot is complete or before the device
+ * is properly provisioned (i.e. out of set-up wizard).
+ * {@link #onBootCompleted()} and {@link #onDeviceProvisioned(boolean)} both need to come
+ * in before we start syncing.
* @param msg Message to dispatch at a later point.
* @return true if a message was enqueued, false otherwise. This is to avoid losing the
* message if we manage to acquire the lock but by the time we do boot has completed.
*/
private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
synchronized (this) {
- if (!mBootCompleted) {
+ if (!mBootCompleted || !mProvisioned) {
// Need to copy the message bc looper will recycle it.
- mBootQueue.add(Message.obtain(msg));
+ mUnreadyQueue.add(Message.obtain(msg));
return true;
+ } else {
+ return false;
}
- return false;
}
}
@@ -2100,7 +2170,7 @@ public class SyncManager {
}
cancelActiveSync(expiredContext.mSyncOperation.target,
expiredContext.mSyncOperation.extras);
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
case SyncHandler.MESSAGE_CANCEL: {
@@ -2111,7 +2181,7 @@ public class SyncManager {
+ payload + " bundle: " + extras);
}
cancelActiveSyncLocked(payload, extras);
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
}
@@ -2120,17 +2190,17 @@ public class SyncManager {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
}
SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj;
- if (!isSyncStillActive(payload.activeSyncContext)) {
+ if (!isSyncStillActiveH(payload.activeSyncContext)) {
Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+ "sync is no longer active: "
+ payload.activeSyncContext);
break;
}
- runSyncFinishedOrCanceledLocked(payload.syncResult,
+ runSyncFinishedOrCanceledH(payload.syncResult,
payload.activeSyncContext);
// since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
@@ -2140,7 +2210,7 @@ public class SyncManager {
+ msgData.activeSyncContext);
}
// check that this isn't an old message
- if (isSyncStillActive(msgData.activeSyncContext)) {
+ if (isSyncStillActiveH(msgData.activeSyncContext)) {
runBoundToAdapter(
msgData.activeSyncContext,
msgData.adapter);
@@ -2156,7 +2226,7 @@ public class SyncManager {
+ currentSyncContext);
}
// check that this isn't an old message
- if (isSyncStillActive(currentSyncContext)) {
+ if (isSyncStillActiveH(currentSyncContext)) {
// cancel the sync if we have a syncadapter, which means one is
// outstanding
try {
@@ -2174,10 +2244,10 @@ public class SyncManager {
// which is a soft error
SyncResult syncResult = new SyncResult();
syncResult.stats.numIoExceptions++;
- runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
+ runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
// since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
}
break;
@@ -2190,7 +2260,7 @@ public class SyncManager {
}
mAlarmScheduleTime = null;
try {
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
} finally {
mHandleAlarmWakeLock.release();
}
@@ -2201,7 +2271,7 @@ public class SyncManager {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
}
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
}
} finally {
@@ -2393,7 +2463,7 @@ public class SyncManager {
0 : (earliestFuturePollTime - nowAbsolute));
}
- private long maybeStartNextSyncLocked() {
+ private long maybeStartNextSyncH() {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) Log.v(TAG, "maybeStartNextSync");
@@ -2612,7 +2682,7 @@ public class SyncManager {
}
if (toReschedule != null) {
- runSyncFinishedOrCanceledLocked(null, toReschedule);
+ runSyncFinishedOrCanceledH(null, toReschedule);
scheduleSyncOperation(toReschedule.mSyncOperation);
}
synchronized (mSyncQueue) {
@@ -2845,14 +2915,14 @@ public class SyncManager {
false /* no config settings */)) {
continue;
}
- runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
+ runSyncFinishedOrCanceledH(null /* no result since this is a cancel */,
activeSyncContext);
}
}
}
- private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
- ActiveSyncContext activeSyncContext) {
+ private void runSyncFinishedOrCanceledH(SyncResult syncResult,
+ ActiveSyncContext activeSyncContext) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3257,7 +3327,7 @@ public class SyncManager {
}
}
- private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
+ private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
for (ActiveSyncContext sync : mActiveSyncContexts) {
if (sync == activeSyncContext) {
return true;
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index e434f39..8c12060 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -214,6 +214,10 @@ final class Constants {
// values which denotes the device type in HDMI Spec 1.4.
static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
+ // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
+ // True by default.
+ static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
+
// Set to false to allow playback device to go to suspend mode even
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 89ffe45..fd3364a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -34,6 +34,9 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDevicePlayback";
+ private static final boolean WAKE_ON_HOTPLUG =
+ SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+
private boolean mIsActiveSource = false;
// Used to keep the device awake while it is the active source. For devices that
@@ -130,7 +133,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
assertRunOnServiceThread();
mCecMessageCache.flushAll();
// We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
- if (connected && mService.isPowerStandbyOrTransient()) {
+ if (WAKE_ON_HOTPLUG && connected && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
if (!connected) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5ac027d..e650456 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -486,6 +486,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId());
mDelayedMessageBuffer.add(message);
} else {
+ updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
}
@@ -1613,6 +1614,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
super.disableDevice(initiatedByCec, callback);
clearDeviceInfoList();
+ getActiveSource().invalidate();
+ setActivePath(Constants.INVALID_PHYSICAL_ADDRESS);
checkIfPendingActionsCleared();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 09d0501..dca762c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -27,9 +27,6 @@ import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.IMediaRouter;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionController;
@@ -718,11 +715,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
- public void setMediaRouter(IMediaRouter router) {
- mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
- }
-
- @Override
public void setMediaButtonReceiver(PendingIntent pi) {
mMediaButtonReceiver = pi;
}
@@ -1209,13 +1201,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
public boolean isTransportControlEnabled() {
return MediaSessionRecord.this.isTransportControlEnabled();
}
-
- @Override
- public IMediaRouterDelegate createMediaRouterDelegate(
- IMediaRouterStateCallback callback) {
- // todo
- return null;
- }
}
private class MessageHandler extends Handler {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 51503ec..dbd3676 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -93,7 +93,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyCharacterMap.FallbackAction;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index c48367e..fd98010 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -539,9 +539,11 @@ final class Notifier {
};
private void playWirelessChargingStartedSound() {
+ final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
- if (soundPath != null) {
+ if (enabled && soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index fd0c06d..d3df151 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -20,6 +20,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
@@ -151,14 +152,14 @@ public class DefaultDialerManager {
for (ResolveInfo resolveInfo : resolveInfoList) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
+ if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+ packageNames.add(activityInfo.packageName);
}
- packageNames.add(activityInfo.packageName);
}
- // TODO: Filter for apps that don't handle DIAL intent with tel scheme
- return packageNames;
+ final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
+ dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
+ return filterByIntent(context, packageNames, dialIntentWithTelScheme);
}
/**
@@ -182,6 +183,36 @@ public class DefaultDialerManager {
|| packageName.equals(tm.getSystemDialerPackage());
}
+ /**
+ * Filter a given list of package names for those packages that contain an activity that has
+ * an intent filter for a given intent.
+ *
+ * @param context A valid context
+ * @param packageNames List of package names to filter.
+ * @return The filtered list.
+ */
+ private static List<String> filterByIntent(Context context, List<String> packageNames,
+ Intent intent) {
+ if (packageNames == null || packageNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final List<String> result = new ArrayList<>();
+ final List<ResolveInfo> resolveInfoList =
+ context.getPackageManager().queryIntentActivities(intent, 0);
+ final int length = resolveInfoList.size();
+ for (int i = 0; i < length; i++) {
+ final ActivityInfo info = resolveInfoList.get(i).activityInfo;
+ if (info != null && packageNames.contains(info.packageName)
+ && !result.contains(info.packageName)) {
+ result.add(info.packageName);
+ }
+ }
+
+ return result;
+ }
+
+
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1431eb8..ebd3f12 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1143,8 +1143,12 @@ public class TelecomManager {
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
+ if (address == null) {
+ Log.w(TAG, "Cannot place call to empty address.");
+ }
try {
- service.placeCall(address, extras, mContext.getOpPackageName());
+ service.placeCall(address, extras == null ? new Bundle() : extras,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
diff --git a/tests/OneMedia/Android.mk b/tests/OneMedia/Android.mk
index b7d7f98..9fc6403 100644
--- a/tests/OneMedia/Android.mk
+++ b/tests/OneMedia/Android.mk
@@ -9,9 +9,6 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) \
LOCAL_PACKAGE_NAME := OneMedia
LOCAL_CERTIFICATE := platform
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-media-protocols
-
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index ef3fad5..c6824ec 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -27,15 +27,6 @@
android:name="com.android.onemedia.OnePlayerService"
android:exported="true"
android:process="com.android.onemedia.service" />
- <service
- android:name=".provider.OneMediaRouteProvider"
- android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE"
- android:exported="true"
- android:process="com.android.onemedia.provider">
- <intent-filter>
- <action android:name="android.media.routing.MediaRouteService" />
- </intent-filter>
- </service>
</application>
</manifest>
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 141a209..2455c9c 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -19,25 +19,17 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.MediaRouter;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.RouteInfo;
import android.media.session.MediaSession;
import android.media.session.MediaSession.QueueItem;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import com.android.onemedia.playback.LocalRenderer;
-import com.android.onemedia.playback.OneMRPRenderer;
import com.android.onemedia.playback.Renderer;
import com.android.onemedia.playback.RequestUtils;
@@ -48,7 +40,6 @@ public class PlayerSession {
private static final String TAG = "PlayerSession";
protected MediaSession mSession;
- protected MediaRouter mRouter;
protected Context mContext;
protected Renderer mRenderer;
protected MediaSession.Callback mCallback;
@@ -84,22 +75,11 @@ public class PlayerSession {
.getSystemService(Context.MEDIA_SESSION_SERVICE);
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
- mRouter = new MediaRouter(mContext);
- mRouter.addSelector(new MediaRouteSelector.Builder()
- .addRequiredProtocol(MediaPlayerProtocol.class)
- .build());
- mRouter.addSelector(new MediaRouteSelector.Builder()
- .setRequiredFeatures(MediaRouter.ROUTE_FEATURE_LIVE_AUDIO)
- .setOptionalFeatures(MediaRouter.ROUTE_FEATURE_LIVE_VIDEO)
- .build());
- mRouter.setRoutingCallback(new RoutingCallback(), null);
-
mSession = new MediaSession(mContext, "OneMedia");
mSession.setCallback(mCallback);
mSession.setPlaybackState(mPlaybackState);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS
| MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
- mSession.setMediaRouter(mRouter);
mSession.setActive(true);
updateMetadata();
}
@@ -117,10 +97,6 @@ public class PlayerSession {
mSession.release();
mSession = null;
}
- if (mRouter != null) {
- mRouter.release();
- mRouter = null;
- }
}
public void setListener(Listener listener) {
@@ -278,63 +254,4 @@ public class PlayerSession {
mRenderer.onPause();
}
}
-
- private class RoutingCallback extends MediaRouter.RoutingCallback {
- @Override
- public void onConnectionStateChanged(int state) {
- if (state == MediaRouter.CONNECTION_STATE_CONNECTING) {
- if (mRenderer != null) {
- mRenderer.onStop();
- }
- mRenderer = null;
- updateState(PlaybackState.STATE_CONNECTING);
- return;
- }
-
- MediaRouter.ConnectionInfo connection = mRouter.getConnection();
- if (connection != null) {
- MediaPlayerProtocol protocol =
- connection.getProtocolObject(MediaPlayerProtocol.class);
- if (protocol != null) {
- Log.d(TAG, "Connected to route using media player protocol");
-
- protocol.setCallback(new PlayerCallback(), null);
- mRenderer = new OneMRPRenderer(protocol);
- updateState(PlaybackState.STATE_NONE);
- return;
- }
- }
-
- // Use local route
- mRenderer = new LocalRenderer(mContext, null);
- mRenderer.registerListener(mRenderListener);
- updateState(PlaybackState.STATE_NONE);
- }
- }
-
- private class PlayerCallback extends MediaPlayerProtocol.Callback {
- @Override
- public void onStatusUpdated(MediaStatus status, Bundle extras) {
- if (status != null) {
- Log.d(TAG, "Received status update: " + status.toBundle());
- switch (status.getPlayerState()) {
- case MediaStatus.PLAYER_STATE_BUFFERING:
- updateState(PlaybackState.STATE_BUFFERING);
- break;
- case MediaStatus.PLAYER_STATE_IDLE:
- updateState(PlaybackState.STATE_STOPPED);
- break;
- case MediaStatus.PLAYER_STATE_PAUSED:
- updateState(PlaybackState.STATE_PAUSED);
- break;
- case MediaStatus.PLAYER_STATE_PLAYING:
- updateState(PlaybackState.STATE_PLAYING);
- break;
- case MediaStatus.PLAYER_STATE_UNKNOWN:
- updateState(PlaybackState.STATE_NONE);
- break;
- }
- }
- }
- }
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
deleted file mode 100644
index 55eb92c..0000000
--- a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.onemedia.playback;
-
-import android.os.Bundle;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
-
-/**
- * Renderer for communicating with the OneMRP route
- */
-public class OneMRPRenderer extends Renderer {
- private final MediaPlayerProtocol mProtocol;
-
- public OneMRPRenderer(MediaPlayerProtocol protocol) {
- super(null, null);
- mProtocol = protocol;
- }
-
- @Override
- public void setContent(Bundle request) {
- MediaInfo mediaInfo = new MediaInfo(request.getString(RequestUtils.EXTRA_KEY_SOURCE),
- MediaInfo.STREAM_TYPE_BUFFERED, "audio/mp3");
- mProtocol.load(mediaInfo, true, 0, null);
- }
-
- @Override
- public boolean onStop() {
- mProtocol.stop(null);
- return true;
- }
-
- @Override
- public boolean onPlay() {
- mProtocol.play(null);
- return true;
- }
-
- @Override
- public boolean onPause() {
- mProtocol.pause(null);
- return true;
- }
-
- @Override
- public long getSeekPosition() {
- return -1;
- }
-}
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
deleted file mode 100644
index 5845e48..0000000
--- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
+++ /dev/null
@@ -1,270 +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.
- */
-package com.android.onemedia.provider;
-
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.MediaRouteService;
-import android.media.routing.MediaRouter.ConnectionInfo;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.DiscoveryRequest;
-import android.media.routing.MediaRouter.RouteInfo;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Process;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
-import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.onemedia.playback.LocalRenderer;
-import com.android.onemedia.playback.Renderer;
-import com.android.onemedia.playback.RequestUtils;
-
-import java.util.ArrayList;
-
-/**
- * Test of MediaRouteProvider. Show a dummy provider with a simple interface for
- * playing music.
- */
-public class OneMediaRouteProvider extends MediaRouteService {
- private static final String TAG = "OneMRP";
- private static final boolean DEBUG = true;
-
- private static final String TEST_DESTINATION_ID = "testDestination";
- private static final String TEST_ROUTE_ID = "testRoute";
-
- private Renderer mRenderer;
- private RenderListener mRenderListener;
- private PlaybackState mPlaybackState;
- private Handler mHandler;
-
- private OneStub mStub;
-
- @Override
- public void onCreate() {
- mHandler = new Handler();
- mRenderer = new LocalRenderer(this, null);
- mRenderListener = new RenderListener();
- PlaybackState.Builder bob = new PlaybackState.Builder();
- bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
- mPlaybackState = bob.build();
-
- mRenderer.registerListener(mRenderListener);
- }
-
- @Override
- public ClientSession onCreateClientSession(ClientInfo client) {
- if (client.getUid() != Process.myUid()) {
- // for testing purposes, only allow connections from this application
- // since this provider is not fully featured
- return null;
- }
- return new OneSession(client);
- }
-
- private final class OneSession extends ClientSession {
- private final ClientInfo mClient;
-
- public OneSession(ClientInfo client) {
- mClient = client;
- }
-
- @Override
- public boolean onStartDiscovery(DiscoveryRequest req, DiscoveryCallback callback) {
- for (MediaRouteSelector selector : req.getSelectors()) {
- if (isMatch(selector)) {
- DestinationInfo destination = new DestinationInfo.Builder(
- TEST_DESTINATION_ID, getServiceMetadata(), "OneMedia")
- .setDescription("Test route from OneMedia app.")
- .build();
- ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
- routes.add(new RouteInfo.Builder(
- TEST_ROUTE_ID, destination, selector).build());
- callback.onDestinationFound(destination, routes);
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onStopDiscovery() {
- }
-
- @Override
- public boolean onConnect(ConnectionRequest req, ConnectionCallback callback) {
- if (req.getRoute().getId().equals(TEST_ROUTE_ID)) {
- mStub = new OneStub();
- ConnectionInfo connection = new ConnectionInfo.Builder(req.getRoute())
- .setProtocolStub(MediaPlayerProtocol.class, mStub)
- .build();
- callback.onConnected(connection);
- return true;
- }
- return false;
- }
-
- @Override
- public void onDisconnect() {
- mStub = null;
- }
-
- private boolean isMatch(MediaRouteSelector selector) {
- if (!selector.containsProtocol(MediaPlayerProtocol.class)) {
- return false;
- }
- for (String protocol : selector.getRequiredProtocols()) {
- if (!protocol.equals(MediaPlayerProtocol.class.getName())) {
- return false;
- }
- }
- return true;
- }
- }
-
- private final class OneStub extends MediaPlayerProtocol.Stub {
- MediaInfo mMediaInfo;
-
- public OneStub() {
- super(mHandler);
- }
-
- @Override
- public void onLoad(MediaInfo mediaInfo, boolean autoplay, long playPosition,
- Bundle extras) {
- if (DEBUG) {
- Log.d(TAG, "Attempting to play " + mediaInfo.getContentId());
- }
- // look up the route and send a play command to it
- mMediaInfo = mediaInfo;
- Bundle bundle = new Bundle();
- bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, mediaInfo.getContentId());
- mRenderer.setContent(bundle);
- }
-
- @Override
- public void onPlay(Bundle extras) {
- mRenderer.onPlay();
- }
-
- @Override
- public void onPause(Bundle extras) {
- mRenderer.onPause();
- }
- }
-
- private class RenderListener implements Renderer.Listener {
-
- @Override
- public void onError(int type, int extra, Bundle extras, Throwable error) {
- Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
- sendStatusUpdate(PlaybackState.STATE_ERROR);
- }
-
- @Override
- public void onStateChanged(int newState) {
- long position = -1;
- if (mRenderer != null) {
- position = mRenderer.getSeekPosition();
- }
- int pbState;
- float rate = 0;
- String errorMsg = null;
- switch (newState) {
- case Renderer.STATE_ENDED:
- case Renderer.STATE_STOPPED:
- pbState = PlaybackState.STATE_STOPPED;
- break;
- case Renderer.STATE_INIT:
- case Renderer.STATE_PREPARING:
- pbState = PlaybackState.STATE_BUFFERING;
- break;
- case Renderer.STATE_ERROR:
- pbState = PlaybackState.STATE_ERROR;
- break;
- case Renderer.STATE_PAUSED:
- pbState = PlaybackState.STATE_PAUSED;
- break;
- case Renderer.STATE_PLAYING:
- pbState = PlaybackState.STATE_PLAYING;
- rate = 1;
- break;
- default:
- pbState = PlaybackState.STATE_ERROR;
- errorMsg = "unknown state";
- break;
- }
- PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
- bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
- bob.setErrorMessage(errorMsg);
- mPlaybackState = bob.build();
-
- sendStatusUpdate(mPlaybackState.getState());
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
- }
-
- @Override
- public void onFocusLost() {
- Log.d(TAG, "Focus lost, pausing");
- // Don't update state here, we'll get a separate call to
- // onStateChanged when it pauses
- mRenderer.onPause();
- }
-
- @Override
- public void onNextStarted() {
- }
-
- private void sendStatusUpdate(int state) {
- if (mStub != null) {
- MediaStatus status = new MediaStatus(1, mStub.mMediaInfo);
- switch (state) {
- case PlaybackState.STATE_BUFFERING:
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
- status.setPlayerState(MediaStatus.PLAYER_STATE_BUFFERING);
- break;
- case PlaybackState.STATE_CONNECTING:
- case PlaybackState.STATE_STOPPED:
- status.setPlayerState(MediaStatus.PLAYER_STATE_IDLE);
- break;
- case PlaybackState.STATE_PAUSED:
- status.setPlayerState(MediaStatus.PLAYER_STATE_PAUSED);
- break;
- case PlaybackState.STATE_PLAYING:
- status.setPlayerState(MediaStatus.PLAYER_STATE_PLAYING);
- break;
- case PlaybackState.STATE_NONE:
- case PlaybackState.STATE_ERROR:
- default:
- status.setPlayerState(MediaStatus.PLAYER_STATE_UNKNOWN);
- break;
- }
- mStub.sendStatusUpdatedEvent(status, null);
- }
- }
- }
-}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 9eea663..87762a6 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -16,19 +16,15 @@
package android.view;
-import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.MergeCookie;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType;
import com.android.layoutlib.bridge.impl.ParserFactory;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.resources.ResourceType;
import com.android.util.Pair;
@@ -233,22 +229,6 @@ public final class BridgeInflater extends LayoutInflater {
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
- if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
- String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES,
- BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE);
- if (type != null) {
- LayoutManagerType layoutManagerType = LayoutManagerType.getByLogicalName(type);
- if (layoutManagerType == null) {
- layoutManagerType = LayoutManagerType.getByClassName(type);
- }
- if (layoutManagerType == null) {
- // add the classname itself.
- bc.addCookie(view, type);
- } else {
- bc.addCookie(view, layoutManagerType);
- }
- }
- }
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index ec3a8d6..30512aa 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -19,6 +19,7 @@ package android.view;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
import android.graphics.LinearGradient;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -125,6 +126,9 @@ public class RectShadowPainter {
private static void sideShadow(Canvas canvas, Paint edgePaint,
RectF edgeShadowRect, float dx, float dy, int rotations) {
+ if (isRectEmpty(edgeShadowRect)) {
+ return;
+ }
int saved = canvas.save();
canvas.translate(dx, dy);
canvas.rotate(rotations * PERPENDICULAR_ANGLE);
@@ -153,4 +157,15 @@ public class RectShadowPainter {
canvas.drawPath(path, paint);
canvas.restoreToCount(saved);
}
+
+ /**
+ * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks.
+ * <p/>
+ * This is required because {@link Canvas_Delegate#native_drawRect(long, float, float, float,
+ * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up
+ * drawing empty rectangles, which results in IllegalArgumentException.
+ */
+ private static boolean isRectEmpty(RectF rect) {
+ return (int) rect.left >= (int) rect.right || (int) rect.top >= (int) rect.bottom;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 094778d..eb5f597 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -93,6 +93,8 @@ import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
+
/**
* Custom implementation of Context/Activity to handle non compiled resources.
*/
@@ -306,7 +308,7 @@ public final class BridgeContext extends Context {
// check if this is a style resource
if (value instanceof StyleResourceValue) {
// get the id that will represent this style.
- outValue.resourceId = getDynamicIdByStyle((StyleResourceValue)value);
+ outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value);
return true;
}
@@ -812,6 +814,14 @@ public final class BridgeContext extends Context {
}
+ @Override
+ public String getPackageName() {
+ if (mApplicationInfo.packageName == null) {
+ mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE);
+ }
+ return mApplicationInfo.packageName;
+ }
+
// ------------- private new methods
/**
@@ -1225,12 +1235,6 @@ public final class BridgeContext extends Context {
}
@Override
- public String getPackageName() {
- // pass
- return null;
- }
-
- @Override
public String getBasePackageName() {
// pass
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index c44a57c..8899e53 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -164,7 +164,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
}
@Override
- public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException {
+ public void showInputMethodPickerFromClient(IInputMethodClient arg0,
+ int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index 2f45473..b98f96f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.android;
+import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.SessionParams.Key;
/**
@@ -31,10 +32,21 @@ public final class RenderParamsFlags {
new Key<String>("rootTag", String.class);
public static final Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING =
new Key<Boolean>("disableBitmapCaching", Boolean.class);
- public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
- new Key<Boolean>("recyclerViewSupport", Boolean.class);
public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES =
new Key<Boolean>("renderAllDrawableStates", Boolean.class);
+ /**
+ * To tell LayoutLib that the IDE supports RecyclerView.
+ * <p/>
+ * Default is false.
+ */
+ public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
+ new Key<Boolean>("recyclerViewSupport", Boolean.class);
+ /**
+ * The application package name. Used via
+ * {@link com.android.ide.common.rendering.api.LayoutlibCallback#getFlag(Key)}
+ */
+ public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
+ new Key<String>("applicationPackage", String.class);
// Disallow instances.
private RenderParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index 9273ac2..e4c7288 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -27,11 +27,12 @@ import com.android.layoutlib.bridge.android.RenderParamsFlags;
import android.content.Context;
import android.view.View;
-import android.widget.LinearLayout;
import java.lang.reflect.Method;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
/**
* Utility class for working with android.support.v7.widget.RecyclerView
@@ -39,17 +40,15 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
@SuppressWarnings("SpellCheckingInspection") // for "recycler".
public class RecyclerViewUtil {
- /**
- * Used by {@link LayoutManagerType}.
- * <p/>
- * Not declared inside the enum, since it needs to be accessible in the constructor.
- */
- private static final Object CONTEXT = new Object();
-
- public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView";
+ private static final String RV_PKG_PREFIX = "android.support.v7.widget.";
+ public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView";
private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
+ // LinearLayoutManager related constants.
+ private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager";
+ private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
+
/**
* Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a
* LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView}
@@ -71,37 +70,33 @@ public class RecyclerViewUtil {
private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
@NonNull LayoutlibCallback callback) throws ReflectionException {
- Object cookie = context.getCookie(recyclerView);
- assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String;
- if (!(cookie instanceof LayoutManagerType)) {
- if (cookie != null) {
- // TODO: When layoutlib API is updated, try to load the class with a null
- // constructor or a constructor taking one argument - the context.
- Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
- "LayoutManager (" + cookie + ") not found, falling back to " +
- "LinearLayoutManager", null);
- }
- cookie = LayoutManagerType.getDefault();
+ if (getLayoutManager(recyclerView) == null) {
+ // Only set the layout manager if not already set by the recycler view.
+ Object layoutManager = createLayoutManager(context, callback);
+ setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
}
- Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback);
- setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
}
+ /** Creates a LinearLayoutManager using the provided context. */
@Nullable
- private static Object createLayoutManager(@Nullable LayoutManagerType type,
- @NonNull Context context, @NonNull LayoutlibCallback callback)
+ private static Object createLayoutManager(@NonNull Context context,
+ @NonNull LayoutlibCallback callback)
throws ReflectionException {
- if (type == null) {
- type = LayoutManagerType.getDefault();
- }
try {
- return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context));
+ return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE,
+ new Object[]{ context});
} catch (Exception e) {
throw new ReflectionException(e);
}
}
@Nullable
+ private static Object getLayoutManager(View recyclerview) throws ReflectionException {
+ Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager");
+ return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null;
+ }
+
+ @Nullable
private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException {
Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
if (ideSupport != Boolean.TRUE) {
@@ -145,74 +140,4 @@ public class RecyclerViewUtil {
}
throw new RuntimeException("invalid object/classname combination.");
}
-
- /** Supported LayoutManagers. */
- public enum LayoutManagerType {
- LINEAR_LAYOUT_MANGER("Linear",
- "android.support.v7.widget.LinearLayoutManager",
- new Class[]{Context.class}, new Object[]{CONTEXT}),
- GRID_LAYOUT_MANAGER("Grid",
- "android.support.v7.widget.GridLayoutManager",
- new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}),
- STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid",
- "android.support.v7.widget.StaggeredGridLayoutManager",
- new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL});
-
- private String mLogicalName;
- private String mClassName;
- private Class[] mSignature;
- private Object[] mArgs;
-
- LayoutManagerType(String logicalName, String className, Class[] signature, Object[] args) {
- mLogicalName = logicalName;
- mClassName = className;
- mSignature = signature;
- mArgs = args;
- }
-
- String getClassName() {
- return mClassName;
- }
-
- Class[] getSignature() {
- return mSignature;
- }
-
- @NonNull
- Object[] getArgs(Context context) {
- Object[] args = new Object[mArgs.length];
- System.arraycopy(mArgs, 0, args, 0, mArgs.length);
- for (int i = 0; i < args.length; i++) {
- if (args[i] == CONTEXT) {
- args[i] = context;
- }
- }
- return args;
- }
-
- @NonNull
- public static LayoutManagerType getDefault() {
- return LINEAR_LAYOUT_MANGER;
- }
-
- @Nullable
- public static LayoutManagerType getByLogicalName(@NonNull String logicalName) {
- for (LayoutManagerType type : values()) {
- if (logicalName.equals(type.mLogicalName)) {
- return type;
- }
- }
- return null;
- }
-
- @Nullable
- public static LayoutManagerType getByClassName(@NonNull String className) {
- for (LayoutManagerType type : values()) {
- if (className.equals(type.mClassName)) {
- return type;
- }
- }
- return null;
- }
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 9f9b968..dc89d0c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -74,7 +74,7 @@ public class Config {
}
public static String getTime(int platformVersion) {
- if (platformVersion == 0) {
+ if (isGreaterOrEqual(platformVersion, LOLLIPOP_MR1)) {
return "5:10";
}
if (platformVersion < GINGERBREAD) {
@@ -117,7 +117,7 @@ public class Config {
}
public static String getWifiIconType(int platformVersion) {
- return platformVersion == 0 ? "xml" : "png";
+ return isGreaterOrEqual(platformVersion, LOLLIPOP) ? "xml" : "png";
}
/**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9450b6c..04aadff 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -21,6 +21,10 @@ import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.AttributeSet;
+import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -29,6 +33,21 @@ public class NavigationBar extends CustomBar {
/** Navigation bar background color attribute name. */
private static final String ATTR_COLOR = "navigationBarColor";
+ /**
+ * Constructor to be used when creating the {@link NavigationBar} as a regular control.
+ * This is currently used by the theme editor.
+ */
+ public NavigationBar(Context context, AttributeSet attrs)
+ throws XmlPullParserException {
+ this((BridgeContext) context,
+ Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+ LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
+ ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
+ View.LAYOUT_DIRECTION_RTL,
+ (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
+ context.getApplicationInfo().targetSdkVersion);
+ }
+
public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index e5f1f68..a0ed0e8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -25,7 +25,9 @@ import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
@@ -39,7 +41,20 @@ public class StatusBar extends CustomBar {
private final int mSimulatedPlatformVersion;
/** Status bar background color attribute name. */
- private static final String ATTR_COLOR = "colorPrimaryDark";
+ private static final String ATTR_COLOR = "statusBarColor";
+
+ /**
+ * Constructor to be used when creating the {@link StatusBar} as a regular control. This
+ * is currently used by the theme editor.
+ */
+ public StatusBar(Context context, AttributeSet attrs) throws XmlPullParserException {
+ this((BridgeContext) context,
+ Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+ LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
+ ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
+ View.LAYOUT_DIRECTION_RTL,
+ context.getApplicationInfo().targetSdkVersion);
+ }
public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
int simulatedPlatformVersion) throws XmlPullParserException {
@@ -50,6 +65,7 @@ public class StatusBar extends CustomBar {
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
+
int color = getThemeAttrColor(ATTR_COLOR, true);
setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 3dee1e2..26f9000 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -42,6 +42,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
*
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index d957259..f6e5ef1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1075,7 +1075,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
boolean windowFullscreen = getBooleanThemeValue(resources,
- "windowFullscreen", false, !isThemeAppCompat(resources));
+ "windowFullscreen", false, true);
if (!windowFullscreen && !mWindowIsFloating) {
// default value
@@ -1210,15 +1210,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
boolean isThemeAppCompat = false;
for (int i = 0; i < 50; i++) {
+ if (defaultTheme == null) {
+ break;
+ }
// for loop ensures that we don't run into cyclic theme inheritance.
if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
isThemeAppCompat = true;
break;
}
defaultTheme = resources.getParent(defaultTheme);
- if (defaultTheme == null) {
- break;
- }
}
mIsThemeAppCompat = isThemeAppCompat;
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index d252462..8af93eb 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
index d109302..069f9f7 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index 816ecc8..36e2688 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
index b034b75..ca438ad 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index f86b1d3..a98abf5 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index 8bbae90..7d8cc84 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index 8af745d..7e6113b 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
new file mode 100644
index 0000000..c9b76be
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
new file mode 100644
index 0000000..2da2cb9
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:ignore="HardcodedText,LabelFor,TextFields,ContentDescription,RtlHardcoded">
+
+ <FrameLayout
+ android:id="@id/frameLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginEnd="311dp">
+
+ <TextView
+ android:id="@id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|top"
+ android:text="New Text" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/frameLayout"
+ android:text="Large Text"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@id/textView2"
+ android:text="Medium Text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@id/textView4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/textView2"
+ android:layout_toEndOf="@id/textView2"
+ android:text="Small Text"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <Button
+ android:id="@id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/textView3"
+ android:layout_toEndOf="@id/textView4"
+ android:text="New Button" />
+
+ <Button
+ android:id="@id/button2"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@id/button"
+ android:text="New Button" />
+
+ <CheckBox
+ android:id="@id/checkBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignEnd="@id/button"
+ android:layout_below="@id/button"
+ android:text="New CheckBox" />
+
+ <Switch
+ android:id="@id/switch1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/textView2"
+ android:text="New Switch" />
+
+ <ImageButton
+ android:id="@id/imageButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button"
+ android:layout_toEndOf="@id/switch1" />
+
+ <ImageView
+ android:id="@id/imageView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_launcher"
+ android:layout_below="@id/button"
+ android:layout_toEndOf="@id/imageButton" />
+
+ <GridLayout
+ android:id="@id/gridLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/imageButton"
+ android:columnCount="2"
+ android:rowCount="2">
+
+ <ProgressBar
+ android:id="@id/progressBar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="0"
+ android:layout_row="0" />
+
+ <ProgressBar
+ android:id="@id/progressBar2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="1"
+ android:layout_row="0" />
+
+ <ProgressBar
+ android:id="@id/progressBar3"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="0"
+ android:layout_row="1" />
+
+ <ProgressBar
+ android:id="@id/progressBar4"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="1"
+ android:layout_row="1" />
+ </GridLayout>
+
+ <SeekBar
+ android:id="@id/seekBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/gridLayout"
+ android:layout_toEndOf="@id/gridLayout" />
+
+ <RatingBar
+ android:id="@id/ratingBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/switch2"
+ android:layout_toEndOf="@id/gridLayout" />
+
+ <Switch
+ android:id="@id/switch2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/seekBar"
+ android:layout_toEndOf="@id/switch1"
+ android:checked="true" />
+
+ <EditText
+ android:id="@id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@id/ratingBar"
+ android:layout_alignParentStart="true"
+ android:text="plain text" />
+
+ <EditText
+ android:id="@id/editText2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/ratingBar"
+ android:ems="3"
+ android:inputType="textPersonName"
+ android:text="Name" />
+
+ <EditText
+ android:id="@id/editText3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/editText2"
+ android:ems="2"
+ android:inputType="textPassword"
+ android:text="password" />
+
+ <EditText
+ android:id="@id/editText4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/editText3"
+ android:layout_toEndOf="@id/editText3"
+ android:ems="3"
+ android:inputType="numberPassword"
+ android:text="numeric password" />
+
+ <EditText
+ android:id="@id/editText5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText3"
+ android:layout_toStartOf="@id/editText6"
+ android:ems="7"
+ android:inputType="textEmailAddress"
+ android:text="email@domain.com" />
+
+ <EditText
+ android:id="@id/editText6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/editText4"
+ android:ems="7"
+ android:inputType="phone"
+ android:text="+11235554344" />
+
+ <EditText
+ android:id="@id/editText7"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText"
+ android:layout_toEndOf="@id/editText4"
+ android:ems="10"
+ android:inputType="textPostalAddress"
+ android:text="1600 Amphitheatre" />
+
+ <EditText
+ android:id="@id/editText9"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText5"
+ android:layout_alignParentStart="true"
+ android:ems="3"
+ android:inputType="time"
+ android:text="12:12" />
+
+ <RadioGroup
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText5"
+ android:layout_toEndOf="@id/editText9"
+ android:orientation="horizontal">
+
+ <RadioButton
+ android:id="@id/radioButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New RadioButton" />
+
+ <RadioButton
+ android:id="@id/radioButton2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New RadioButton" />
+
+ </RadioGroup>
+
+ <CheckedTextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="CheckedTextView"
+ android:id="@id/checkedTextView"
+ android:layout_below="@id/button2"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/button2" />
+
+ <DialerFilter
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/checkBox"
+ android:layout_toStartOf="@id/quickContactBadge"
+ android:id="@id/dialerFilter"
+ android:layout_above="@id/ratingBar"
+ android:layout_toEndOf="@id/seekBar">
+
+ <EditText
+ android:id="@android:id/hint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Hint" />
+
+ <EditText
+ android:id="@android:id/primary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/hint"
+ android:text="Primary" />
+ </DialerFilter>
+
+ <QuickContactBadge
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/quickContactBadge"
+ android:layout_below="@id/checkedTextView"
+ android:layout_alignParentEnd="true" />
+
+ <android.inputmethodservice.ExtractEditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ExtractEditText"
+ android:id="@id/extractEditText"
+ android:layout_below="@id/editText9"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/checkedTextView" />
+
+ <ZoomControls
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/zoomControls"
+ android:layout_below="@id/editText9"
+ android:layout_alignParentStart="true" />
+
+ <TextureView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/textureView"
+ android:layout_below="@id/zoomControls"
+ android:layout_alignParentStart="true"
+ android:layout_alignBottom="@id/extractEditText"
+ android:layout_toStartOf="@id/editText3" />
+
+ <ListView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/listView"
+ android:layout_below="@id/textureView"
+ android:layout_alignParentStart="true"
+ android:layout_alignEnd="@id/textureView" />
+
+ <GridView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/gridView"
+ android:layout_below="@id/extractEditText"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/extractEditText" />
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/scrollView"
+ android:layout_below="@id/zoomControls"
+ android:layout_toRightOf="@id/listView"
+ android:layout_toLeftOf="@id/extractEditText">
+
+ <TabHost
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@id/tabHost">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@id/linearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@id/linearLayout2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"/>
+
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+</ScrollView>
+
+ <SearchView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/searchView"
+ android:layout_alignBottom="@id/zoomControls"
+ android:layout_toEndOf="@id/seekBar" />
+
+</RelativeLayout>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml
new file mode 100644
index 0000000..1dc2fa0
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item type="id" name="button" />
+ <item type="id" name="button2" />
+ <item type="id" name="checkBox" />
+ <item type="id" name="checkedTextView" />
+ <item type="id" name="dialerFilter" />
+ <item type="id" name="editText" />
+ <item type="id" name="editText2" />
+ <item type="id" name="editText3" />
+ <item type="id" name="editText4" />
+ <item type="id" name="editText5" />
+ <item type="id" name="editText6" />
+ <item type="id" name="editText7" />
+ <item type="id" name="editText8" />
+ <item type="id" name="editText9" />
+ <item type="id" name="extractEditText" />
+ <item type="id" name="frameLayout" />
+ <item type="id" name="gridLayout" />
+ <item type="id" name="gridView" />
+ <item type="id" name="imageButton" />
+ <item type="id" name="imageView" />
+ <item type="id" name="linearLayout" />
+ <item type="id" name="linearLayout2" />
+ <item type="id" name="listView" />
+ <item type="id" name="progressBar" />
+ <item type="id" name="progressBar2" />
+ <item type="id" name="progressBar3" />
+ <item type="id" name="progressBar4" />
+ <item type="id" name="quickContactBadge" />
+ <item type="id" name="radioButton" />
+ <item type="id" name="radioButton2" />
+ <item type="id" name="ratingBar" />
+ <item type="id" name="scrollView" />
+ <item type="id" name="searchView" />
+ <item type="id" name="seekBar" />
+ <item type="id" name="spinner" />
+ <item type="id" name="switch1" />
+ <item type="id" name="switch2" />
+ <item type="id" name="tabHost" />
+ <item type="id" name="textView" />
+ <item type="id" name="textView2" />
+ <item type="id" name="textView3" />
+ <item type="id" name="textView4" />
+ <item type="id" name="textureView" />
+ <item type="id" name="zoomControls" />
+</resources> \ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index ac23564..f2a039e 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -261,7 +261,7 @@ public class Main {
new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) {
@NonNull
@Override
- protected ResourceItem createResourceItem(String name) {
+ protected ResourceItem createResourceItem(@NonNull String name) {
return new ResourceItem(name);
}
};
@@ -275,14 +275,27 @@ public class Main {
ConfigGenerator.getEnumMap(attrs), getLayoutLog());
}
+ /** Text activity.xml */
+ @Test
+ public void testActivity() throws ClassNotFoundException {
+ renderAndVerify("activity.xml", "activity.png");
+
+ }
+
+ /** Test allwidgets.xml */
+ @Test
+ public void testAllWidgets() throws ClassNotFoundException {
+ renderAndVerify("allwidgets.xml", "allwidgets.png");
+ }
+
/**
- * Create a new rendering session and test that rendering /layout/activity.xml on nexus 5
- * doesn't throw any exceptions.
+ * Create a new rendering session and test that rendering given layout on nexus 5
+ * doesn't throw any exceptions and matches the provided image.
*/
- @Test
- public void testRendering() throws ClassNotFoundException {
+ private void renderAndVerify(String layoutFileName, String goldenFileName)
+ throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/activity.xml");
+ LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -301,7 +314,7 @@ public class Main {
session.getResult().getErrorMessage());
}
try {
- String goldenImagePath = APP_TEST_DIR + "/golden/activity.png";
+ String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
ImageUtils.requireSimilar(goldenImagePath, session.getImage());
} catch (IOException e) {
getLogger().error(e, e.getMessage());
@@ -309,7 +322,7 @@ public class Main {
}
/**
- * Uses Theme.Material and Target sdk version as 21.
+ * Uses Theme.Material and Target sdk version as 22.
*/
private SessionParams getSessionParams(LayoutPullParser layoutParser,
ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback) {
@@ -327,7 +340,7 @@ public class Main {
resourceResolver,
layoutLibCallback,
0,
- 21, // TODO: Make it more configurable to run tests for various versions.
+ 22, // TODO: Make it more configurable to run tests for various versions.
getLayoutLog());
}
@@ -381,17 +394,17 @@ public class Main {
}
@Override
- public void warning(String msgFormat, Object... args) {
+ public void warning(@NonNull String msgFormat, Object... args) {
failWithMsg(msgFormat, args);
}
@Override
- public void info(String msgFormat, Object... args) {
+ public void info(@NonNull String msgFormat, Object... args) {
// pass.
}
@Override
- public void verbose(String msgFormat, Object... args) {
+ public void verbose(@NonNull String msgFormat, Object... args) {
// pass.
}
};
@@ -399,7 +412,7 @@ public class Main {
return mLogger;
}
- private static void failWithMsg(String msgFormat, Object... args) {
- fail(msgFormat == null || args == null ? "" : String.format(msgFormat, args));
+ private static void failWithMsg(@NonNull String msgFormat, Object... args) {
+ fail(args == null ? "" : String.format(msgFormat, args));
}
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
index a5c3202..1191df6 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
@@ -21,12 +21,11 @@ import com.android.ide.common.resources.configuration.CountryCodeQualifier;
import com.android.ide.common.resources.configuration.DensityQualifier;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
-import com.android.ide.common.resources.configuration.LanguageQualifier;
import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
import com.android.ide.common.resources.configuration.NightModeQualifier;
-import com.android.ide.common.resources.configuration.RegionQualifier;
import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
@@ -158,10 +157,9 @@ public class ConfigGenerator {
config.setUiModeQualifier(new UiModeQualifier(UiMode.NORMAL));
config.setNightModeQualifier(new NightModeQualifier(NightMode.NOTNIGHT));
config.setCountryCodeQualifier(new CountryCodeQualifier());
- config.setLanguageQualifier(new LanguageQualifier());
config.setLayoutDirectionQualifier(new LayoutDirectionQualifier());
config.setNetworkCodeQualifier(new NetworkCodeQualifier());
- config.setRegionQualifier(new RegionQualifier());
+ config.setLocaleQualifier(new LocaleQualifier());
config.setVersionQualifier(new VersionQualifier());
return config;
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 0a5e798..5b648ef 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -23,8 +23,8 @@ import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.resources.ResourceType;
import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.resources.ResourceType;
import com.android.util.Pair;
import com.android.utils.ILogger;
@@ -36,6 +36,8 @@ import java.util.Map;
import com.google.android.collect.Maps;
+import static org.junit.Assert.fail;
+
@SuppressWarnings("deprecation") // For Pair
public class LayoutLibTestCallback extends LayoutlibCallback {
@@ -121,7 +123,7 @@ public class LayoutLibTestCallback extends LayoutlibCallback {
@Override
public ILayoutPullParser getParser(String layoutName) {
- org.junit.Assert.fail("This method shouldn't be called by this version of LayoutLib.");
+ fail("This method shouldn't be called by this version of LayoutLib.");
return null;
}