summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt340
-rw-r--r--core/java/android/animation/BidirectionalTypeConverter.java73
-rw-r--r--core/java/android/animation/ObjectAnimator.java4
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java10
-rw-r--r--core/java/android/animation/TypeConverter.java12
-rw-r--r--core/java/android/app/Activity.java32
-rw-r--r--core/java/android/app/ActivityTransitionCoordinator.java21
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java19
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java19
-rw-r--r--core/java/android/app/Notification.java220
-rw-r--r--core/java/android/app/RemoteInput.java297
-rw-r--r--core/java/android/app/SharedElementListener.java57
-rw-r--r--core/java/android/app/WallpaperManager.java29
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java101
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl7
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java32
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java10
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl19
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java648
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiser.aidl19
-rw-r--r--core/java/android/bluetooth/BluetoothLeAdvertiser.java600
-rw-r--r--core/java/android/bluetooth/BluetoothLeScanFilter.aidl19
-rw-r--r--core/java/android/bluetooth/BluetoothLeScanFilter.java577
-rw-r--r--core/java/android/bluetooth/BluetoothLeScanner.aidl20
-rw-r--r--core/java/android/bluetooth/BluetoothLeScanner.java759
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java20
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl11
-rw-r--r--core/java/android/bluetooth/IBluetoothGattCallback.aidl1
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/res/ColorStateList.java45
-rw-r--r--core/java/android/content/res/Resources.java9
-rw-r--r--core/java/android/hardware/usb/UsbConfiguration.java30
-rw-r--r--core/java/android/hardware/usb/UsbDeviceConnection.java2
-rw-r--r--core/java/android/net/ConnectivityManager.java11
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/ProxyInfo.java3
-rw-r--r--core/java/android/os/UserManager.java11
-rw-r--r--core/java/android/provider/Settings.java45
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl10
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java217
-rw-r--r--core/java/android/service/notification/NotificationOrderUpdate.java62
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.aidl (renamed from core/java/android/service/notification/NotificationOrderUpdate.aidl)2
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java77
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java2
-rw-r--r--core/java/android/tv/TvView.java1
-rw-r--r--core/java/android/view/InputDevice.java8
-rw-r--r--core/java/android/view/KeyEvent.java20
-rw-r--r--core/java/android/view/RenderNodeAnimator.java10
-rw-r--r--core/java/android/view/View.java61
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java29
-rw-r--r--core/java/android/view/ViewPropertyAnimatorRT.java118
-rw-r--r--core/java/android/widget/AbsListView.java21
-rw-r--r--core/java/android/widget/AbsSeekBar.java20
-rw-r--r--core/java/android/widget/CompoundButton.java2
-rw-r--r--core/java/android/widget/Switch.java2
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java11
-rw-r--r--core/jni/android/graphics/Paint.cpp2
-rw-r--r--core/jni/android_hardware_UsbRequest.cpp22
-rw-r--r--core/res/res/drawable/btn_borderless_quantum.xml4
-rw-r--r--core/res/res/drawable/btn_default_quantum.xml4
-rw-r--r--core/res/res/drawable/edit_text_quantum.xml4
-rw-r--r--core/res/res/drawable/item_background_quantum.xml2
-rw-r--r--core/res/res/drawable/list_selector_quantum.xml4
-rw-r--r--core/res/res/drawable/notification_bg_dim.xml4
-rw-r--r--core/res/res/drawable/notification_quantum_bg_dim.xml4
-rw-r--r--core/res/res/values/attrs.xml9
-rw-r--r--core/res/res/values/ids.xml1
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java14
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java74
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java185
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java53
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java16
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java67
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java26
-rw-r--r--graphics/java/android/graphics/drawable/DrawableWrapper.java305
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java22
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java132
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java (renamed from graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java)84
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java28
-rw-r--r--media/java/android/media/AudioManager.java175
-rw-r--r--media/java/android/media/AudioService.java8
-rw-r--r--media/java/android/media/AudioSystem.java51
-rw-r--r--media/java/android/media/MediaCodec.java1
-rw-r--r--media/java/android/media/MediaCodecInfo.java31
-rw-r--r--media/java/android/media/RemoteControlClient.java13
-rw-r--r--media/jni/android_media_MediaScanner.cpp2
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java18
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java36
-rw-r--r--packages/SystemUI/res/drawable/recents_dismiss_dark.xml3
-rw-r--r--packages/SystemUI/res/drawable/recents_dismiss_light.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml1
-rw-r--r--packages/SystemUI/res/layout/recents_task_view.xml15
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/BarController.java2
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java418
-rw-r--r--services/core/java/com/android/server/AppOpsService.java5
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java95
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/hdmi/NewDeviceAction.java7
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java12
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java144
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java9
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java14
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java126
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--tests/RenderThreadTest/Android.mk2
-rw-r--r--tests/RenderThreadTest/AndroidManifest.xml4
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java3
128 files changed, 5998 insertions, 1648 deletions
diff --git a/api/current.txt b/api/current.txt
index eda087d..f6bc276 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -864,6 +864,7 @@ package android {
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingEnd = 16843700; // 0x10103b4
field public static final int paddingLeft = 16842966; // 0x10100d6
+ field public static final int paddingMode = 16843866; // 0x101045a
field public static final int paddingRight = 16842968; // 0x10100d8
field public static final int paddingStart = 16843699; // 0x10103b3
field public static final int paddingTop = 16842967; // 0x10100d7
@@ -1640,7 +1641,6 @@ package android {
field public static final int selectAll = 16908319; // 0x102001f
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
- field public static final int shared_element = 16908354; // 0x1020042
field public static final int startSelectingText = 16908328; // 0x1020028
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
@@ -2830,6 +2830,12 @@ package android.animation {
method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
}
+ public abstract class BidirectionalTypeConverter extends android.animation.TypeConverter {
+ ctor public BidirectionalTypeConverter(java.lang.Class<T>, java.lang.Class<V>);
+ method public abstract T convertBack(V);
+ method public android.animation.BidirectionalTypeConverter<V, T> invert();
+ }
+
public class FloatArrayEvaluator implements android.animation.TypeEvaluator {
ctor public FloatArrayEvaluator();
ctor public FloatArrayEvaluator(float[]);
@@ -3008,7 +3014,6 @@ package android.animation {
public abstract class TypeConverter {
ctor public TypeConverter(java.lang.Class<T>, java.lang.Class<V>);
method public abstract V convert(T);
- method public T convertBack(V);
}
public abstract interface TypeEvaluator {
@@ -3336,6 +3341,8 @@ package android.app {
method public void setContentView(android.view.View);
method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public final void setDefaultKeyMode(int);
+ method public void setEnterSharedElementListener(android.app.SharedElementListener);
+ method public void setExitSharedElementListener(android.app.SharedElementListener);
method public final void setFeatureDrawable(int, android.graphics.drawable.Drawable);
method public final void setFeatureDrawableAlpha(int, int);
method public final void setFeatureDrawableResource(int, int);
@@ -3351,7 +3358,6 @@ package android.app {
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
method public final void setSecondaryProgress(int);
- method public void setSharedElementListener(android.app.SharedElementListener);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
@@ -4418,6 +4424,8 @@ package android.app {
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public java.lang.String getGroup();
+ method public java.lang.String getSortKey();
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -4460,6 +4468,7 @@ package android.app {
field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+ field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
field public static final int FLAG_INSISTENT = 4; // 0x4
field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
@@ -4510,6 +4519,7 @@ package android.app {
method public android.app.Notification.Action clone();
method public int describeContents();
method public android.os.Bundle getExtras();
+ method public android.app.RemoteInput[] getRemoteInputs();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public android.app.PendingIntent actionIntent;
@@ -4517,14 +4527,20 @@ package android.app {
field public java.lang.CharSequence title;
}
- public static class Notification.Action.Builder {
+ public static final class Notification.Action.Builder {
ctor public Notification.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
ctor public Notification.Action.Builder(android.app.Notification.Action);
method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
+ method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
+ method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
method public android.app.Notification.Action build();
method public android.os.Bundle getExtras();
}
+ public static abstract interface Notification.Action.Builder.Extender {
+ method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
+ }
+
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
ctor public Notification.BigPictureStyle();
ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
@@ -4548,6 +4564,7 @@ package android.app {
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
+ method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
method public android.app.Notification build();
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
@@ -4563,6 +4580,8 @@ package android.app {
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setExtras(android.os.Bundle);
method public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+ method public android.app.Notification.Builder setGroup(java.lang.String);
+ method public android.app.Notification.Builder setGroupSummary(boolean);
method public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
@@ -4575,6 +4594,7 @@ package android.app {
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
method public android.app.Notification.Builder setSmallIcon(int, int);
+ method public android.app.Notification.Builder setSortKey(java.lang.String);
method public android.app.Notification.Builder setSound(android.net.Uri);
method public android.app.Notification.Builder setSound(android.net.Uri, int);
method public android.app.Notification.Builder setStyle(android.app.Notification.Style);
@@ -4587,6 +4607,10 @@ package android.app {
method public android.app.Notification.Builder setWhen(long);
}
+ public static abstract interface Notification.Builder.Extender {
+ method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
+ }
+
public static class Notification.InboxStyle extends android.app.Notification.Style {
ctor public Notification.InboxStyle();
ctor public Notification.InboxStyle(android.app.Notification.Builder);
@@ -4690,6 +4714,31 @@ package android.app {
field public static final int STYLE_SPINNER = 0; // 0x0
}
+ public final class RemoteInput implements android.os.Parcelable {
+ method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
+ method public int describeContents();
+ method public boolean getAllowFreeFormInput();
+ method public java.lang.CharSequence[] getChoices();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getLabel();
+ method public java.lang.String getResultKey();
+ method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+ field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+ }
+
+ public static final class RemoteInput.Builder {
+ ctor public RemoteInput.Builder(java.lang.String);
+ method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
+ method public android.app.RemoteInput build();
+ method public android.os.Bundle getExtras();
+ method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+ method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
+ method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
+ }
+
public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
method public android.content.ComponentName getGlobalSearchActivity();
method public android.app.SearchableInfo getSearchableInfo(android.content.ComponentName);
@@ -4810,7 +4859,7 @@ package android.app {
field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
}
- public class SharedElementListener {
+ public abstract class SharedElementListener {
ctor public SharedElementListener();
method public void handleRejectedSharedElements(java.util.List<android.view.View>);
method public void remapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
@@ -5047,6 +5096,7 @@ package android.app.admin {
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
+ method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
@@ -5054,8 +5104,11 @@ package android.app.admin {
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setApplicationBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+ method public int setApplicationsBlocked(android.content.ComponentName, android.content.Intent, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
+ method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
@@ -5071,6 +5124,7 @@ package android.app.admin {
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
method public void setProfileEnabled(android.content.ComponentName);
+ method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void wipeData(int);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
@@ -5364,6 +5418,8 @@ package android.bluetooth {
method public boolean disable();
method public boolean enable();
method public java.lang.String getAddress();
+ method public android.bluetooth.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
+ method public android.bluetooth.BluetoothLeScanner getBluetoothLeScanner();
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
@@ -6006,6 +6062,178 @@ package android.bluetooth {
method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
+ public final class BluetoothLeAdvertiseScanData {
+ ctor public BluetoothLeAdvertiseScanData();
+ field public static final int ADVERTISING_DATA = 0; // 0x0
+ field public static final int PARSED_SCAN_RECORD = 2; // 0x2
+ }
+
+ public static abstract class BluetoothLeAdvertiseScanData.AdvertiseBaseData {
+ method public int getDataType();
+ method public int getManufacturerId();
+ method public byte[] getManufacturerSpecificData();
+ method public byte[] getServiceData();
+ method public android.os.ParcelUuid getServiceDataUuid();
+ method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+ }
+
+ public static final class BluetoothLeAdvertiseScanData.AdvertisementData extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getIncludeTxPowerLevel();
+ method public static android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder newBuilder();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class BluetoothLeAdvertiseScanData.AdvertisementData.Builder {
+ ctor public BluetoothLeAdvertiseScanData.AdvertisementData.Builder();
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData build();
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder dataType(int);
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder includeTxPowerLevel(boolean);
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder manufacturerData(int, byte[]);
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceData(android.os.ParcelUuid, byte[]);
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceUuids(java.util.List<android.os.ParcelUuid>);
+ }
+
+ public static final class BluetoothLeAdvertiseScanData.ScanRecord extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData {
+ method public int getAdvertiseFlags();
+ method public java.lang.String getLocalName();
+ method public static android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord.Parser getParser();
+ method public int getTxPowerLevel();
+ }
+
+ public static final class BluetoothLeAdvertiseScanData.ScanRecord.Parser {
+ ctor public BluetoothLeAdvertiseScanData.ScanRecord.Parser();
+ method public android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord parseFromScanRecord(byte[]);
+ }
+
+ public class BluetoothLeAdvertiser {
+ method public void startAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
+ method public void stopAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
+ }
+
+ public static abstract interface BluetoothLeAdvertiser.AdvertiseCallback {
+ method public abstract void onFailure(int);
+ method public abstract void onSuccess(android.bluetooth.BluetoothLeAdvertiser.Settings);
+ field public static final int ADVERISING_NOT_STARTED = 4; // 0x4
+ field public static final int ADVERTISING_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISING_SERVICE_UNKNOWN = 1; // 0x1
+ field public static final int CONTROLLER_FAILURE = 5; // 0x5
+ field public static final int TOO_MANY_ADVERTISERS = 2; // 0x2
+ }
+
+ public static final class BluetoothLeAdvertiser.Settings implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getMode();
+ method public int getTxPowerLevel();
+ method public int getType();
+ method public static android.bluetooth.BluetoothLeAdvertiser.Settings.Builder newBuilder();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ADVERTISE_MODE_BALANCED = 1; // 0x1
+ field public static final int ADVERTISE_MODE_LOW_LATENCY = 2; // 0x2
+ field public static final int ADVERTISE_MODE_LOW_POWER = 0; // 0x0
+ field public static final int ADVERTISE_TX_POWER_HIGH = 3; // 0x3
+ field public static final int ADVERTISE_TX_POWER_LOW = 1; // 0x1
+ field public static final int ADVERTISE_TX_POWER_MEDIUM = 2; // 0x2
+ field public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; // 0x0
+ field public static final int ADVERTISE_TYPE_CONNECTABLE = 2; // 0x2
+ field public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; // 0x0
+ field public static final int ADVERTISE_TYPE_SCANNABLE = 1; // 0x1
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class BluetoothLeAdvertiser.Settings.Builder {
+ method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder advertiseMode(int);
+ method public android.bluetooth.BluetoothLeAdvertiser.Settings build();
+ method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder txPowerLevel(int);
+ method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder type(int);
+ }
+
+ public final class BluetoothLeScanFilter implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getDeviceAddress();
+ method public java.lang.String getLocalName();
+ method public byte[] getManufacturerData();
+ method public byte[] getManufacturerDataMask();
+ method public int getManufacturerId();
+ method public int getMaxRssi();
+ method public int getMinRssi();
+ method public byte[] getServiceData();
+ method public byte[] getServiceDataMask();
+ method public android.os.ParcelUuid getServiceUuid();
+ method public android.os.ParcelUuid getServiceUuidMask();
+ method public boolean matches(android.bluetooth.BluetoothLeScanner.ScanResult);
+ method public static android.bluetooth.BluetoothLeScanFilter.Builder newBuilder();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static class BluetoothLeScanFilter.Builder {
+ method public android.bluetooth.BluetoothLeScanFilter build();
+ method public android.bluetooth.BluetoothLeScanFilter.Builder macAddress(java.lang.String);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerData(int, byte[]);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerDataMask(byte[]);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder name(java.lang.String);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder rssiRange(int, int);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder serviceData(byte[]);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder serviceDataMask(byte[]);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuid(android.os.ParcelUuid);
+ method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuidMask(android.os.ParcelUuid);
+ }
+
+ public class BluetoothLeScanner {
+ method public void startScan(java.util.List<android.bluetooth.BluetoothLeScanFilter>, android.bluetooth.BluetoothLeScanner.Settings, android.bluetooth.BluetoothLeScanner.ScanCallback);
+ method public void stopScan(android.bluetooth.BluetoothLeScanner.Settings);
+ }
+
+ public static abstract interface BluetoothLeScanner.ScanCallback {
+ method public abstract void onBatchScanResults(java.util.List<android.bluetooth.BluetoothLeScanner.ScanResult>);
+ method public abstract void onDeviceFound(android.bluetooth.BluetoothLeScanner.ScanResult);
+ method public abstract void onDeviceLost(android.bluetooth.BluetoothDevice);
+ method public abstract void onDeviceUpdate(android.bluetooth.BluetoothLeScanner.ScanResult);
+ method public abstract void onScanFailed(int);
+ field public static final int APPLICATION_REGISTRATION_FAILED = 2; // 0x2
+ field public static final int CONTROLLER_FAILURE = 4; // 0x4
+ field public static final int GATT_SERVICE_FAILURE = 3; // 0x3
+ field public static final int SCAN_ALREADY_STARTED = 1; // 0x1
+ }
+
+ public static final class BluetoothLeScanner.ScanResult implements android.os.Parcelable {
+ ctor public BluetoothLeScanner.ScanResult(android.bluetooth.BluetoothDevice, byte[], int, long);
+ method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getRssi();
+ method public byte[] getScanRecord();
+ method public long getTimestampMicros();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class BluetoothLeScanner.Settings implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCallbackType();
+ method public long getReportDelayMicros();
+ method public int getScanMode();
+ method public int getScanResultType();
+ method public static android.bluetooth.BluetoothLeScanner.Settings.Builder newBuilder();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALLBACK_TYPE_ON_FOUND = 1; // 0x1
+ field public static final int CALLBACK_TYPE_ON_LOST = 2; // 0x2
+ field public static final int CALLBACK_TYPE_ON_UPDATE = 0; // 0x0
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int SCAN_MODE_BALANCED = 1; // 0x1
+ field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
+ field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+ field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
+ }
+
+ public static class BluetoothLeScanner.Settings.Builder {
+ method public android.bluetooth.BluetoothLeScanner.Settings build();
+ method public android.bluetooth.BluetoothLeScanner.Settings.Builder callbackType(int);
+ method public android.bluetooth.BluetoothLeScanner.Settings.Builder reportDelayMicros(long);
+ method public android.bluetooth.BluetoothLeScanner.Settings.Builder scanMode(int);
+ }
+
public final class BluetoothManager {
method public android.bluetooth.BluetoothAdapter getAdapter();
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -8091,6 +8319,7 @@ package android.content.pm {
field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch";
+ field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview";
field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi";
field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final int GET_ACTIVITIES = 1; // 0x1
@@ -10908,7 +11137,6 @@ package android.graphics.drawable {
method public void applyTheme(android.content.res.Resources.Theme);
method public boolean canApplyTheme();
method public void clearColorFilter();
- method public void clearHotspots();
method public final void copyBounds(android.graphics.Rect);
method public final android.graphics.Rect copyBounds();
method public static android.graphics.drawable.Drawable createFromPath(java.lang.String);
@@ -10951,7 +11179,6 @@ package android.graphics.drawable {
method protected void onBoundsChange(android.graphics.Rect);
method protected boolean onLevelChange(int);
method protected boolean onStateChange(int[]);
- method public void removeHotspot(int);
method public static int resolveOpacity(int, int);
method public void scheduleSelf(java.lang.Runnable, long);
method public abstract void setAlpha(int);
@@ -10964,11 +11191,11 @@ package android.graphics.drawable {
method public void setColorFilter(int, android.graphics.PorterDuff.Mode);
method public void setDither(boolean);
method public void setFilterBitmap(boolean);
- method public void setHotspot(int, float, float);
+ method public void setHotspot(float, float);
+ method public void setHotspotBounds(int, int, int, int);
method public final boolean setLevel(int);
method public boolean setState(int[]);
method public boolean setVisible(boolean, boolean);
- method public boolean supportsHotspots();
method public void unscheduleSelf(java.lang.Runnable);
}
@@ -11027,30 +11254,6 @@ package android.graphics.drawable {
method public final void setVariablePadding(boolean);
}
- public class DrawableWrapper extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
- ctor public DrawableWrapper();
- method public void draw(android.graphics.Canvas);
- method public android.graphics.Rect getDirtyBounds();
- method protected final android.graphics.drawable.Drawable getDrawable();
- method public int getOpacity();
- method public final int[] getState();
- method public void invalidateDrawable(android.graphics.drawable.Drawable);
- method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
- method public void setAlpha(int);
- method public final void setBounds(int, int, int, int);
- method public final void setBounds(android.graphics.Rect);
- method public void setColorFilter(android.graphics.ColorFilter);
- method protected void setConstantState(android.graphics.drawable.DrawableWrapper.WrapperState, android.content.res.Resources);
- method protected final void setDrawable(android.graphics.drawable.Drawable, android.content.res.Resources);
- method public final boolean setState(int[]);
- method public void setXfermode(android.graphics.Xfermode);
- method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
- }
-
- public static abstract class DrawableWrapper.WrapperState extends android.graphics.drawable.Drawable.ConstantState {
- method public int getChangingConfigurations();
- }
-
public class GradientDrawable extends android.graphics.drawable.Drawable {
ctor public GradientDrawable();
ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]);
@@ -11174,6 +11377,13 @@ package android.graphics.drawable {
method public void setPicture(android.graphics.Picture);
}
+ public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
+ method public android.graphics.Rect getDirtyBounds();
+ method public android.content.res.ColorStateList getTint();
+ method public void setTint(android.content.res.ColorStateList);
+ method public void setTintMode(android.graphics.PorterDuff.Mode);
+ }
+
public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
ctor public RotateDrawable();
method public void draw(android.graphics.Canvas);
@@ -11243,13 +11453,6 @@ package android.graphics.drawable {
method public void addState(int[], android.graphics.drawable.Drawable);
}
- public class TouchFeedbackDrawable extends android.graphics.drawable.LayerDrawable {
- method public android.graphics.Rect getDirtyBounds();
- method public android.content.res.ColorStateList getTint();
- method public void setTint(android.content.res.ColorStateList);
- method public void setTintMode(android.graphics.PorterDuff.Mode);
- }
-
public class TransitionDrawable extends android.graphics.drawable.LayerDrawable implements android.graphics.drawable.Drawable.Callback {
ctor public TransitionDrawable(android.graphics.drawable.Drawable[]);
method public boolean isCrossFadeEnabled();
@@ -12673,15 +12876,14 @@ package android.hardware.usb {
public class UsbConfiguration implements android.os.Parcelable {
method public int describeContents();
- method public int getAttributes();
method public int getId();
method public android.hardware.usb.UsbInterface getInterface(int);
method public int getInterfaceCount();
method public int getMaxPower();
method public java.lang.String getName();
+ method public boolean isRemoteWakeup();
+ method public boolean isSelfPowered();
method public void writeToParcel(android.os.Parcel, int);
- field public static final int ATTR_REMOTE_WAKEUP_MASK = 32; // 0x20
- field public static final int ATTR_SELF_POWERED_MASK = 64; // 0x40
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -14065,6 +14267,34 @@ package android.media {
field public static final int H263ProfileISWV3 = 16; // 0x10
field public static final int H263ProfileInterlace = 128; // 0x80
field public static final int H263ProfileInternet = 64; // 0x40
+ field public static final int HEVCHighTierLevel1 = 2; // 0x2
+ field public static final int HEVCHighTierLevel2 = 8; // 0x8
+ field public static final int HEVCHighTierLevel21 = 32; // 0x20
+ field public static final int HEVCHighTierLevel3 = 128; // 0x80
+ field public static final int HEVCHighTierLevel31 = 512; // 0x200
+ field public static final int HEVCHighTierLevel4 = 2048; // 0x800
+ field public static final int HEVCHighTierLevel41 = 8192; // 0x2000
+ field public static final int HEVCHighTierLevel5 = 32768; // 0x8000
+ field public static final int HEVCHighTierLevel51 = 131072; // 0x20000
+ field public static final int HEVCHighTierLevel52 = 524288; // 0x80000
+ field public static final int HEVCHighTierLevel6 = 2097152; // 0x200000
+ field public static final int HEVCHighTierLevel61 = 8388608; // 0x800000
+ field public static final int HEVCHighTierLevel62 = 33554432; // 0x2000000
+ field public static final int HEVCMainTierLevel1 = 1; // 0x1
+ field public static final int HEVCMainTierLevel2 = 4; // 0x4
+ field public static final int HEVCMainTierLevel21 = 16; // 0x10
+ field public static final int HEVCMainTierLevel3 = 64; // 0x40
+ field public static final int HEVCMainTierLevel31 = 256; // 0x100
+ field public static final int HEVCMainTierLevel4 = 1024; // 0x400
+ field public static final int HEVCMainTierLevel41 = 4096; // 0x1000
+ field public static final int HEVCMainTierLevel5 = 16384; // 0x4000
+ field public static final int HEVCMainTierLevel51 = 65536; // 0x10000
+ field public static final int HEVCMainTierLevel52 = 262144; // 0x40000
+ field public static final int HEVCMainTierLevel6 = 1048576; // 0x100000
+ field public static final int HEVCMainTierLevel61 = 4194304; // 0x400000
+ field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000
+ field public static final int HEVCProfileMain = 1; // 0x1
+ field public static final int HEVCProfileMain10 = 2; // 0x2
field public static final int MPEG4Level0 = 1; // 0x1
field public static final int MPEG4Level0b = 2; // 0x2
field public static final int MPEG4Level1 = 4; // 0x4
@@ -14754,6 +14984,7 @@ package android.media {
ctor public RemoteControlClient(android.app.PendingIntent);
ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
method public android.media.RemoteControlClient.MetadataEditor editMetadata(boolean);
+ method public android.media.session.MediaSession getMediaSession();
method public void setMetadataUpdateListener(android.media.RemoteControlClient.OnMetadataUpdateListener);
method public void setOnGetPlaybackPositionListener(android.media.RemoteControlClient.OnGetPlaybackPositionListener);
method public void setPlaybackPositionUpdateListener(android.media.RemoteControlClient.OnPlaybackPositionUpdateListener);
@@ -20811,6 +21042,7 @@ package android.os {
field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
+ field public static final java.lang.String DISALLOW_TELEPHONY = "no_telephony";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -23625,7 +23857,7 @@ package android.provider {
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
- field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String MODE_RINGER = "mode_ringer";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
@@ -23695,7 +23927,7 @@ package android.provider {
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
- field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String LOCATION_MODE = "location_mode";
field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
@@ -25408,20 +25640,24 @@ package android.service.notification {
method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int);
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
+ method public java.lang.String[] getActiveNotificationKeys();
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
- method public java.lang.String[] getOrderedNotificationKeys();
+ method public android.service.notification.NotificationListenerService.Ranking getCurrentRanking();
method public android.os.IBinder onBind(android.content.Intent);
method public void onListenerConnected(java.lang.String[]);
- method public void onNotificationOrderUpdate();
method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
+ method public void onNotificationRankingUpdate();
method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
- public class NotificationOrderUpdate implements android.os.Parcelable {
- ctor public NotificationOrderUpdate(android.os.Parcel);
+ public static class NotificationListenerService.Ranking implements android.os.Parcelable {
method public int describeContents();
+ method public int getIndexOfKey(java.lang.String);
+ method public java.lang.String[] getOrderedKeys();
+ method public boolean isAmbient(java.lang.String);
+ method public boolean isInterceptedByDoNotDisturb(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -30662,6 +30898,7 @@ package android.view {
field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
field public static final int SOURCE_DPAD = 513; // 0x201
field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+ field public static final int SOURCE_HDMI = 33554433; // 0x2000001
field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
field public static final int SOURCE_KEYBOARD = 257; // 0x101
field public static final int SOURCE_MOUSE = 8194; // 0x2002
@@ -30830,6 +31067,8 @@ package android.view {
field public static final deprecated int FLAG_WOKE_HERE = 1; // 0x1
field public static final int KEYCODE_0 = 7; // 0x7
field public static final int KEYCODE_1 = 8; // 0x8
+ field public static final int KEYCODE_11 = 227; // 0xe3
+ field public static final int KEYCODE_12 = 228; // 0xe4
field public static final int KEYCODE_2 = 9; // 0x9
field public static final int KEYCODE_3 = 10; // 0xa
field public static final int KEYCODE_3D_MODE = 206; // 0xce
@@ -30949,6 +31188,7 @@ package android.view {
field public static final int KEYCODE_KATAKANA_HIRAGANA = 215; // 0xd7
field public static final int KEYCODE_L = 40; // 0x28
field public static final int KEYCODE_LANGUAGE_SWITCH = 204; // 0xcc
+ field public static final int KEYCODE_LAST_CHANNEL = 229; // 0xe5
field public static final int KEYCODE_LEFT_BRACKET = 71; // 0x47
field public static final int KEYCODE_M = 41; // 0x29
field public static final int KEYCODE_MANNER_MODE = 205; // 0xcd
@@ -30964,6 +31204,7 @@ package android.view {
field public static final int KEYCODE_MEDIA_RECORD = 130; // 0x82
field public static final int KEYCODE_MEDIA_REWIND = 89; // 0x59
field public static final int KEYCODE_MEDIA_STOP = 86; // 0x56
+ field public static final int KEYCODE_MEDIA_TOP_MENU = 226; // 0xe2
field public static final int KEYCODE_MENU = 82; // 0x52
field public static final int KEYCODE_META_LEFT = 117; // 0x75
field public static final int KEYCODE_META_RIGHT = 118; // 0x76
@@ -31036,6 +31277,7 @@ package android.view {
field public static final int KEYCODE_T = 48; // 0x30
field public static final int KEYCODE_TAB = 61; // 0x3d
field public static final int KEYCODE_TV = 170; // 0xaa
+ field public static final int KEYCODE_TV_DATA_SERVICE = 230; // 0xe6
field public static final int KEYCODE_TV_INPUT = 178; // 0xb2
field public static final int KEYCODE_TV_POWER = 177; // 0xb1
field public static final int KEYCODE_U = 49; // 0x31
diff --git a/core/java/android/animation/BidirectionalTypeConverter.java b/core/java/android/animation/BidirectionalTypeConverter.java
new file mode 100644
index 0000000..960650e
--- /dev/null
+++ b/core/java/android/animation/BidirectionalTypeConverter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.animation;
+
+/**
+ * Abstract base class used convert type T to another type V and back again. This
+ * is necessary when the value types of in animation are different from the property
+ * type. BidirectionalTypeConverter is needed when only the final value for the
+ * animation is supplied to animators.
+ * @see PropertyValuesHolder#setConverter(TypeConverter)
+ */
+public abstract class BidirectionalTypeConverter<T, V> extends TypeConverter<T, V> {
+ private BidirectionalTypeConverter mInvertedConverter;
+
+ public BidirectionalTypeConverter(Class<T> fromClass, Class<V> toClass) {
+ super(fromClass, toClass);
+ }
+
+ /**
+ * Does a conversion from the target type back to the source type. The subclass
+ * must implement this when a TypeConverter is used in animations and current
+ * values will need to be read for an animation.
+ * @param value The Object to convert.
+ * @return A value of type T, converted from <code>value</code>.
+ */
+ public abstract T convertBack(V value);
+
+ /**
+ * Returns the inverse of this converter, where the from and to classes are reversed.
+ * The inverted converter uses this convert to call {@link #convertBack(Object)} for
+ * {@link #convert(Object)} calls and {@link #convert(Object)} for
+ * {@link #convertBack(Object)} calls.
+ * @return The inverse of this converter, where the from and to classes are reversed.
+ */
+ public BidirectionalTypeConverter<V, T> invert() {
+ if (mInvertedConverter == null) {
+ mInvertedConverter = new InvertedConverter(this);
+ }
+ return mInvertedConverter;
+ }
+
+ private static class InvertedConverter<From, To> extends BidirectionalTypeConverter<From, To> {
+ private BidirectionalTypeConverter<To, From> mConverter;
+
+ public InvertedConverter(BidirectionalTypeConverter<To, From> converter) {
+ super(converter.getTargetType(), converter.getSourceType());
+ mConverter = converter;
+ }
+
+ @Override
+ public From convertBack(To value) {
+ return mConverter.convert(value);
+ }
+
+ @Override
+ public To convert(From value) {
+ return mConverter.convertBack(value);
+ }
+ }
+}
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index c0ce795..130754e 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -610,8 +610,8 @@ public final class ObjectAnimator extends ValueAnimator {
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation). This variant supplies a <code>TypeConverter</code> to
* convert from the animated values to the type of the property. If only one value is
- * supplied, the <code>TypeConverter</code> must implement
- * {@link TypeConverter#convertBack(Object)} to retrieve the current value.
+ * supplied, the <code>TypeConverter</code> must be a
+ * {@link android.animation.BidirectionalTypeConverter} to retrieve the current value.
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 8fce80a..bf2924c 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -456,7 +456,7 @@ public class PropertyValuesHolder implements Cloneable {
* cannot automatically interpolate between objects of unknown type. This variant also
* takes a <code>TypeConverter</code> to convert from animated values to the type
* of the property. If only one value is supplied, the <code>TypeConverter</code>
- * must implement {@link TypeConverter#convertBack(Object)} to retrieve the current
+ * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
* value.
*
* @param property The property being animated. Should not be null.
@@ -635,6 +635,8 @@ public class PropertyValuesHolder implements Cloneable {
/**
* Sets the converter to convert from the values type to the setter's parameter type.
+ * If only one value is supplied, <var>converter</var> must be a
+ * {@link android.animation.BidirectionalTypeConverter}.
* @param converter The converter to use to convert values.
*/
public void setConverter(TypeConverter converter) {
@@ -816,12 +818,12 @@ public class PropertyValuesHolder implements Cloneable {
private Object convertBack(Object value) {
if (mConverter != null) {
- value = mConverter.convertBack(value);
- if (value == null) {
+ if (!(mConverter instanceof BidirectionalTypeConverter)) {
throw new IllegalArgumentException("Converter "
+ mConverter.getClass().getName()
- + " must implement convertBack and not return null.");
+ + " must be a BidirectionalTypeConverter");
}
+ value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
}
return value;
}
diff --git a/core/java/android/animation/TypeConverter.java b/core/java/android/animation/TypeConverter.java
index 03b3eb5..9ead2ad 100644
--- a/core/java/android/animation/TypeConverter.java
+++ b/core/java/android/animation/TypeConverter.java
@@ -53,16 +53,4 @@ public abstract class TypeConverter<T, V> {
* @return A value of type V, converted from <code>value</code>.
*/
public abstract V convert(T value);
-
- /**
- * Does a conversion from the target type back to the source type. The subclass
- * must implement this when a TypeConverter is used in animations and current
- * values will need to be read for an animation. By default, this will return null,
- * indicating that back-conversion is not supported.
- * @param value The Object to convert.
- * @return A value of type T, converted from <code>value</code>.
- */
- public T convertBack(V value) {
- return null;
- }
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e1a94d7..3de971c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -779,7 +779,8 @@ public class Activity extends ContextThemeWrapper
final Handler mHandler = new Handler();
private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
- SharedElementListener mTransitionListener = new SharedElementListener();
+ SharedElementListener mEnterTransitionListener = SharedElementListener.NULL_LISTENER;
+ SharedElementListener mExitTransitionListener = SharedElementListener.NULL_LISTENER;
/** Return the intent that started this activity. */
public Intent getIntent() {
@@ -5557,16 +5558,32 @@ public class Activity extends ContextThemeWrapper
/**
* When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
* android.view.View, String)} was used to start an Activity, <var>listener</var>
- * will be called to handle shared elements. This requires
+ * will be called to handle shared elements on the <i>launched</i> Activity. This requires
* {@link Window#FEATURE_CONTENT_TRANSITIONS}.
*
- * @param listener Used to manipulate how shared element transitions function.
+ * @param listener Used to manipulate shared element transitions on the launched Activity.
*/
- public void setSharedElementListener(SharedElementListener listener) {
+ public void setEnterSharedElementListener(SharedElementListener listener) {
if (listener == null) {
- listener = new SharedElementListener();
+ listener = SharedElementListener.NULL_LISTENER;
}
- mTransitionListener = listener;
+ mEnterTransitionListener = listener;
+ }
+
+ /**
+ * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+ * android.view.View, String)} was used to start an Activity, <var>listener</var>
+ * will be called to handle shared elements on the <i>launching</i> Activity. Most
+ * calls will only come when returning from the started Activity.
+ * This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}.
+ *
+ * @param listener Used to manipulate shared element transitions on the launching Activity.
+ */
+ public void setExitSharedElementListener(SharedElementListener listener) {
+ if (listener == null) {
+ listener = SharedElementListener.NULL_LISTENER;
+ }
+ mExitTransitionListener = listener;
}
// ------------------ Internal API ------------------
@@ -5882,7 +5899,8 @@ public class Activity extends ContextThemeWrapper
* have completed drawing. This is necessary only after an {@link Activity} has been made
* opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
* translucent again following a call to {@link
- * Activity#convertToTranslucent(TranslucentConversionListener)}.
+ * Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener,
+ * ActivityOptions)}
*
* @hide
*/
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 6c6a52f..2acf5b2 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -189,15 +189,17 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
final protected SharedElementListener mListener;
protected ResultReceiver mResultReceiver;
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+ final protected boolean mIsReturning;
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
ArrayList<String> accepted, ArrayList<String> localNames,
- SharedElementListener listener) {
+ SharedElementListener listener, boolean isReturning) {
super(new Handler());
mWindow = window;
mListener = listener;
mAllSharedElementNames = allSharedElementNames;
+ mIsReturning = isReturning;
setSharedElements(accepted, localNames);
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
@@ -330,7 +332,21 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
mResultReceiver = resultReceiver;
}
- protected abstract Transition getViewsTransition();
+ protected Transition getViewsTransition() {
+ if (mIsReturning) {
+ return getWindow().getExitTransition();
+ } else {
+ return getWindow().getEnterTransition();
+ }
+ }
+
+ protected Transition getSharedElementTransition() {
+ if (mIsReturning) {
+ return getWindow().getSharedElementExitTransition();
+ } else {
+ return getWindow().getSharedElementEnterTransition();
+ }
+ }
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
private Rect mEpicenter;
@@ -342,4 +358,5 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
return mEpicenter;
}
}
+
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 636205b..b40d16c 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -51,7 +51,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private static final long MAX_WAIT_MS = 1500;
private boolean mSharedElementTransitionStarted;
- private boolean mIsReturning;
private Activity mActivity;
private boolean mHasStopped;
private Handler mHandler;
@@ -61,9 +60,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
ArrayList<String> sharedElementNames,
ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
- activity.mTransitionListener);
+ getListener(activity, acceptedNames), acceptedNames != null);
mActivity = activity;
- mIsReturning = acceptedNames != null;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
@@ -80,6 +78,12 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
}
+ private static SharedElementListener getListener(Activity activity,
+ ArrayList<String> acceptedNames) {
+ boolean isReturning = acceptedNames != null;
+ return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
+ }
+
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
@@ -299,7 +303,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
if (sharedElementBundle != null) {
Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
View snapshot = new View(context);
- snapshot.setId(com.android.internal.R.id.shared_element);
Resources resources = getWindow().getContext().getResources();
snapshot.setBackground(new BitmapDrawable(resources, bitmap));
snapshot.setViewName(name);
@@ -420,12 +423,4 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
}
}
- @Override
- protected Transition getViewsTransition() {
- return getWindow().getEnterTransition();
- }
-
- protected Transition getSharedElementTransition() {
- return getWindow().getSharedElementEnterTransition();
- }
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 43a60a3..1d78b30 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -53,20 +53,22 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
private boolean mIsBackgroundReady;
- private boolean mIsReturning;
-
private boolean mIsCanceled;
private Handler mHandler;
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
- super(activity.getWindow(), names, accepted, mapped, activity.mTransitionListener);
- mIsReturning = isReturning;
+ super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
+ isReturning);
mIsBackgroundReady = !mIsReturning;
mActivity = activity;
}
+ private static SharedElementListener getListener(Activity activity, boolean isReturning) {
+ return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
+ }
+
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
@@ -271,13 +273,4 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
}
return -1;
}
-
- @Override
- protected Transition getViewsTransition() {
- return getWindow().getExitTransition();
- }
-
- protected Transition getSharedElementTransition() {
- return getWindow().getSharedElementExitTransition();
- }
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fd76b9c4..59b3a27 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -45,6 +45,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Collections;
/**
* A class that represents how a persistent notification is to be presented to
@@ -370,6 +371,14 @@ public class Notification implements Parcelable
*/
public static final int FLAG_LOCAL_ONLY = 0x00000100;
+ /**
+ * Bit to be bitswise-ored into the {@link #flags} field that should be
+ * set if this notification is the group summary for a group of notifications.
+ * Grouped notifications may display in a cluster or stack on devices which
+ * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
+ */
+ public static final int FLAG_GROUP_SUMMARY = 0x00000200;
+
public int flags;
/** @hide */
@@ -539,6 +548,34 @@ public class Notification implements Parcelable
*/
public String category;
+ private String mGroupKey;
+
+ /**
+ * Get the key used to group this notification into a cluster or stack
+ * with other notifications on devices which support such rendering.
+ */
+ public String getGroup() {
+ return mGroupKey;
+ }
+
+ private String mSortKey;
+
+ /**
+ * Get a sort key that orders this notification among other notifications from the
+ * same package. This can be useful if an external sort was already applied and an app
+ * would like to preserve this. Notifications will be sorted lexicographically using this
+ * value, although providing different priorities in addition to providing sort key may
+ * cause this value to be ignored.
+ *
+ * <p>This sort key can also be used to order members of a notification group. See
+ * {@link Builder#setGroup}.
+ *
+ * @see String#compareTo(String)
+ */
+ public String getSortKey() {
+ return mSortKey;
+ }
+
/**
* Additional semantic data to be carried around with this Notification.
* <p>
@@ -706,15 +743,18 @@ public class Notification implements Parcelable
*/
public static class Action implements Parcelable {
private final Bundle mExtras;
+ private RemoteInput[] mRemoteInputs;
/**
* Small icon representing the action.
*/
public int icon;
+
/**
* Title of the action.
*/
public CharSequence title;
+
/**
* Intent to send when the user invokes this action. May be null, in which case the action
* may be rendered in a disabled presentation by the system UI.
@@ -728,19 +768,23 @@ public class Notification implements Parcelable
actionIntent = PendingIntent.CREATOR.createFromParcel(in);
}
mExtras = in.readBundle();
+ mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
}
+
/**
* Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
*/
public Action(int icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle());
+ this(icon, title, intent, new Bundle(), null);
}
- private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
+ private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
+ RemoteInput[] remoteInputs) {
this.icon = icon;
this.title = title;
this.actionIntent = intent;
this.mExtras = extras != null ? extras : new Bundle();
+ this.mRemoteInputs = remoteInputs;
}
/**
@@ -751,13 +795,22 @@ public class Notification implements Parcelable
}
/**
+ * Get the list of inputs to be collected from the user when this action is sent.
+ * May return null if no remote inputs were added.
+ */
+ public RemoteInput[] getRemoteInputs() {
+ return mRemoteInputs;
+ }
+
+ /**
* Builder class for {@link Action} objects.
*/
- public static class Builder {
+ public static final class Builder {
private final int mIcon;
private final CharSequence mTitle;
private final PendingIntent mIntent;
private final Bundle mExtras;
+ private ArrayList<RemoteInput> mRemoteInputs;
/**
* Construct a new builder for {@link Action} object.
@@ -766,7 +819,7 @@ public class Notification implements Parcelable
* @param intent the {@link PendingIntent} to fire when users trigger this action
*/
public Builder(int icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle());
+ this(icon, title, intent, new Bundle(), null);
}
/**
@@ -775,14 +828,20 @@ public class Notification implements Parcelable
* @param action the action to read fields from.
*/
public Builder(Action action) {
- this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
+ this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
+ action.getRemoteInputs());
}
- private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
+ private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
+ RemoteInput[] remoteInputs) {
mIcon = icon;
mTitle = title;
mIntent = intent;
mExtras = extras;
+ if (remoteInputs != null) {
+ mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
+ Collections.addAll(mRemoteInputs, remoteInputs);
+ }
}
/**
@@ -809,22 +868,62 @@ public class Notification implements Parcelable
}
/**
+ * Add an input to be collected from the user when this action is sent.
+ * Response values can be retrieved from the fired intent by using the
+ * {@link RemoteInput#getResultsFromIntent} function.
+ * @param remoteInput a {@link RemoteInput} to add to the action
+ * @return this object for method chaining
+ */
+ public Builder addRemoteInput(RemoteInput remoteInput) {
+ if (mRemoteInputs == null) {
+ mRemoteInputs = new ArrayList<RemoteInput>();
+ }
+ mRemoteInputs.add(remoteInput);
+ return this;
+ }
+
+ /**
+ * Apply an extender to this action builder. Extenders may be used to add
+ * metadata or change options on this builder.
+ */
+ public Builder apply(Extender extender) {
+ extender.applyTo(this);
+ return this;
+ }
+
+ /**
+ * Extender interface for use with {@link #apply}. Extenders may be used to add
+ * metadata or change options on this builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification action builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder applyTo(Builder builder);
+ }
+
+ /**
* Combine all of the options that have been set and return a new {@link Action}
* object.
* @return the built action
*/
public Action build() {
- return new Action(mIcon, mTitle, mIntent, mExtras);
+ RemoteInput[] remoteInputs = mRemoteInputs != null
+ ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
+ return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
}
}
@Override
public Action clone() {
return new Action(
- this.icon,
- this.title,
- this.actionIntent, // safe to alias
- new Bundle(this.mExtras));
+ icon,
+ title,
+ actionIntent, // safe to alias
+ new Bundle(mExtras),
+ getRemoteInputs());
}
@Override
public int describeContents() {
@@ -841,6 +940,7 @@ public class Notification implements Parcelable
out.writeInt(0);
}
out.writeBundle(mExtras);
+ out.writeTypedArray(mRemoteInputs, flags);
}
public static final Parcelable.Creator<Action> CREATOR =
new Parcelable.Creator<Action>() {
@@ -960,6 +1060,10 @@ public class Notification implements Parcelable
category = parcel.readString();
+ mGroupKey = parcel.readString();
+
+ mSortKey = parcel.readString();
+
extras = parcel.readBundle(); // may be null
actions = parcel.createTypedArray(Action.CREATOR); // may be null
@@ -1037,6 +1141,10 @@ public class Notification implements Parcelable
that.category = this.category;
+ that.mGroupKey = this.mGroupKey;
+
+ that.mSortKey = this.mSortKey;
+
if (this.extras != null) {
try {
that.extras = new Bundle(this.extras);
@@ -1188,6 +1296,10 @@ public class Notification implements Parcelable
parcel.writeString(category);
+ parcel.writeString(mGroupKey);
+
+ parcel.writeString(mSortKey);
+
parcel.writeBundle(extras); // null ok
parcel.writeTypedArray(actions, 0); // null ok
@@ -1325,7 +1437,18 @@ public class Notification implements Parcelable
sb.append(" flags=0x");
sb.append(Integer.toHexString(this.flags));
sb.append(String.format(" color=0x%08x", this.color));
- sb.append(" category="); sb.append(this.category);
+ if (this.category != null) {
+ sb.append(" category=");
+ sb.append(this.category);
+ }
+ if (this.mGroupKey != null) {
+ sb.append(" groupKey=");
+ sb.append(this.mGroupKey);
+ }
+ if (this.mSortKey != null) {
+ sb.append(" sortKey=");
+ sb.append(this.mSortKey);
+ }
if (actions != null) {
sb.append(" ");
sb.append(actions.length);
@@ -1408,6 +1531,8 @@ public class Notification implements Parcelable
private int mProgress;
private boolean mProgressIndeterminate;
private String mCategory;
+ private String mGroupKey;
+ private String mSortKey;
private Bundle mExtras;
private int mPriority;
private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
@@ -1839,6 +1964,51 @@ public class Notification implements Parcelable
}
/**
+ * Set this notification to be part of a group of notifications sharing the same key.
+ * Grouped notifications may display in a cluster or stack on devices which
+ * support such rendering.
+ *
+ * <p>To make this notification the summary for its group, also call
+ * {@link #setGroupSummary}. A sort order can be specified for group members by using
+ * {@link #setSortKey}.
+ * @param groupKey The group key of the group.
+ * @return this object for method chaining
+ */
+ public Builder setGroup(String groupKey) {
+ mGroupKey = groupKey;
+ return this;
+ }
+
+ /**
+ * Set this notification to be the group summary for a group of notifications.
+ * Grouped notifications may display in a cluster or stack on devices which
+ * support such rendering. Requires a group key also be set using {@link #setGroup}.
+ * @param isGroupSummary Whether this notification should be a group summary.
+ * @return this object for method chaining
+ */
+ public Builder setGroupSummary(boolean isGroupSummary) {
+ setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
+ return this;
+ }
+
+ /**
+ * Set a sort key that orders this notification among other notifications from the
+ * same package. This can be useful if an external sort was already applied and an app
+ * would like to preserve this. Notifications will be sorted lexicographically using this
+ * value, although providing different priorities in addition to providing sort key may
+ * cause this value to be ignored.
+ *
+ * <p>This sort key can also be used to order members of a notification group. See
+ * {@link #setGroup}.
+ *
+ * @see String#compareTo(String)
+ */
+ public Builder setSortKey(String sortKey) {
+ mSortKey = sortKey;
+ return this;
+ }
+
+ /**
* Merge additional metadata into this notification.
*
* <p>Values within the Bundle will replace existing extras values in this Builder.
@@ -1949,7 +2119,7 @@ public class Notification implements Parcelable
/**
* Specify the value of {@link #visibility}.
-
+ *
* @param visibility One of {@link #VISIBILITY_PRIVATE} (the default),
* {@link #VISIBILITY_SECRET}, or {@link #VISIBILITY_PUBLIC}.
*
@@ -1971,6 +2141,28 @@ public class Notification implements Parcelable
return this;
}
+ /**
+ * Apply an extender to this notification builder. Extenders may be used to add
+ * metadata or change options on this builder.
+ */
+ public Builder apply(Extender extender) {
+ extender.applyTo(this);
+ return this;
+ }
+
+ /**
+ * Extender interface for use with {@link #apply}. Extenders may be used to add
+ * metadata or change options on this builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder applyTo(Builder builder);
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -2298,6 +2490,8 @@ public class Notification implements Parcelable
n.flags |= FLAG_SHOW_LIGHTS;
}
n.category = mCategory;
+ n.mGroupKey = mGroupKey;
+ n.mSortKey = mSortKey;
n.priority = mPriority;
if (mActions.size() > 0) {
n.actions = new Action[mActions.size()];
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
new file mode 100644
index 0000000..9cfc541
--- /dev/null
+++ b/core/java/android/app/RemoteInput.java
@@ -0,0 +1,297 @@
+/*
+ * 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.app;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A {@code RemoteInput} object specifies input to be collected from a user to be passed along with
+ * an intent inside a {@link android.app.PendingIntent} that is sent.
+ * Always use {@link RemoteInput.Builder} to create instances of this class.
+ * <p class="note"> See
+ * <a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input from
+ * a Notification</a> for more information on how to use this class.
+ *
+ * <p>The following example adds a {@code RemoteInput} to a {@link Notification.Action},
+ * sets the result key as {@code quick_reply}, and sets the label as {@code Quick reply}.
+ * Users are prompted to input a response when they trigger the action. The results are sent along
+ * with the intent and can be retrieved with the result key (provided to the {@link Builder}
+ * constructor) from the Bundle returned by {@link #getResultsFromIntent}.
+ *
+ * <pre class="prettyprint">
+ * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
+ * Notification.Action action = new Notification.Action.Builder(
+ * R.drawable.reply, &quot;Reply&quot;, actionIntent)
+ * <b>.addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT)
+ * .setLabel("Quick reply").build()</b>)
+ * .build();</pre>
+ *
+ * <p>When the {@link android.app.PendingIntent} is fired, the intent inside will contain the
+ * input results if collected. To access these results, use the {@link #getResultsFromIntent}
+ * function. The result values will present under the result key passed to the {@link Builder}
+ * constructor.
+ *
+ * <pre class="prettyprint">
+ * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
+ * Bundle results = RemoteInput.getResultsFromIntent(intent);
+ * if (results != null) {
+ * CharSequence quickReplyResult = results.getCharSequence(KEY_QUICK_REPLY_TEXT);
+ * }</pre>
+ */
+public final class RemoteInput implements Parcelable {
+ /** Label used to denote the clip data type used for remote input transport */
+ public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+
+ /** Extra added to a clip data intent object to hold the results bundle. */
+ public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+
+ private final String mResultKey;
+ private final CharSequence mLabel;
+ private final CharSequence[] mChoices;
+ private final boolean mAllowFreeFormInput;
+ private final Bundle mExtras;
+
+ private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
+ boolean allowFreeFormInput, Bundle extras) {
+ this.mResultKey = resultKey;
+ this.mLabel = label;
+ this.mChoices = choices;
+ this.mAllowFreeFormInput = allowFreeFormInput;
+ this.mExtras = extras;
+ }
+
+ /**
+ * Get the key that the result of this input will be set in from the Bundle returned by
+ * {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
+ */
+ public String getResultKey() {
+ return mResultKey;
+ }
+
+ /**
+ * Get the label to display to users when collecting this input.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Get possible input choices. This can be {@code null} if there are no choices to present.
+ */
+ public CharSequence[] getChoices() {
+ return mChoices;
+ }
+
+ /**
+ * Get whether or not users can provide an arbitrary value for
+ * input. If you set this to {@code false}, users must select one of the
+ * choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
+ * if you set this to false and {@link #getChoices} returns {@code null} or empty.
+ */
+ public boolean getAllowFreeFormInput() {
+ return mAllowFreeFormInput;
+ }
+
+ /**
+ * Get additional metadata carried around with this remote input.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder class for {@link RemoteInput} objects.
+ */
+ public static final class Builder {
+ private final String mResultKey;
+ private CharSequence mLabel;
+ private CharSequence[] mChoices;
+ private boolean mAllowFreeFormInput = true;
+ private Bundle mExtras = new Bundle();
+
+ /**
+ * Create a builder object for {@link RemoteInput} objects.
+ * @param resultKey the Bundle key that refers to this input when collected from the user
+ */
+ public Builder(String resultKey) {
+ if (resultKey == null) {
+ throw new IllegalArgumentException("Result key can't be null");
+ }
+ mResultKey = resultKey;
+ }
+
+ /**
+ * Set a label to be displayed to the user when collecting this input.
+ * @param label The label to show to users when they input a response.
+ * @return this object for method chaining
+ */
+ public Builder setLabel(CharSequence label) {
+ mLabel = Notification.safeCharSequence(label);
+ return this;
+ }
+
+ /**
+ * Specifies choices available to the user to satisfy this input.
+ * @param choices an array of pre-defined choices for users input.
+ * You must provide a non-null and non-empty array if
+ * you disabled free form input using {@link #setAllowFreeFormInput}.
+ * @return this object for method chaining
+ */
+ public Builder setChoices(CharSequence[] choices) {
+ if (choices == null) {
+ mChoices = null;
+ } else {
+ mChoices = new CharSequence[choices.length];
+ for (int i = 0; i < choices.length; i++) {
+ mChoices[i] = Notification.safeCharSequence(choices[i]);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Specifies whether the user can provide arbitrary values.
+ *
+ * @param allowFreeFormInput The default is {@code true}.
+ * If you specify {@code false}, you must provide a non-null
+ * and non-empty array to {@link #setChoices} or an
+ * {@link IllegalArgumentException} is thrown.
+ * @return this object for method chaining
+ */
+ public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
+ mAllowFreeFormInput = allowFreeFormInput;
+ return this;
+ }
+
+ /**
+ * Merge additional metadata into this builder.
+ *
+ * <p>Values within the Bundle will replace existing extras values in this Builder.
+ *
+ * @see RemoteInput#getExtras
+ */
+ public Builder addExtras(Bundle extras) {
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
+ return this;
+ }
+
+ /**
+ * Get the metadata Bundle used by this Builder.
+ *
+ * <p>The returned Bundle is shared with this Builder.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Combine all of the options that have been set and return a new {@link RemoteInput}
+ * object.
+ */
+ public RemoteInput build() {
+ return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
+ }
+ }
+
+ private RemoteInput(Parcel in) {
+ mResultKey = in.readString();
+ mLabel = in.readCharSequence();
+ mChoices = in.readCharSequenceArray();
+ mAllowFreeFormInput = in.readInt() != 0;
+ mExtras = in.readBundle();
+ }
+
+ /**
+ * Get the remote input results bundle from an intent. The returned Bundle will
+ * contain a key/value for every result key populated by remote input collector.
+ * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
+ * @param intent The intent object that fired in response to an action or content intent
+ * which also had one or more remote input requested.
+ */
+ public static Bundle getResultsFromIntent(Intent intent) {
+ ClipData clipData = intent.getClipData();
+ if (clipData == null) {
+ return null;
+ }
+ ClipDescription clipDescription = clipData.getDescription();
+ if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+ return null;
+ }
+ if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
+ return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA);
+ }
+ return null;
+ }
+
+ /**
+ * Populate an intent object with the results gathered from remote input. This method
+ * should only be called by remote input collection services when sending results to a
+ * pending intent.
+ * @param remoteInputs The remote inputs for which results are being provided
+ * @param intent The intent to add remote inputs to. The {@link ClipData}
+ * field of the intent will be modified to contain the results.
+ * @param results A bundle holding the remote input results. This bundle should
+ * be populated with keys matching the result keys specified in
+ * {@code remoteInputs} with values being the result per key.
+ */
+ public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
+ Bundle results) {
+ Bundle resultsBundle = new Bundle();
+ for (RemoteInput remoteInput : remoteInputs) {
+ Object result = results.get(remoteInput.getResultKey());
+ if (result instanceof CharSequence) {
+ resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
+ }
+ }
+ Intent clipIntent = new Intent();
+ clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
+ intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mResultKey);
+ out.writeCharSequence(mLabel);
+ out.writeCharSequenceArray(mChoices);
+ out.writeInt(mAllowFreeFormInput ? 1 : 0);
+ out.writeBundle(mExtras);
+ }
+
+ public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() {
+ @Override
+ public RemoteInput createFromParcel(Parcel in) {
+ return new RemoteInput(in);
+ }
+
+ @Override
+ public RemoteInput[] newArray(int size) {
+ return new RemoteInput[size];
+ }
+ };
+}
diff --git a/core/java/android/app/SharedElementListener.java b/core/java/android/app/SharedElementListener.java
index d4bc019..e03e42e 100644
--- a/core/java/android/app/SharedElementListener.java
+++ b/core/java/android/app/SharedElementListener.java
@@ -22,15 +22,27 @@ import java.util.Map;
/**
* Listener provided in
- * {@link Activity#setSharedElementListener(SharedElementListener)}
- * to monitor the Activity transitions. The events can be used to customize or override Activity
+ * {@link Activity#setEnterSharedElementListener(SharedElementListener)} and
+ * {@link Activity#setExitSharedElementListener(SharedElementListener)}
+ * to monitor the Activity transitions. The events can be used to customize Activity
* Transition behavior.
*/
-public class SharedElementListener {
+public abstract class SharedElementListener {
+
+ static final SharedElementListener NULL_LISTENER = new SharedElementListener() {
+ };
+
/**
- * Called to allow the listener to customize the start state of the shared element for
- * the shared element entering transition. By default, the shared element is placed in
- * the position and with the size of the shared element in the calling Activity or Fragment.
+ * Called to allow the listener to customize the start state of the shared element when
+ * transferring in shared element state.
+ * <p>
+ * The shared element will start at the size and position of the shared element
+ * in the launching Activity or Fragment. It will also transfer ImageView scaleType
+ * and imageMatrix if the shared elements in the calling and called Activities are
+ * ImageViews. Some applications may want to make additional changes, such as
+ * changing the clip bounds, scaling, or rotation if the shared element end state
+ * does not map well to the start state.
+ * </p>
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
@@ -44,8 +56,17 @@ public class SharedElementListener {
List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
- * Called to allow the listener to customize the end state of the shared element for
- * the shared element entering transition.
+ * Called to allow the listener to customize the end state of the shared element when
+ * transferring in shared element state.
+ * <p>
+ * Any customization done in
+ * {@link #setSharedElementStart(java.util.List, java.util.List, java.util.List)}
+ * may need to be modified to the final state of the shared element if it is not
+ * automatically corrected by layout. For example, rotation or scale will not
+ * be affected by layout and if changed in {@link #setSharedElementStart(java.util.List,
+ * java.util.List, java.util.List)}, it will also have to be set here again to correct
+ * the end state.
+ * </p>
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
@@ -59,13 +80,18 @@ public class SharedElementListener {
List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
- * If nothing is done, all shared elements that were not accepted by
- * {@link #remapSharedElements(java.util.List, java.util.Map)} will be Transitioned
- * out of the entering scene automatically. Any elements removed from
- * rejectedSharedElements must be handled by the ActivityTransitionListener.
- * <p>Views in rejectedSharedElements will have their position and size set to the
- * position of the calling shared element, relative to the Window decor View. This
- * view may be safely added to the decor View's overlay to remain in position.</p>
+ * Called after {@link #remapSharedElements(java.util.List, java.util.Map)} when
+ * transferring shared elements in. Any shared elements that have no mapping will be in
+ * <var>rejectedSharedElements</var>. The elements remaining in
+ * <var>rejectedSharedElements</var> will be transitioned out of the Scene. If a
+ * View is removed from <var>rejectedSharedElements</var>, it must be handled by the
+ * <code>SharedElementListener</code>.
+ * <p>
+ * Views in rejectedSharedElements will have their position and size set to the
+ * position of the calling shared element, relative to the Window decor View and contain
+ * snapshots of the View from the calling Activity or Fragment. This
+ * view may be safely added to the decor View's overlay to remain in position.
+ * </p>
*
* @param rejectedSharedElements Views containing visual information of shared elements
* that are not part of the entering scene. These Views
@@ -78,6 +104,7 @@ public class SharedElementListener {
/**
* Lets the ActivityTransitionListener adjust the mapping of shared element names to
* Views.
+ *
* @param names The names of all shared elements transferred from the calling Activity
* to the started Activity.
* @param sharedElements The mapping of shared element names to Views. The best guess
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 58d707c..48ff5b6 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -911,6 +911,35 @@ public class WallpaperManager {
*/
public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
try {
+ /**
+ * The framework makes no attempt to limit the window size
+ * to the maximum texture size. Any window larger than this
+ * cannot be composited.
+ *
+ * Read maximum texture size from system property and scale down
+ * minimumWidth and minimumHeight accordingly.
+ */
+ int maximumTextureSize;
+ try {
+ maximumTextureSize = SystemProperties.getInt("sys.max_texture_size", 0);
+ } catch (Exception e) {
+ maximumTextureSize = 0;
+ }
+
+ if (maximumTextureSize > 0) {
+ if ((minimumWidth > maximumTextureSize) ||
+ (minimumHeight > maximumTextureSize)) {
+ float aspect = (float)minimumHeight / (float)minimumWidth;
+ if (minimumWidth > minimumHeight) {
+ minimumWidth = maximumTextureSize;
+ minimumHeight = (int)((minimumWidth * aspect) + 0.5);
+ } else {
+ minimumHeight = maximumTextureSize;
+ minimumWidth = (int)((minimumHeight / aspect) + 0.5);
+ }
+ }
+ }
+
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
} else {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8884446..18e2a95 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,6 +32,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.util.Log;
@@ -2050,6 +2051,68 @@ public class DevicePolicyManager {
}
/**
+ * Called by device or profile owner to block or unblock packages. When a package is blocked it
+ * is unavailable for use, but the data and actual package file remain.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The name of the package to block or unblock.
+ * @param blocked {@code true} if the package should be blocked, {@code false} if it should be
+ * unblocked.
+ * @return boolean Whether the blocked setting of the package was successfully updated.
+ */
+ public boolean setApplicationBlocked(ComponentName admin, String packageName,
+ boolean blocked) {
+ if (mService != null) {
+ try {
+ return mService.setApplicationBlocked(admin, packageName, blocked);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by profile or device owner to block or unblock currently installed packages. This
+ * should only be called by a profile or device owner running within a managed profile.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param intent An intent matching the app(s) to be updated. All apps that resolve for this
+ * intent will be updated in the current profile.
+ * @param blocked {@code true} if the packages should be blocked, {@code false} if they should
+ * be unblocked.
+ * @return int The number of activities that matched the intent and were updated.
+ */
+ public int setApplicationsBlocked(ComponentName admin, Intent intent, boolean blocked) {
+ if (mService != null) {
+ try {
+ return mService.setApplicationsBlocked(admin, intent, blocked);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Called by device or profile owner to determine if a package is blocked.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The name of the package to retrieve the blocked status of.
+ * @return boolean {@code true} if the package is blocked, {@code false} otherwise.
+ */
+ public boolean isApplicationBlocked(ComponentName admin, String packageName) {
+ if (mService != null) {
+ try {
+ return mService.isApplicationBlocked(admin, packageName);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by profile or device owner to re-enable a system app that was disabled by default
* when the managed profile was created. This should only be called from a profile or device
* owner running within a managed profile.
@@ -2181,4 +2244,42 @@ public class DevicePolicyManager {
}
return false;
}
+
+ /**
+ * Called by device owners to update {@link Settings.Global} settings. Validation that the value
+ * of the setting is in the correct form for the setting type should be performed by the caller.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param setting The name of the setting to update.
+ * @param value The value to update the setting to.
+ */
+ public void setGlobalSetting(ComponentName admin, String setting, String value) {
+ if (mService != null) {
+ try {
+ mService.setGlobalSetting(admin, setting, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Called by profile or device owners to update {@link Settings.Secure} settings. Validation
+ * that the value of the setting is in the correct form for the setting type should be performed
+ * by the caller.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param setting The name of the setting to update.
+ * @param value The value to update the setting to.
+ */
+ public void setSecureSetting(ComponentName admin, String setting, String value) {
+ if (mService != null) {
+ try {
+ mService.setSecureSetting(admin, setting, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 03ced0f..7257158 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -124,6 +124,10 @@ interface IDevicePolicyManager {
void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearForwardingIntentFilters(in ComponentName admin);
+ boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked);
+ int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
+ boolean isApplicationBlocked(in ComponentName admin, in String packageName);
+
void enableSystemApp(in ComponentName admin, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
@@ -133,4 +137,7 @@ interface IDevicePolicyManager {
void setLockTaskComponents(in ComponentName[] components);
ComponentName[] getLockTaskComponents();
boolean isLockTaskPermitted(in ComponentName component);
+
+ void setGlobalSetting(in ComponentName who, in String setting, in String value);
+ void setSecureSetting(in ComponentName who, in String setting, in String value);
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index e79deec..9e1c995 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -498,6 +498,34 @@ public final class BluetoothAdapter {
}
/**
+ * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
+ */
+ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
+ // TODO: Return null if this feature is not supported by hardware.
+ try {
+ IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+ return new BluetoothLeAdvertiser(iGatt);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
+ */
+ public BluetoothLeScanner getBluetoothLeScanner() {
+ // TODO: Return null if BLE scan is not supported by hardware.
+ try {
+ IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+ return new BluetoothLeScanner(iGatt);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e);
+ return null;
+ }
+ }
+
+ /**
* Interface for BLE advertising callback.
*
* @hide
@@ -2024,6 +2052,10 @@ public final class BluetoothAdapter {
}
}
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ // no op
+ }
/**
* Callback reporting LE ATT MTU.
* @hide
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 601d9ee..c9df9c0 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -581,7 +581,15 @@ public final class BluetoothGatt implements BluetoothProfile {
public void onAdvertiseStateChange(int state, int status) {
if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
+ state + " status=" + status);
- }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ // no op.
+ }
/**
* Callback invoked when the MTU for a given connection changes
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl
new file mode 100644
index 0000000..4aa8881
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.bluetooth;
+
+parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
new file mode 100644
index 0000000..d12ac6c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
@@ -0,0 +1,648 @@
+/*
+ * 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.bluetooth;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement
+ * data to be advertised, or the scan record obtained from BLE scans.
+ * <p>
+ * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth
+ * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth
+ * Core Specification Version 4. Currently the following fields are allowed to be set:
+ * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
+ * <li>Tx power level which is the transmission power level.
+ * <li>Service data which is the data associated with a service.
+ * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
+ *
+ * @see BluetoothLeAdvertiser
+ */
+public final class BluetoothLeAdvertiseScanData {
+ private static final String TAG = "BluetoothLeAdvertiseScanData";
+
+ /**
+ * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising
+ * packet.
+ */
+ public static final int ADVERTISING_DATA = 0;
+ /**
+ * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising
+ * packet.
+ * <p>
+ * TODO: unhide when stack supports setting scan response data.
+ *
+ * @hide
+ */
+ public static final int SCAN_RESPONSE_DATA = 1;
+ /**
+ * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of
+ * advertising data and scan response data.
+ */
+ public static final int PARSED_SCAN_RECORD = 2;
+
+ /**
+ * Base data type which contains the common fields for {@link AdvertisementData} and
+ * {@link ScanRecord}.
+ */
+ public abstract static class AdvertiseBaseData {
+
+ private final int mDataType;
+
+ @Nullable
+ private final List<ParcelUuid> mServiceUuids;
+
+ private final int mManufacturerId;
+ @Nullable
+ private final byte[] mManufacturerSpecificData;
+
+ @Nullable
+ private final ParcelUuid mServiceDataUuid;
+ @Nullable
+ private final byte[] mServiceData;
+
+ private AdvertiseBaseData(int dataType,
+ List<ParcelUuid> serviceUuids,
+ ParcelUuid serviceDataUuid, byte[] serviceData,
+ int manufacturerId,
+ byte[] manufacturerSpecificData) {
+ mDataType = dataType;
+ mServiceUuids = serviceUuids;
+ mManufacturerId = manufacturerId;
+ mManufacturerSpecificData = manufacturerSpecificData;
+ mServiceDataUuid = serviceDataUuid;
+ mServiceData = serviceData;
+ }
+
+ /**
+ * Returns the type of data, indicating whether the data is advertising data, scan response
+ * data or scan record.
+ */
+ public int getDataType() {
+ return mDataType;
+ }
+
+ /**
+ * Returns a list of service uuids within the advertisement that are used to identify the
+ * bluetooth gatt services.
+ */
+ public List<ParcelUuid> getServiceUuids() {
+ return mServiceUuids;
+ }
+
+ /**
+ * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
+ * SIG.
+ */
+ public int getManufacturerId() {
+ return mManufacturerId;
+ }
+
+ /**
+ * Returns the manufacturer specific data which is the content of manufacturer specific data
+ * field. The first 2 bytes of the data contain the company id.
+ */
+ public byte[] getManufacturerSpecificData() {
+ return mManufacturerSpecificData;
+ }
+
+ /**
+ * Returns a 16 bit uuid of the service that the service data is associated with.
+ */
+ public ParcelUuid getServiceDataUuid() {
+ return mServiceDataUuid;
+ }
+
+ /**
+ * Returns service data. The first two bytes should be a 16 bit service uuid associated with
+ * the service data.
+ */
+ public byte[] getServiceData() {
+ return mServiceData;
+ }
+
+ @Override
+ public String toString() {
+ return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids
+ + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
+ + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+ + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]";
+ }
+ }
+
+ /**
+ * Advertisement data packet for Bluetooth LE advertising. This represents the data to be
+ * broadcasted in Bluetooth LE advertising.
+ * <p>
+ * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to
+ * be advertised.
+ *
+ * @see BluetoothLeAdvertiser
+ */
+ public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable {
+
+ private boolean mIncludeTxPowerLevel;
+
+ /**
+ * Whether the transmission power level will be included in the advertisement packet.
+ */
+ public boolean getIncludeTxPowerLevel() {
+ return mIncludeTxPowerLevel;
+ }
+
+ /**
+ * Returns a {@link Builder} to build {@link AdvertisementData}.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getDataType());
+ List<ParcelUuid> uuids = getServiceUuids();
+ if (uuids == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(uuids.size());
+ dest.writeList(uuids);
+ }
+
+ dest.writeInt(getManufacturerId());
+ byte[] manufacturerData = getManufacturerSpecificData();
+ if (manufacturerData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(manufacturerData.length);
+ dest.writeByteArray(manufacturerData);
+ }
+
+ ParcelUuid serviceDataUuid = getServiceDataUuid();
+ if (serviceDataUuid == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeParcelable(serviceDataUuid, flags);
+ byte[] serviceData = getServiceData();
+ if (serviceData == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(serviceData.length);
+ dest.writeByteArray(serviceData);
+ }
+ }
+ dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
+ }
+
+ private AdvertisementData(int dataType,
+ List<ParcelUuid> serviceUuids,
+ ParcelUuid serviceDataUuid, byte[] serviceData,
+ int manufacturerId,
+ byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) {
+ super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
+ manufacturerSpecificData);
+ this.mIncludeTxPowerLevel = mIncludeTxPowerLevel;
+ }
+
+ public static final Parcelable.Creator<AdvertisementData> CREATOR =
+ new Creator<AdvertisementData>() {
+ @Override
+ public AdvertisementData[] newArray(int size) {
+ return new AdvertisementData[size];
+ }
+
+ @Override
+ public AdvertisementData createFromParcel(Parcel in) {
+ Builder builder = newBuilder();
+ int dataType = in.readInt();
+ builder.dataType(dataType);
+ if (in.readInt() > 0) {
+ List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
+ in.readList(uuids, ParcelUuid.class.getClassLoader());
+ builder.serviceUuids(uuids);
+ }
+ int manufacturerId = in.readInt();
+ int manufacturerDataLength = in.readInt();
+ if (manufacturerDataLength > 0) {
+ byte[] manufacturerData = new byte[manufacturerDataLength];
+ in.readByteArray(manufacturerData);
+ builder.manufacturerData(manufacturerId, manufacturerData);
+ }
+ if (in.readInt() == 1) {
+ ParcelUuid serviceDataUuid = in.readParcelable(
+ ParcelUuid.class.getClassLoader());
+ int serviceDataLength = in.readInt();
+ if (serviceDataLength > 0) {
+ byte[] serviceData = new byte[serviceDataLength];
+ in.readByteArray(serviceData);
+ builder.serviceData(serviceDataUuid, serviceData);
+ }
+ }
+ builder.includeTxPowerLevel(in.readByte() == 1);
+ return builder.build();
+ }
+ };
+
+ /**
+ * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use
+ * {@link AdvertisementData#newBuilder()} to get an instance of the Builder.
+ */
+ public static final class Builder {
+ private static final int MAX_ADVERTISING_DATA_BYTES = 31;
+ // Each fields need one byte for field length and another byte for field type.
+ private static final int OVERHEAD_BYTES_PER_FIELD = 2;
+ // Flags field will be set by system.
+ private static final int FLAGS_FIELD_BYTES = 3;
+
+ private int mDataType;
+ @Nullable
+ private List<ParcelUuid> mServiceUuids;
+ private boolean mIncludeTxPowerLevel;
+ private int mManufacturerId;
+ @Nullable
+ private byte[] mManufacturerSpecificData;
+ @Nullable
+ private ParcelUuid mServiceDataUuid;
+ @Nullable
+ private byte[] mServiceData;
+
+ /**
+ * Set data type.
+ *
+ * @param dataType Data type, could only be
+ * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA}
+ * @throws IllegalArgumentException If the {@code dataType} is invalid.
+ */
+ public Builder dataType(int dataType) {
+ if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) {
+ throw new IllegalArgumentException("invalid data type - " + dataType);
+ }
+ mDataType = dataType;
+ return this;
+ }
+
+ /**
+ * Set the service uuids. Note the corresponding bluetooth Gatt services need to be
+ * already added on the device before start BLE advertising.
+ *
+ * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or
+ * 128-bit uuids.
+ * @throws IllegalArgumentException If the {@code serviceUuids} are null.
+ */
+ public Builder serviceUuids(List<ParcelUuid> serviceUuids) {
+ if (serviceUuids == null) {
+ throw new IllegalArgumentException("serivceUuids are null");
+ }
+ mServiceUuids = serviceUuids;
+ return this;
+ }
+
+ /**
+ * Add service data to advertisement.
+ *
+ * @param serviceDataUuid A 16 bit uuid of the service data
+ * @param serviceData Service data - the first two bytes of the service data are the
+ * service data uuid.
+ * @throws IllegalArgumentException If the {@code serviceDataUuid} or
+ * {@code serviceData} is empty.
+ */
+ public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
+ if (serviceDataUuid == null || serviceData == null) {
+ throw new IllegalArgumentException(
+ "serviceDataUuid or serviceDataUuid is null");
+ }
+ mServiceDataUuid = serviceDataUuid;
+ mServiceData = serviceData;
+ return this;
+ }
+
+ /**
+ * Set manufacturer id and data. See <a
+ * href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers">assigned
+ * manufacturer identifies</a> for the existing company identifiers.
+ *
+ * @param manufacturerId Manufacturer id assigned by Bluetooth SIG.
+ * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of
+ * the manufacturer specific data are the manufacturer id.
+ * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
+ * {@code manufacturerSpecificData} is null.
+ */
+ public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
+ if (manufacturerId < 0) {
+ throw new IllegalArgumentException(
+ "invalid manufacturerId - " + manufacturerId);
+ }
+ if (manufacturerSpecificData == null) {
+ throw new IllegalArgumentException("manufacturerSpecificData is null");
+ }
+ mManufacturerId = manufacturerId;
+ mManufacturerSpecificData = manufacturerSpecificData;
+ return this;
+ }
+
+ /**
+ * Whether the transmission power level should be included in the advertising packet.
+ */
+ public Builder includeTxPowerLevel(boolean includeTxPowerLevel) {
+ mIncludeTxPowerLevel = includeTxPowerLevel;
+ return this;
+ }
+
+ /**
+ * Build the {@link BluetoothLeAdvertiseScanData}.
+ *
+ * @throws IllegalArgumentException If the data size is larger than 31 bytes.
+ */
+ public AdvertisementData build() {
+ if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) {
+ throw new IllegalArgumentException(
+ "advertisement data size is larger than 31 bytes");
+ }
+ return new AdvertisementData(mDataType,
+ mServiceUuids,
+ mServiceDataUuid,
+ mServiceData, mManufacturerId, mManufacturerSpecificData,
+ mIncludeTxPowerLevel);
+ }
+
+ // Compute the size of the advertisement data.
+ private int totalBytes() {
+ int size = FLAGS_FIELD_BYTES; // flags field is always set.
+ if (mServiceUuids != null) {
+ int num16BitUuids = 0;
+ int num32BitUuids = 0;
+ int num128BitUuids = 0;
+ for (ParcelUuid uuid : mServiceUuids) {
+ if (BluetoothUuid.is16BitUuid(uuid)) {
+ ++num16BitUuids;
+ } else if (BluetoothUuid.is32BitUuid(uuid)) {
+ ++num32BitUuids;
+ } else {
+ ++num128BitUuids;
+ }
+ }
+ // 16 bit service uuids are grouped into one field when doing advertising.
+ if (num16BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
+ }
+ // 32 bit service uuids are grouped into one field when doing advertising.
+ if (num32BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
+ }
+ // 128 bit service uuids are grouped into one field when doing advertising.
+ if (num128BitUuids != 0) {
+ size += OVERHEAD_BYTES_PER_FIELD +
+ num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
+ }
+ }
+ if (mServiceData != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
+ }
+ if (mManufacturerSpecificData != null) {
+ size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
+ }
+ if (mIncludeTxPowerLevel) {
+ size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
+ }
+ return size;
+ }
+ }
+
+ }
+
+ /**
+ * Represents a scan record from Bluetooth LE scan.
+ */
+ public static final class ScanRecord extends AdvertiseBaseData {
+ // Flags of the advertising data.
+ private final int mAdvertiseFlags;
+
+ // Transmission power level(in dB).
+ private final int mTxPowerLevel;
+
+ // Local name of the Bluetooth LE device.
+ private final String mLocalName;
+
+ /**
+ * Returns the advertising flags indicating the discoverable mode and capability of the
+ * device. Returns -1 if the flag field is not set.
+ */
+ public int getAdvertiseFlags() {
+ return mAdvertiseFlags;
+ }
+
+ /**
+ * Returns the transmission power level of the packet in dBm. Returns
+ * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate
+ * the path loss of a received packet using the following equation:
+ * <p>
+ * <code>pathloss = txPowerLevel - rssi</code>
+ */
+ public int getTxPowerLevel() {
+ return mTxPowerLevel;
+ }
+
+ /**
+ * Returns the local name of the BLE device. The is a UTF-8 encoded string.
+ */
+ @Nullable
+ public String getLocalName() {
+ return mLocalName;
+ }
+
+ ScanRecord(int dataType,
+ List<ParcelUuid> serviceUuids,
+ ParcelUuid serviceDataUuid, byte[] serviceData,
+ int manufacturerId,
+ byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
+ String localName) {
+ super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
+ manufacturerSpecificData);
+ mLocalName = localName;
+ mAdvertiseFlags = advertiseFlags;
+ mTxPowerLevel = txPowerLevel;
+ }
+
+ /**
+ * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}.
+ */
+ public static Parser getParser() {
+ return new Parser();
+ }
+
+ /**
+ * A parser class used to parse a Bluetooth LE scan record to
+ * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed.
+ */
+ public static final class Parser {
+ private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser";
+
+ // The following data type values are assigned by Bluetooth SIG.
+ // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18.
+ private static final int DATA_TYPE_FLAGS = 0x01;
+ private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
+ private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
+ private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
+ private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
+ private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
+ private static final int DATA_TYPE_SERVICE_DATA = 0x16;
+ private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
+
+ // Helper method to extract bytes from byte array.
+ private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
+ byte[] bytes = new byte[length];
+ System.arraycopy(scanRecord, start, bytes, 0, length);
+ return bytes;
+ }
+
+ /**
+ * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}.
+ * <p>
+ * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
+ * and 18.
+ * <p>
+ * All numerical multi-byte entities and values shall use little-endian
+ * <strong>byte</strong> order.
+ *
+ * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
+ */
+ public ScanRecord parseFromScanRecord(byte[] scanRecord) {
+ if (scanRecord == null) {
+ return null;
+ }
+
+ int currentPos = 0;
+ int advertiseFlag = -1;
+ List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
+ String localName = null;
+ int txPowerLevel = Integer.MIN_VALUE;
+ ParcelUuid serviceDataUuid = null;
+ byte[] serviceData = null;
+ int manufacturerId = -1;
+ byte[] manufacturerSpecificData = null;
+
+ try {
+ while (currentPos < scanRecord.length) {
+ // length is unsigned int.
+ int length = scanRecord[currentPos++] & 0xFF;
+ if (length == 0) {
+ break;
+ }
+ // Note the length includes the length of the field type itself.
+ int dataLength = length - 1;
+ // fieldType is unsigned int.
+ int fieldType = scanRecord[currentPos++] & 0xFF;
+ switch (fieldType) {
+ case DATA_TYPE_FLAGS:
+ advertiseFlag = scanRecord[currentPos] & 0xFF;
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos,
+ dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_LOCAL_NAME_SHORT:
+ case DATA_TYPE_LOCAL_NAME_COMPLETE:
+ localName = new String(
+ extractBytes(scanRecord, currentPos, dataLength));
+ break;
+ case DATA_TYPE_TX_POWER_LEVEL:
+ txPowerLevel = scanRecord[currentPos];
+ break;
+ case DATA_TYPE_SERVICE_DATA:
+ serviceData = extractBytes(scanRecord, currentPos, dataLength);
+ // The first two bytes of the service data are service data uuid.
+ int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
+ byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
+ serviceUuidLength);
+ serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
+ break;
+ case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
+ manufacturerSpecificData = extractBytes(scanRecord, currentPos,
+ dataLength);
+ // The first two bytes of the manufacturer specific data are
+ // manufacturer ids in little endian.
+ manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) +
+ (manufacturerSpecificData[0] & 0xFF);
+ break;
+ default:
+ // Just ignore, we don't handle such data type.
+ break;
+ }
+ currentPos += dataLength;
+ }
+
+ if (serviceUuids.isEmpty()) {
+ serviceUuids = null;
+ }
+ return new ScanRecord(PARSED_SCAN_RECORD,
+ serviceUuids, serviceDataUuid, serviceData,
+ manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
+ localName);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(PARSER_TAG,
+ "unable to parse scan record: " + Arrays.toString(scanRecord));
+ return null;
+ }
+ }
+
+ // Parse service uuids.
+ private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
+ int uuidLength, List<ParcelUuid> serviceUuids) {
+ while (dataLength > 0) {
+ byte[] uuidBytes = extractBytes(scanRecord, currentPos,
+ uuidLength);
+ serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
+ dataLength -= uuidLength;
+ currentPos += uuidLength;
+ }
+ return currentPos;
+ }
+ }
+ }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl
new file mode 100644
index 0000000..3108610
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.bluetooth;
+
+parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/BluetoothLeAdvertiser.java
new file mode 100644
index 0000000..2a8aa23
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeAdvertiser.java
@@ -0,0 +1,600 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop
+ * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by
+ * {@link BluetoothLeAdvertiseScanData.AdvertisementData}.
+ * <p>
+ * To get an instance of {@link BluetoothLeAdvertiser}, call the
+ * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
+ * <p>
+ * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see BluetoothLeAdvertiseScanData.AdvertisementData
+ */
+public class BluetoothLeAdvertiser {
+
+ private static final String TAG = "BluetoothLeAdvertiser";
+
+ /**
+ * The {@link Settings} provide a way to adjust advertising preferences for each individual
+ * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance.
+ */
+ public static final class Settings implements Parcelable {
+ /**
+ * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
+ * advertising mode as it consumes the least power.
+ */
+ public static final int ADVERTISE_MODE_LOW_POWER = 0;
+ /**
+ * Perform Bluetooth LE advertising in balanced power mode. This is balanced between
+ * advertising frequency and power consumption.
+ */
+ public static final int ADVERTISE_MODE_BALANCED = 1;
+ /**
+ * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest
+ * power consumption and should not be used for background continuous advertising.
+ */
+ public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
+
+ /**
+ * Advertise using the lowest transmission(tx) power level. An app can use low transmission
+ * power to restrict the visibility range of its advertising packet.
+ */
+ public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
+ /**
+ * Advertise using low tx power level.
+ */
+ public static final int ADVERTISE_TX_POWER_LOW = 1;
+ /**
+ * Advertise using medium tx power level.
+ */
+ public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
+ /**
+ * Advertise using high tx power level. This is corresponding to largest visibility range of
+ * the advertising packet.
+ */
+ public static final int ADVERTISE_TX_POWER_HIGH = 3;
+
+ /**
+ * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0
+ * vol6, part B, section 4.4.2 - Advertising state.
+ */
+ public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0;
+ /**
+ * Scannable undirected advertise type, as defined in same spec mentioned above. This event
+ * type allows a scanner to send a scan request asking additional information about the
+ * advertiser.
+ */
+ public static final int ADVERTISE_TYPE_SCANNABLE = 1;
+ /**
+ * Connectable undirected advertising type, as defined in same spec mentioned above. This
+ * event type allows a scanner to send scan request asking additional information about the
+ * advertiser. It also allows an initiator to send a connect request for connection.
+ */
+ public static final int ADVERTISE_TYPE_CONNECTABLE = 2;
+
+ private final int mAdvertiseMode;
+ private final int mAdvertiseTxPowerLevel;
+ private final int mAdvertiseEventType;
+
+ private Settings(int advertiseMode, int advertiseTxPowerLevel,
+ int advertiseEventType) {
+ mAdvertiseMode = advertiseMode;
+ mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
+ mAdvertiseEventType = advertiseEventType;
+ }
+
+ private Settings(Parcel in) {
+ mAdvertiseMode = in.readInt();
+ mAdvertiseTxPowerLevel = in.readInt();
+ mAdvertiseEventType = in.readInt();
+ }
+
+ /**
+ * Creates a {@link Builder} to construct a {@link Settings} object.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns the advertise mode.
+ */
+ public int getMode() {
+ return mAdvertiseMode;
+ }
+
+ /**
+ * Returns the tx power level for advertising.
+ */
+ public int getTxPowerLevel() {
+ return mAdvertiseTxPowerLevel;
+ }
+
+ /**
+ * Returns the advertise event type.
+ */
+ public int getType() {
+ return mAdvertiseEventType;
+ }
+
+ @Override
+ public String toString() {
+ return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel="
+ + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAdvertiseMode);
+ dest.writeInt(mAdvertiseTxPowerLevel);
+ dest.writeInt(mAdvertiseEventType);
+ }
+
+ public static final Parcelable.Creator<Settings> CREATOR =
+ new Creator<BluetoothLeAdvertiser.Settings>() {
+ @Override
+ public Settings[] newArray(int size) {
+ return new Settings[size];
+ }
+
+ @Override
+ public Settings createFromParcel(Parcel in) {
+ return new Settings(in);
+ }
+ };
+
+ /**
+ * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use
+ * {@link Settings#newBuilder()} to get an instance of the builder.
+ */
+ public static final class Builder {
+ private int mMode = ADVERTISE_MODE_LOW_POWER;
+ private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
+ private int mType = ADVERTISE_TYPE_NON_CONNECTABLE;
+
+ // Private constructor, use Settings.newBuilder() get an instance of BUILDER.
+ private Builder() {
+ }
+
+ /**
+ * Set advertise mode to control the advertising power and latency.
+ *
+ * @param advertiseMode Bluetooth LE Advertising mode, can only be one of
+ * {@link Settings#ADVERTISE_MODE_LOW_POWER},
+ * {@link Settings#ADVERTISE_MODE_BALANCED}, or
+ * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}.
+ * @throws IllegalArgumentException If the advertiseMode is invalid.
+ */
+ public Builder advertiseMode(int advertiseMode) {
+ if (advertiseMode < ADVERTISE_MODE_LOW_POWER
+ || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
+ throw new IllegalArgumentException("unknown mode " + advertiseMode);
+ }
+ mMode = advertiseMode;
+ return this;
+ }
+
+ /**
+ * Set advertise tx power level to control the transmission power level for the
+ * advertising.
+ *
+ * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one
+ * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW},
+ * {@link Settings#ADVERTISE_TX_POWER_LOW},
+ * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or
+ * {@link Settings#ADVERTISE_TX_POWER_HIGH}.
+ * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+ */
+ public Builder txPowerLevel(int txPowerLevel) {
+ if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
+ || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
+ throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
+ }
+ mTxPowerLevel = txPowerLevel;
+ return this;
+ }
+
+ /**
+ * Set advertise type to control the event type of advertising.
+ *
+ * @param type Bluetooth LE Advertising type, can be either
+ * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE},
+ * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or
+ * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}.
+ * @throws IllegalArgumentException If the {@code type} is invalid.
+ */
+ public Builder type(int type) {
+ if (type < ADVERTISE_TYPE_NON_CONNECTABLE
+ || type > ADVERTISE_TYPE_CONNECTABLE) {
+ throw new IllegalArgumentException("unknown advertise type " + type);
+ }
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Build the {@link Settings} object.
+ */
+ public Settings build() {
+ return new Settings(mMode, mTxPowerLevel, mType);
+ }
+ }
+ }
+
+ /**
+ * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and
+ * stop advertising.
+ */
+ public interface AdvertiseCallback {
+
+ /**
+ * The operation is success.
+ *
+ * @hide
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Fails to start advertising as the advertisement data contains services that are not added
+ * to the local bluetooth Gatt server.
+ */
+ public static final int ADVERTISING_SERVICE_UNKNOWN = 1;
+ /**
+ * Fails to start advertising as system runs out of quota for advertisers.
+ */
+ public static final int TOO_MANY_ADVERTISERS = 2;
+
+ /**
+ * Fails to start advertising as the advertising is already started.
+ */
+ public static final int ADVERTISING_ALREADY_STARTED = 3;
+ /**
+ * Fails to stop advertising as the advertising is not started.
+ */
+ public static final int ADVERISING_NOT_STARTED = 4;
+
+ /**
+ * Operation fails due to bluetooth controller failure.
+ */
+ public static final int CONTROLLER_FAILURE = 5;
+
+ /**
+ * Callback when advertising operation succeeds.
+ *
+ * @param settingsInEffect The actual settings used for advertising, which may be different
+ * from what the app asks.
+ */
+ public void onSuccess(Settings settingsInEffect);
+
+ /**
+ * Callback when advertising operation fails.
+ *
+ * @param errorCode Error code for failures.
+ */
+ public void onFailure(int errorCode);
+ }
+
+ private final IBluetoothGatt mBluetoothGatt;
+ private final Handler mHandler;
+ private final Map<Settings, AdvertiseCallbackWrapper>
+ mLeAdvertisers = new HashMap<Settings, AdvertiseCallbackWrapper>();
+
+ // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead.
+ BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
+ mBluetoothGatt = bluetoothGatt;
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ /**
+ * Bluetooth GATT interface callbacks for advertising.
+ */
+ private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
+ private final AdvertiseCallback mAdvertiseCallback;
+ private final AdvertisementData mAdvertisement;
+ private final Settings mSettings;
+ private final IBluetoothGatt mBluetoothGatt;
+
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // >0: registered and scan started
+ private int mLeHandle;
+ private boolean isAdvertising = false;
+
+ public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
+ AdvertisementData advertiseData, Settings settings,
+ IBluetoothGatt bluetoothGatt) {
+ mAdvertiseCallback = advertiseCallback;
+ mAdvertisement = advertiseData;
+ mSettings = settings;
+ mBluetoothGatt = bluetoothGatt;
+ mLeHandle = 0;
+ }
+
+ public boolean advertiseStarted() {
+ boolean started = false;
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ return false;
+ }
+ try {
+ wait(LE_CALLBACK_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ started = (mLeHandle > 0 && isAdvertising);
+ }
+ return started;
+ }
+
+ public boolean advertiseStopped() {
+ synchronized (this) {
+ try {
+ wait(LE_CALLBACK_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ return !isAdvertising;
+ }
+ }
+
+ /**
+ * Application interface registered - app is ready to go
+ */
+ @Override
+ public void onClientRegistered(int status, int clientIf) {
+ Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
+ synchronized (this) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ try {
+ mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, mSettings);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le advertise: " + e);
+ mLeHandle = -1;
+ notifyAll();
+ } catch (Exception e) {
+ Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ @Override
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ // no op
+ }
+
+ @Override
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
+
+ @Override
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descUuid) {
+ // no op
+ }
+
+ @Override
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ @Override
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid) {
+ // no op
+ }
+
+ @Override
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+
+ @Override
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ // no op
+ }
+
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ synchronized (this) {
+ if (status == 0) {
+ isAdvertising = !isAdvertising;
+ if (!isAdvertising) {
+ try {
+ mBluetoothGatt.unregisterClient(mLeHandle);
+ mLeHandle = -1;
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception when unregistering", e);
+ }
+ }
+ mAdvertiseCallback.onSuccess(null);
+ } else {
+ mAdvertiseCallback.onFailure(status);
+ }
+ notifyAll();
+ }
+
+ }
+
+ /**
+ * Callback reporting LE ATT MTU.
+ *
+ * @hide
+ */
+ public void onConfigureMTU(String address, int mtu, int status) {
+ // no op
+ }
+ }
+
+ /**
+ * Start Bluetooth LE Advertising.
+ *
+ * @param settings {@link Settings} for Bluetooth LE advertising.
+ * @param advertiseData {@link AdvertisementData} to be advertised.
+ * @param callback {@link AdvertiseCallback} for advertising status.
+ */
+ public void startAdvertising(Settings settings,
+ AdvertisementData advertiseData, final AdvertiseCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (mLeAdvertisers.containsKey(settings)) {
+ postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED);
+ return;
+ }
+ AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
+ settings,
+ mBluetoothGatt);
+ UUID uuid = UUID.randomUUID();
+ try {
+ mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.advertiseStarted()) {
+ mLeAdvertisers.put(settings, wrapper);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop advertising", e);
+ }
+ }
+
+ /**
+ * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered
+ * through the {@code callback}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param settings {@link Settings} used to start Bluetooth LE advertising.
+ * @param callback {@link AdvertiseCallback} for delivering stopping advertising status.
+ */
+ public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings);
+ if (wrapper == null) {
+ postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED);
+ return;
+ }
+ try {
+ mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
+ if (wrapper.advertiseStopped()) {
+ mLeAdvertisers.remove(settings);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to stop advertising", e);
+ }
+ }
+
+ private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onFailure(error);
+ }
+ });
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
new file mode 100644
index 0000000..86ee06d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.bluetooth;
+
+parcelable BluetoothLeScanFilter;
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.java b/core/java/android/bluetooth/BluetoothLeScanFilter.java
new file mode 100644
index 0000000..2ed85ba
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeScanFilter.java
@@ -0,0 +1,577 @@
+/*
+ * 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.bluetooth;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord;
+import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement
+ * packet fields.
+ * <p>
+ * Current filtering on the following fields are supported:
+ * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
+ * <li>Name of remote Bluetooth LE device.
+ * <li>Mac address of the remote device.
+ * <li>Rssi which indicates the received power level.
+ * <li>Service data which is the data associated with a service.
+ * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
+ *
+ * @see BluetoothLeAdvertiseScanData.ScanRecord
+ * @see BluetoothLeScanner
+ */
+public final class BluetoothLeScanFilter implements Parcelable {
+
+ @Nullable
+ private final String mLocalName;
+
+ @Nullable
+ private final String mMacAddress;
+
+ @Nullable
+ private final ParcelUuid mServiceUuid;
+ @Nullable
+ private final ParcelUuid mServiceUuidMask;
+
+ @Nullable
+ private final byte[] mServiceData;
+ @Nullable
+ private final byte[] mServiceDataMask;
+
+ private final int mManufacturerId;
+ @Nullable
+ private final byte[] mManufacturerData;
+ @Nullable
+ private final byte[] mManufacturerDataMask;
+
+ private final int mMinRssi;
+ private final int mMaxRssi;
+
+ private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid,
+ ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask,
+ int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
+ int minRssi, int maxRssi) {
+ mLocalName = name;
+ mServiceUuid = uuid;
+ mServiceUuidMask = uuidMask;
+ mMacAddress = macAddress;
+ mServiceData = serviceData;
+ mServiceDataMask = serviceDataMask;
+ mManufacturerId = manufacturerId;
+ mManufacturerData = manufacturerData;
+ mManufacturerDataMask = manufacturerDataMask;
+ mMinRssi = minRssi;
+ mMaxRssi = maxRssi;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mLocalName == null ? 0 : 1);
+ if (mLocalName != null) {
+ dest.writeString(mLocalName);
+ }
+ dest.writeInt(mMacAddress == null ? 0 : 1);
+ if (mMacAddress != null) {
+ dest.writeString(mMacAddress);
+ }
+ dest.writeInt(mServiceUuid == null ? 0 : 1);
+ if (mServiceUuid != null) {
+ dest.writeParcelable(mServiceUuid, flags);
+ }
+ dest.writeInt(mServiceUuidMask == null ? 0 : 1);
+ if (mServiceUuidMask != null) {
+ dest.writeParcelable(mServiceUuidMask, flags);
+ }
+ dest.writeInt(mServiceData == null ? 0 : mServiceData.length);
+ if (mServiceData != null) {
+ dest.writeByteArray(mServiceData);
+ }
+ dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
+ if (mServiceDataMask != null) {
+ dest.writeByteArray(mServiceDataMask);
+ }
+ dest.writeInt(mManufacturerId);
+ dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length);
+ if (mManufacturerData != null) {
+ dest.writeByteArray(mManufacturerData);
+ }
+ dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
+ if (mManufacturerDataMask != null) {
+ dest.writeByteArray(mManufacturerDataMask);
+ }
+ dest.writeInt(mMinRssi);
+ dest.writeInt(mMaxRssi);
+ }
+
+ /**
+ * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel.
+ */
+ public static final Creator<BluetoothLeScanFilter>
+ CREATOR = new Creator<BluetoothLeScanFilter>() {
+
+ @Override
+ public BluetoothLeScanFilter[] newArray(int size) {
+ return new BluetoothLeScanFilter[size];
+ }
+
+ @Override
+ public BluetoothLeScanFilter createFromParcel(Parcel in) {
+ Builder builder = newBuilder();
+ if (in.readInt() == 1) {
+ builder.name(in.readString());
+ }
+ if (in.readInt() == 1) {
+ builder.macAddress(in.readString());
+ }
+ if (in.readInt() == 1) {
+ ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
+ builder.serviceUuid(uuid);
+ }
+ if (in.readInt() == 1) {
+ ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader());
+ builder.serviceUuidMask(uuidMask);
+ }
+ int serviceDataLength = in.readInt();
+ if (serviceDataLength > 0) {
+ byte[] serviceData = new byte[serviceDataLength];
+ in.readByteArray(serviceData);
+ builder.serviceData(serviceData);
+ }
+ int serviceDataMaskLength = in.readInt();
+ if (serviceDataMaskLength > 0) {
+ byte[] serviceDataMask = new byte[serviceDataMaskLength];
+ in.readByteArray(serviceDataMask);
+ builder.serviceDataMask(serviceDataMask);
+ }
+ int manufacturerId = in.readInt();
+ int manufacturerDataLength = in.readInt();
+ if (manufacturerDataLength > 0) {
+ byte[] manufacturerData = new byte[manufacturerDataLength];
+ in.readByteArray(manufacturerData);
+ builder.manufacturerData(manufacturerId, manufacturerData);
+ }
+ int manufacturerDataMaskLength = in.readInt();
+ if (manufacturerDataMaskLength > 0) {
+ byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
+ in.readByteArray(manufacturerDataMask);
+ builder.manufacturerDataMask(manufacturerDataMask);
+ }
+ int minRssi = in.readInt();
+ int maxRssi = in.readInt();
+ builder.rssiRange(minRssi, maxRssi);
+ return builder.build();
+ }
+ };
+
+ /**
+ * Returns the filter set the local name field of Bluetooth advertisement data.
+ */
+ @Nullable
+ public String getLocalName() {
+ return mLocalName;
+ }
+
+ @Nullable /**
+ * Returns the filter set on the service uuid.
+ */
+ public ParcelUuid getServiceUuid() {
+ return mServiceUuid;
+ }
+
+ @Nullable
+ public ParcelUuid getServiceUuidMask() {
+ return mServiceUuidMask;
+ }
+
+ @Nullable
+ public String getDeviceAddress() {
+ return mMacAddress;
+ }
+
+ @Nullable
+ public byte[] getServiceData() {
+ return mServiceData;
+ }
+
+ @Nullable
+ public byte[] getServiceDataMask() {
+ return mServiceDataMask;
+ }
+
+ /**
+ * Returns the manufacturer id. -1 if the manufacturer filter is not set.
+ */
+ public int getManufacturerId() {
+ return mManufacturerId;
+ }
+
+ @Nullable
+ public byte[] getManufacturerData() {
+ return mManufacturerData;
+ }
+
+ @Nullable
+ public byte[] getManufacturerDataMask() {
+ return mManufacturerDataMask;
+ }
+
+ /**
+ * Returns minimum value of rssi for the scan filter. {@link Integer#MIN_VALUE} if not set.
+ */
+ public int getMinRssi() {
+ return mMinRssi;
+ }
+
+ /**
+ * Returns maximum value of the rssi for the scan filter. {@link Integer#MAX_VALUE} if not set.
+ */
+ public int getMaxRssi() {
+ return mMaxRssi;
+ }
+
+ /**
+ * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
+ * if it matches all the field filters.
+ */
+ public boolean matches(ScanResult scanResult) {
+ if (scanResult == null) {
+ return false;
+ }
+ BluetoothDevice device = scanResult.getDevice();
+ // Device match.
+ if (mMacAddress != null && (device == null || !mMacAddress.equals(device.getAddress()))) {
+ return false;
+ }
+
+ int rssi = scanResult.getRssi();
+ if (rssi < mMinRssi || rssi > mMaxRssi) {
+ return false;
+ }
+
+ byte[] scanRecordBytes = scanResult.getScanRecord();
+ ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes);
+
+ // Scan record is null but there exist filters on it.
+ if (scanRecord == null
+ && (mLocalName != null || mServiceUuid != null || mManufacturerData != null
+ || mServiceData != null)) {
+ return false;
+ }
+
+ // Local name match.
+ if (mLocalName != null && !mLocalName.equals(scanRecord.getLocalName())) {
+ return false;
+ }
+
+ // UUID match.
+ if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask,
+ scanRecord.getServiceUuids())) {
+ return false;
+ }
+
+ // Service data match
+ if (mServiceData != null &&
+ !matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData())) {
+ return false;
+ }
+
+ // Manufacturer data match.
+ if (mManufacturerData != null && !matchesPartialData(mManufacturerData,
+ mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) {
+ return false;
+ }
+ // All filters match.
+ return true;
+ }
+
+ // Check if the uuid pattern is contained in a list of parcel uuids.
+ private boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask,
+ List<ParcelUuid> uuids) {
+ if (uuid == null) {
+ return true;
+ }
+ if (uuids == null) {
+ return false;
+ }
+
+ for (ParcelUuid parcelUuid : uuids) {
+ UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
+ if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Check if the uuid pattern matches the particular service uuid.
+ private boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
+ if (mask == null) {
+ return uuid.equals(data);
+ }
+ if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) !=
+ (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) {
+ return false;
+ }
+ return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) ==
+ (data.getMostSignificantBits() & mask.getMostSignificantBits()));
+ }
+
+ // Check whether the data pattern matches the parsed data.
+ private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
+ if (dataMask == null) {
+ return Arrays.equals(data, parsedData);
+ }
+ if (parsedData == null) {
+ return false;
+ }
+ for (int i = 0; i < data.length; ++i) {
+ if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "BluetoothLeScanFilter [mLocalName=" + mLocalName + ", mMacAddress=" + mMacAddress
+ + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData="
+ + Arrays.toString(mServiceData) + ", mServiceDataMask="
+ + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
+ + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
+ + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask)
+ + ", mMinRssi=" + mMinRssi + ", mMaxRssi=" + mMaxRssi + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLocalName, mMacAddress, mManufacturerId, mManufacturerData,
+ mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask,
+ mServiceUuid, mServiceUuidMask);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj;
+ return Objects.equals(mLocalName, other.mLocalName) &&
+ Objects.equals(mMacAddress, other.mMacAddress) &&
+ mManufacturerId == other.mManufacturerId &&
+ Objects.deepEquals(mManufacturerData, other.mManufacturerData) &&
+ Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) &&
+ mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi &&
+ Objects.deepEquals(mServiceData, other.mServiceData) &&
+ Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) &&
+ Objects.equals(mServiceUuid, other.mServiceUuid) &&
+ Objects.equals(mServiceUuidMask, other.mServiceUuidMask);
+ }
+
+ /**
+ * Returns the {@link Builder} for {@link BluetoothLeScanFilter}.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for {@link BluetoothLeScanFilter}. Use
+ * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}.
+ */
+ public static class Builder {
+
+ private String mLocalName;
+ private String mMacAddress;
+
+ private ParcelUuid mServiceUuid;
+ private ParcelUuid mUuidMask;
+
+ private byte[] mServiceData;
+ private byte[] mServiceDataMask;
+
+ private int mManufacturerId = -1;
+ private byte[] mManufacturerData;
+ private byte[] mManufacturerDataMask;
+
+ private int mMinRssi = Integer.MIN_VALUE;
+ private int mMaxRssi = Integer.MAX_VALUE;
+
+ // Private constructor, use BluetoothLeScanFilter.newBuilder instead.
+ private Builder() {
+ }
+
+ /**
+ * Set filtering on local name.
+ */
+ public Builder name(String localName) {
+ mLocalName = localName;
+ return this;
+ }
+
+ /**
+ * Set filtering on device mac address.
+ *
+ * @param macAddress The device mac address for the filter. It needs to be in the format of
+ * "01:02:03:AB:CD:EF". The mac address can be validated using
+ * {@link BluetoothAdapter#checkBluetoothAddress}.
+ * @throws IllegalArgumentException If the {@code macAddress} is invalid.
+ */
+ public Builder macAddress(String macAddress) {
+ if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) {
+ throw new IllegalArgumentException("invalid mac address " + macAddress);
+ }
+ mMacAddress = macAddress;
+ return this;
+ }
+
+ /**
+ * Set filtering on service uuid.
+ */
+ public Builder serviceUuid(ParcelUuid serviceUuid) {
+ mServiceUuid = serviceUuid;
+ return this;
+ }
+
+ /**
+ * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set
+ * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate
+ * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit.
+ * <p>
+ * The length of {@code uuidMask} must be the same as {@code serviceUuid}.
+ */
+ public Builder serviceUuidMask(ParcelUuid uuidMask) {
+ mUuidMask = uuidMask;
+ return this;
+ }
+
+ /**
+ * Set service data filter.
+ */
+ public Builder serviceData(byte[] serviceData) {
+ mServiceData = serviceData;
+ return this;
+ }
+
+ /**
+ * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it
+ * needs to match the one in service data, otherwise set it to 0 to ignore that bit.
+ * <p>
+ * The {@code serviceDataMask} must have the same length of the {@code serviceData} set
+ * through {@link #serviceData(byte[])}.
+ */
+ public Builder serviceDataMask(byte[] serviceDataMask) {
+ mServiceDataMask = serviceDataMask;
+ return this;
+ }
+
+ /**
+ * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as
+ * invalid id.
+ * <p>
+ * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
+ */
+ public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) {
+ if (manufacturerData != null && manufacturerId < 0) {
+ throw new IllegalArgumentException("invalid manufacture id");
+ }
+ mManufacturerId = manufacturerId;
+ mManufacturerData = manufacturerData;
+ return this;
+ }
+
+ /**
+ * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it
+ * needs to match the one in manufacturer data, otherwise set it to 0.
+ * <p>
+ * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}
+ * set through {@link #manufacturerData(int, byte[])}.
+ */
+ public Builder manufacturerDataMask(byte[] manufacturerDataMask) {
+ mManufacturerDataMask = manufacturerDataMask;
+ return this;
+ }
+
+ /**
+ * Set the desired rssi range for the filter. A scan result with rssi in the range of
+ * [minRssi, maxRssi] will be consider as a match.
+ */
+ public Builder rssiRange(int minRssi, int maxRssi) {
+ mMinRssi = minRssi;
+ mMaxRssi = maxRssi;
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeScanFilter}.
+ *
+ * @throws IllegalArgumentException If the filter cannot be built.
+ */
+ public BluetoothLeScanFilter build() {
+ if (mUuidMask != null && mServiceUuid == null) {
+ throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
+ }
+
+ if (mServiceDataMask != null) {
+ if (mServiceData == null) {
+ throw new IllegalArgumentException(
+ "serviceData is null while serviceDataMask is not null");
+ }
+ // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
+ // byte array need to be the same.
+ if (mServiceData.length != mServiceDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for service data and service data mask");
+ }
+ }
+
+ if (mManufacturerDataMask != null) {
+ if (mManufacturerData == null) {
+ throw new IllegalArgumentException(
+ "manufacturerData is null while manufacturerDataMask is not null");
+ }
+ // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
+ // of the two byte array need to be the same.
+ if (mManufacturerData.length != mManufacturerDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for manufacturerData and manufacturerDataMask");
+ }
+ }
+ return new BluetoothLeScanFilter(mLocalName, mMacAddress,
+ mServiceUuid, mUuidMask,
+ mServiceData, mServiceDataMask,
+ mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.aidl b/core/java/android/bluetooth/BluetoothLeScanner.aidl
new file mode 100644
index 0000000..8cecdd7
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeScanner.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.bluetooth;
+
+parcelable BluetoothLeScanner.ScanResult;
+parcelable BluetoothLeScanner.Settings;
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.java b/core/java/android/bluetooth/BluetoothLeScanner.java
new file mode 100644
index 0000000..ed3188b
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeScanner.java
@@ -0,0 +1,759 @@
+/*
+ * 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.bluetooth;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class provides methods to perform scan related operations for Bluetooth LE devices. An
+ * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It
+ * can also request different types of callbacks for delivering the result.
+ * <p>
+ * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
+ * {@link BluetoothLeScanner}.
+ * <p>
+ * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see BluetoothLeScanFilter
+ */
+public class BluetoothLeScanner {
+
+ private static final String TAG = "BluetoothLeScanner";
+ private static final boolean DBG = true;
+
+ /**
+ * Settings for Bluetooth LE scan.
+ */
+ public static final class Settings implements Parcelable {
+ /**
+ * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes
+ * the least power.
+ */
+ public static final int SCAN_MODE_LOW_POWER = 0;
+ /**
+ * Perform Bluetooth LE scan in balanced power mode.
+ */
+ public static final int SCAN_MODE_BALANCED = 1;
+ /**
+ * Scan using highest duty cycle. It's recommended only using this mode when the application
+ * is running in foreground.
+ */
+ public static final int SCAN_MODE_LOW_LATENCY = 2;
+
+ /**
+ * Callback each time when a bluetooth advertisement is found.
+ */
+ public static final int CALLBACK_TYPE_ON_UPDATE = 0;
+ /**
+ * Callback when a bluetooth advertisement is found for the first time.
+ */
+ public static final int CALLBACK_TYPE_ON_FOUND = 1;
+ /**
+ * Callback when a bluetooth advertisement is found for the first time, then lost.
+ */
+ public static final int CALLBACK_TYPE_ON_LOST = 2;
+
+ /**
+ * Full scan result which contains device mac address, rssi, advertising and scan response
+ * and scan timestamp.
+ */
+ public static final int SCAN_RESULT_TYPE_FULL = 0;
+ /**
+ * Truncated scan result which contains device mac address, rssi and scan timestamp. Note
+ * it's possible for an app to get more scan results that it asks if there are multiple apps
+ * using this type. TODO: decide whether we could unhide this setting.
+ *
+ * @hide
+ */
+ public static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
+
+ // Bluetooth LE scan mode.
+ private int mScanMode;
+
+ // Bluetooth LE scan callback type
+ private int mCallbackType;
+
+ // Bluetooth LE scan result type
+ private int mScanResultType;
+
+ // Time of delay for reporting the scan result
+ private long mReportDelayMicros;
+
+ public int getScanMode() {
+ return mScanMode;
+ }
+
+ public int getCallbackType() {
+ return mCallbackType;
+ }
+
+ public int getScanResultType() {
+ return mScanResultType;
+ }
+
+ /**
+ * Returns report delay timestamp based on the device clock.
+ */
+ public long getReportDelayMicros() {
+ return mReportDelayMicros;
+ }
+
+ /**
+ * Creates a new {@link Builder} to build {@link Settings} object.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ private Settings(int scanMode, int callbackType, int scanResultType,
+ long reportDelayMicros) {
+ mScanMode = scanMode;
+ mCallbackType = callbackType;
+ mScanResultType = scanResultType;
+ mReportDelayMicros = reportDelayMicros;
+ }
+
+ private Settings(Parcel in) {
+ mScanMode = in.readInt();
+ mCallbackType = in.readInt();
+ mScanResultType = in.readInt();
+ mReportDelayMicros = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mScanMode);
+ dest.writeInt(mCallbackType);
+ dest.writeInt(mScanResultType);
+ dest.writeLong(mReportDelayMicros);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<Settings> CREATOR = new Creator<Settings>() {
+ @Override
+ public Settings[] newArray(int size) {
+ return new Settings[size];
+ }
+
+ @Override
+ public Settings createFromParcel(Parcel in) {
+ return new Settings(in);
+ }
+ };
+
+ /**
+ * Builder for {@link BluetoothLeScanner.Settings}.
+ */
+ public static class Builder {
+ private int mScanMode = SCAN_MODE_LOW_POWER;
+ private int mCallbackType = CALLBACK_TYPE_ON_UPDATE;
+ private int mScanResultType = SCAN_RESULT_TYPE_FULL;
+ private long mReportDelayMicros = 0;
+
+ // Hidden constructor.
+ private Builder() {
+ }
+
+ /**
+ * Set scan mode for Bluetooth LE scan.
+ *
+ * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER},
+ * {@link Settings#SCAN_MODE_BALANCED} or
+ * {@link Settings#SCAN_MODE_LOW_LATENCY}.
+ * @throws IllegalArgumentException If the {@code scanMode} is invalid.
+ */
+ public Builder scanMode(int scanMode) {
+ if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+ throw new IllegalArgumentException("invalid scan mode " + scanMode);
+ }
+ mScanMode = scanMode;
+ return this;
+ }
+
+ /**
+ * Set callback type for Bluetooth LE scan.
+ *
+ * @param callbackType The callback type for the scan. Can be either one of
+ * {@link Settings#CALLBACK_TYPE_ON_UPDATE},
+ * {@link Settings#CALLBACK_TYPE_ON_FOUND} or
+ * {@link Settings#CALLBACK_TYPE_ON_LOST}.
+ * @throws IllegalArgumentException If the {@code callbackType} is invalid.
+ */
+ public Builder callbackType(int callbackType) {
+ if (callbackType < CALLBACK_TYPE_ON_UPDATE
+ || callbackType > CALLBACK_TYPE_ON_LOST) {
+ throw new IllegalArgumentException("invalid callback type - " + callbackType);
+ }
+ mCallbackType = callbackType;
+ return this;
+ }
+
+ /**
+ * Set scan result type for Bluetooth LE scan.
+ *
+ * @param scanResultType Type for scan result, could be either
+ * {@link Settings#SCAN_RESULT_TYPE_FULL} or
+ * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}.
+ * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
+ * @hide
+ */
+ public Builder scanResultType(int scanResultType) {
+ if (scanResultType < SCAN_RESULT_TYPE_FULL
+ || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) {
+ throw new IllegalArgumentException(
+ "invalid scanResultType - " + scanResultType);
+ }
+ mScanResultType = scanResultType;
+ return this;
+ }
+
+ /**
+ * Set report delay timestamp for Bluetooth LE scan.
+ */
+ public Builder reportDelayMicros(long reportDelayMicros) {
+ mReportDelayMicros = reportDelayMicros;
+ return this;
+ }
+
+ /**
+ * Build {@link Settings}.
+ */
+ public Settings build() {
+ return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros);
+ }
+ }
+ }
+
+ /**
+ * ScanResult for Bluetooth LE scan.
+ */
+ public static final class ScanResult implements Parcelable {
+ // Remote bluetooth device.
+ private BluetoothDevice mDevice;
+
+ // Scan record, including advertising data and scan response data.
+ private byte[] mScanRecord;
+
+ // Received signal strength.
+ private int mRssi;
+
+ // Device timestamp when the result was last seen.
+ private long mTimestampMicros;
+
+ // Constructor of scan result.
+ public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) {
+ mDevice = device;
+ mScanRecord = scanRecord;
+ mRssi = rssi;
+ mTimestampMicros = timestampMicros;
+ }
+
+ private ScanResult(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mDevice != null) {
+ dest.writeInt(1);
+ mDevice.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mScanRecord != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(mScanRecord);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mRssi);
+ dest.writeLong(mTimestampMicros);
+ }
+
+ private void readFromParcel(Parcel in) {
+ if (in.readInt() == 1) {
+ mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() == 1) {
+ mScanRecord = in.createByteArray();
+ }
+ mRssi = in.readInt();
+ mTimestampMicros = in.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the remote bluetooth device identified by the bluetooth device address.
+ */
+ @Nullable
+ public BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ @Nullable /**
+ * Returns the scan record, which can be a combination of advertisement and scan response.
+ */
+ public byte[] getScanRecord() {
+ return mScanRecord;
+ }
+
+ /**
+ * Returns the received signal strength in dBm. The valid range is [-127, 127].
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ /**
+ * Returns timestamp since boot when the scan record was observed.
+ */
+ public long getTimestampMicros() {
+ return mTimestampMicros;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ScanResult other = (ScanResult) obj;
+ return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
+ Objects.deepEquals(mScanRecord, other.mScanRecord)
+ && (mTimestampMicros == other.mTimestampMicros);
+ }
+
+ @Override
+ public String toString() {
+ return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
+ + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros="
+ + mTimestampMicros + '}';
+ }
+
+ public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
+ @Override
+ public ScanResult createFromParcel(Parcel source) {
+ return new ScanResult(source);
+ }
+
+ @Override
+ public ScanResult[] newArray(int size) {
+ return new ScanResult[size];
+ }
+ };
+
+ }
+
+ /**
+ * Callback of Bluetooth LE scans. The results of the scans will be delivered through the
+ * callbacks.
+ */
+ public interface ScanCallback {
+ /**
+ * Callback when any BLE beacon is found.
+ *
+ * @param result A Bluetooth LE scan result.
+ */
+ public void onDeviceUpdate(ScanResult result);
+
+ /**
+ * Callback when the BLE beacon is found for the first time.
+ *
+ * @param result The Bluetooth LE scan result when the onFound event is triggered.
+ */
+ public void onDeviceFound(ScanResult result);
+
+ /**
+ * Callback when the BLE device was lost. Note a device has to be "found" before it's lost.
+ *
+ * @param device The Bluetooth device that is lost.
+ */
+ public void onDeviceLost(BluetoothDevice device);
+
+ /**
+ * Callback when batch results are delivered.
+ *
+ * @param results List of scan results that are previously scanned.
+ */
+ public void onBatchScanResults(List<ScanResult> results);
+
+ /**
+ * Fails to start scan as BLE scan with the same settings is already started by the app.
+ */
+ public static final int SCAN_ALREADY_STARTED = 1;
+ /**
+ * Fails to start scan as app cannot be registered.
+ */
+ public static final int APPLICATION_REGISTRATION_FAILED = 2;
+ /**
+ * Fails to start scan due to gatt service failure.
+ */
+ public static final int GATT_SERVICE_FAILURE = 3;
+ /**
+ * Fails to start scan due to controller failure.
+ */
+ public static final int CONTROLLER_FAILURE = 4;
+
+ /**
+ * Callback when scan failed.
+ */
+ public void onScanFailed(int errorCode);
+ }
+
+ private final IBluetoothGatt mBluetoothGatt;
+ private final Handler mHandler;
+ private final Map<Settings, BleScanCallbackWrapper> mLeScanClients;
+
+ BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
+ mBluetoothGatt = bluetoothGatt;
+ mHandler = new Handler(Looper.getMainLooper());
+ mLeScanClients = new HashMap<Settings, BleScanCallbackWrapper>();
+ }
+
+ /**
+ * Bluetooth GATT interface callbacks
+ */
+ private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
+
+ private final ScanCallback mScanCallback;
+ private final List<BluetoothLeScanFilter> mFilters;
+ private Settings mSettings;
+ private IBluetoothGatt mBluetoothGatt;
+
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // > 0: registered and scan started
+ private int mLeHandle;
+
+ public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
+ List<BluetoothLeScanFilter> filters, Settings settings, ScanCallback scanCallback) {
+ mBluetoothGatt = bluetoothGatt;
+ mFilters = filters;
+ mSettings = settings;
+ mScanCallback = scanCallback;
+ mLeHandle = 0;
+ }
+
+ public boolean scanStarted() {
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ return false;
+ }
+ try {
+ wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ }
+ return mLeHandle > 0;
+ }
+
+ public void stopLeScan() {
+ synchronized (this) {
+ if (mLeHandle <= 0) {
+ Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+ return;
+ }
+ try {
+ mBluetoothGatt.stopScan(mLeHandle, false);
+ mBluetoothGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop scan and unregister" + e);
+ }
+ mLeHandle = -1;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Application interface registered - app is ready to go
+ */
+ @Override
+ public void onClientRegistered(int status, int clientIf) {
+ Log.d(TAG, "onClientRegistered() - status=" + status +
+ " clientIf=" + clientIf);
+
+ synchronized (this) {
+ if (mLeHandle == -1) {
+ if (DBG)
+ Log.d(TAG, "onClientRegistered LE scan canceled");
+ }
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ try {
+ mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le scan: " + e);
+ mLeHandle = -1;
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ }
+ notifyAll();
+ }
+ }
+
+ @Override
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ *
+ * @hide
+ */
+ @Override
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG)
+ Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi);
+
+ // Check null in case the scan has been stopped
+ synchronized (this) {
+ if (mLeHandle <= 0)
+ return;
+ }
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ address);
+ long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos());
+ ScanResult result = new ScanResult(device, advData, rssi,
+ scanMicros);
+ mScanCallback.onDeviceUpdate(result);
+ }
+
+ @Override
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
+
+ @Override
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
+
+ @Override
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descUuid) {
+ // no op
+ }
+
+ @Override
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ @Override
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
+
+ @Override
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int descInstId, ParcelUuid descrUuid) {
+ // no op
+ }
+
+ @Override
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
+
+ @Override
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+
+ @Override
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ // no op
+ }
+
+ @Override
+ public void onMultiAdvertiseCallback(int status) {
+ // no op
+ }
+
+ @Override
+ public void onConfigureMTU(String address, int mtu, int status) {
+ // no op
+ }
+ }
+
+ /**
+ * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+ *
+ * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices.
+ * @param settings Settings for ble scan.
+ * @param callback Callback when scan results are delivered.
+ * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+ */
+ public void startScan(List<BluetoothLeScanFilter> filters, Settings settings,
+ final ScanCallback callback) {
+ if (settings == null || callback == null) {
+ throw new IllegalArgumentException("settings or callback is null");
+ }
+ synchronized (mLeScanClients) {
+ if (mLeScanClients.get(settings) != null) {
+ postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED);
+ return;
+ }
+ BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
+ settings, callback);
+ try {
+ UUID uuid = UUID.randomUUID();
+ mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.scanStarted()) {
+ mLeScanClients.put(settings, wrapper);
+ } else {
+ postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "GATT service exception when starting scan", e);
+ postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE);
+ }
+ }
+ }
+
+ private void postCallbackError(final ScanCallback callback, final int errorCode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onScanFailed(errorCode);
+ }
+ });
+ }
+
+ /**
+ * Stop Bluetooth LE scan.
+ *
+ * @param settings The same settings as used in {@link #startScan}, which is used to identify
+ * the BLE scan.
+ */
+ public void stopScan(Settings settings) {
+ synchronized (mLeScanClients) {
+ BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings);
+ if (wrapper == null) {
+ return;
+ }
+ wrapper.stopLeScan();
+ }
+ }
+
+ /**
+ * Returns available storage size for batch scan results. It's recommended not to use batch scan
+ * if available storage size is small (less than 1k bytes, for instance).
+ *
+ * @hide TODO: unhide when batching is supported in stack.
+ */
+ public int getAvailableBatchStorageSizeBytes() {
+ throw new UnsupportedOperationException("not impelemented");
+ }
+
+ /**
+ * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
+ * batched on bluetooth controller.
+ *
+ * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
+ * used to start scan.
+ * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
+ * get batch scan callback if the batch scan buffer is flushed.
+ * @return Batch Scan results.
+ * @hide TODO: unhide when batching is supported in stack.
+ */
+ public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
+ throw new UnsupportedOperationException("not impelemented");
+ }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index ab53fb0..1e22eb3 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -273,11 +273,29 @@ public final class BluetoothUuid {
* @param parcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
*/
- public static boolean isShortUuid(ParcelUuid parcelUuid) {
+ public static boolean is16BitUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
return false;
}
return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
}
+
+
+ /**
+ * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
+ *
+ * @param parcelUuid
+ * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
+ */
+ public static boolean is32BitUuid(ParcelUuid parcelUuid) {
+ UUID uuid = parcelUuid.getUuid();
+ if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
+ return false;
+ }
+ if (is16BitUuid(parcelUuid)) {
+ return false;
+ }
+ return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
+ }
}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 3dd7094..091b806 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -17,6 +17,10 @@
package android.bluetooth;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAdvertiseScanData;
+import android.bluetooth.BluetoothLeAdvertiser;
+import android.bluetooth.BluetoothLeScanFilter;
+import android.bluetooth.BluetoothLeScanner;
import android.os.ParcelUuid;
import android.bluetooth.IBluetoothGattCallback;
@@ -33,8 +37,13 @@ interface IBluetoothGatt {
void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids);
void startScanWithUuidsScanParam(in int appIf, in boolean isServer,
in ParcelUuid[] ids, int scanWindow, int scanInterval);
+ void startScanWithFilters(in int appIf, in boolean isServer,
+ in BluetoothLeScanner.Settings settings,
+ in List<BluetoothLeScanFilter> filters);
void stopScan(in int appIf, in boolean isServer);
-
+ void startMultiAdvertising(in int appIf, in BluetoothLeAdvertiseScanData.AdvertisementData data,
+ in BluetoothLeAdvertiser.Settings settings);
+ void stopMultiAdvertising(in int appIf);
void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
void unregisterClient(in int clientIf);
void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index a78c29b..bf9e0a7 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -64,5 +64,6 @@ interface IBluetoothGattCallback {
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
oneway void onAdvertiseStateChange(in int advertiseState, in int status);
+ oneway void onMultiAdvertiseCallback(in int status);
void onConfigureMTU(in String address, in int mtu, in int status);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0e2eab7..35bcc02 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1405,6 +1405,14 @@ public abstract class PackageManager {
public static final String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device has a full implementation of the android.webkit.* APIs. Devices
+ * lacking this feature will not have a functioning WebView implementation.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WEBVIEW = "android.software.webview";
+
+ /**
* Action to external storage service to clean out removed apps.
* @hide
*/
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 5674154..3f01dd2 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -64,7 +64,6 @@ import java.util.Arrays;
* List Resource</a>.</p>
*/
public class ColorStateList implements Parcelable {
-
private int[][] mStateSpecs; // must be parallel to mColors
private int[] mColors; // must be parallel to mStateSpecs
private int mDefaultColor = 0xffff0000;
@@ -100,9 +99,9 @@ public class ColorStateList implements Parcelable {
public static ColorStateList valueOf(int color) {
// TODO: should we collect these eventually?
synchronized (sCache) {
- WeakReference<ColorStateList> ref = sCache.get(color);
- ColorStateList csl = ref != null ? ref.get() : null;
+ final WeakReference<ColorStateList> ref = sCache.get(color);
+ ColorStateList csl = ref != null ? ref.get() : null;
if (csl != null) {
return csl;
}
@@ -118,8 +117,7 @@ public class ColorStateList implements Parcelable {
*/
public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
@@ -133,22 +131,22 @@ public class ColorStateList implements Parcelable {
return createFromXmlInner(r, parser, attrs);
}
- /* Create from inside an XML document. Called on a parser positioned at
- * a tag in an XML document, tries to create a ColorStateList from that tag.
- * Returns null if the tag is not a valid ColorStateList.
+ /**
+ * Create from inside an XML document. Called on a parser positioned at a
+ * tag in an XML document, tries to create a ColorStateList from that tag.
+ *
+ * @throws XmlPullParserException if the current tag is not &lt;selector>
+ * @return A color state list for the current tag.
*/
private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser,
AttributeSet attrs) throws XmlPullParserException, IOException {
-
- ColorStateList colorStateList;
-
+ final ColorStateList colorStateList;
final String name = parser.getName();
-
if (name.equals("selector")) {
colorStateList = new ColorStateList();
} else {
throw new XmlPullParserException(
- parser.getPositionDescription() + ": invalid drawable tag " + name);
+ parser.getPositionDescription() + ": invalid drawable tag " + name);
}
colorStateList.inflate(r, parser, attrs);
@@ -161,9 +159,8 @@ public class ColorStateList implements Parcelable {
* (0-255).
*/
public ColorStateList withAlpha(int alpha) {
- int[] colors = new int[mColors.length];
-
- int len = colors.length;
+ final int[] colors = new int[mColors.length];
+ final int len = colors.length;
for (int i = 0; i < len; i++) {
colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24);
}
@@ -176,7 +173,6 @@ public class ColorStateList implements Parcelable {
*/
private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
-
int type;
final int innerDepth = parser.getDepth()+1;
@@ -259,10 +255,25 @@ public class ColorStateList implements Parcelable {
System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
}
+ /**
+ * Indicates whether this color state list contains more than one state spec
+ * and will change color based on state.
+ *
+ * @return True if this color state list changes color based on state, false
+ * otherwise.
+ * @see #getColorForState(int[], int)
+ */
public boolean isStateful() {
return mStateSpecs.length > 1;
}
+ /**
+ * Indicates whether this color state list is opaque, which means that every
+ * color returned from {@link #getColorForState(int[], int)} has an alpha
+ * value of 255.
+ *
+ * @return True if this color state list is opaque.
+ */
public boolean isOpaque() {
final int n = mColors.length;
for (int i = 0; i < n; i++) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 1692a79..a78f8e2 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -21,6 +21,7 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
@@ -719,12 +720,12 @@ public class Resources {
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @param theme The theme used to style the drawable attributes.
+ * @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*/
- public Drawable getDrawable(int id, Theme theme) throws NotFoundException {
+ public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
@@ -777,12 +778,12 @@ public class Resources {
* The value 0 is an invalid identifier.
* @param density The desired screen density indicated by the resource as
* found in {@link DisplayMetrics}.
- * @param theme The theme used to style the drawable attributes.
+ * @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*/
- public Drawable getDrawableForDensity(int id, int density, Theme theme) {
+ public Drawable getDrawableForDensity(int id, int density, @Nullable Theme theme) {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
diff --git a/core/java/android/hardware/usb/UsbConfiguration.java b/core/java/android/hardware/usb/UsbConfiguration.java
index 92d6f75..da5c128 100644
--- a/core/java/android/hardware/usb/UsbConfiguration.java
+++ b/core/java/android/hardware/usb/UsbConfiguration.java
@@ -44,13 +44,13 @@ public class UsbConfiguration implements Parcelable {
* Mask for "self-powered" bit in the configuration's attributes.
* @see #getAttributes
*/
- public static final int ATTR_SELF_POWERED_MASK = 1 << 6;
+ private static final int ATTR_SELF_POWERED = 1 << 6;
/**
* Mask for "remote wakeup" bit in the configuration's attributes.
* @see #getAttributes
*/
- public static final int ATTR_REMOTE_WAKEUP_MASK = 1 << 5;
+ private static final int ATTR_REMOTE_WAKEUP = 1 << 5;
/**
* UsbConfiguration should only be instantiated by UsbService implementation
@@ -83,19 +83,23 @@ public class UsbConfiguration implements Parcelable {
}
/**
- * Returns the configuration's attributes field.
- * This field contains a bit field with the following flags:
+ * Returns the self-powered attribute value configuration's attributes field.
+ * This attribute indicates that the device has a power source other than the USB connection.
*
- * Bit 7: always set to 1
- * Bit 6: self-powered
- * Bit 5: remote wakeup enabled
- * Bit 0-4: reserved
- * @see #ATTR_SELF_POWERED_MASK
- * @see #ATTR_REMOTE_WAKEUP_MASK
- * @return the configuration's attributes
+ * @return the configuration's self-powered attribute
*/
- public int getAttributes() {
- return mAttributes;
+ public boolean isSelfPowered() {
+ return (mAttributes & ATTR_SELF_POWERED) != 0;
+ }
+
+ /**
+ * Returns the remote-wakeup attribute value configuration's attributes field.
+ * This attributes that the device may signal the host to wake from suspend.
+ *
+ * @return the configuration's remote-wakeup attribute
+ */
+ public boolean isRemoteWakeup() {
+ return (mAttributes & ATTR_REMOTE_WAKEUP) != 0;
}
/**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 6283951..c062b3a 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -104,7 +104,7 @@ public class UsbDeviceConnection {
* Sets the current {@link android.hardware.usb.UsbInterface}.
* Used to select between two interfaces with the same ID but different alternate setting.
*
- * @return true if the interface was successfully released
+ * @return true if the interface was successfully selected
*/
public boolean setInterface(UsbInterface intf) {
return native_set_interface(intf.getId(), intf.getAlternateSetting());
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1837335..80a9598 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1655,9 +1655,16 @@ public class ConnectivityManager {
}
/** {@hide} */
- public void registerNetworkFactory(Messenger messenger) {
+ public void registerNetworkFactory(Messenger messenger, String name) {
try {
- mService.registerNetworkFactory(messenger);
+ mService.registerNetworkFactory(messenger, name);
+ } catch (RemoteException e) { }
+ }
+
+ /** {@hide} */
+ public void unregisterNetworkFactory(Messenger messenger) {
+ try {
+ mService.unregisterNetworkFactory(messenger);
} catch (RemoteException e) { }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 885b8b6..d97b1e9 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -153,7 +153,9 @@ interface IConnectivityManager
void setAirplaneMode(boolean enable);
- void registerNetworkFactory(in Messenger messenger);
+ void registerNetworkFactory(in Messenger messenger, in String name);
+
+ void unregisterNetworkFactory(in Messenger messenger);
void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
in NetworkCapabilities nc, int score);
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index ceedd98..7ea6bae 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -155,9 +155,6 @@ public class ProxyInfo implements Parcelable {
mHost = source.getHost();
mPort = source.getPort();
mPacFileUrl = source.mPacFileUrl;
- if (mPacFileUrl == null) {
- mPacFileUrl = Uri.EMPTY;
- }
mExclusionList = source.getExclusionListAsString();
mParsedExclusionList = source.mParsedExclusionList;
} else {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 312cdbe..ee219e3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -266,6 +266,17 @@ public class UserManager {
*/
public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
+ /**
+ * Key for user restrictions. Specifies that the user is not allowed to send or receive
+ * phone calls or text messages. Emergency calls may still be permitted.
+ * The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_TELEPHONY = "no_telephony";
+
/** @hide */
public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
/** @hide */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e896063..2d03e1d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1088,6 +1088,9 @@ public final class Settings {
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_COUNT);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_DELAY_MS);
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS);
+
+ // At one time in System, then Global, but now back in Secure
+ MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS);
}
private static final HashSet<String> MOVED_TO_GLOBAL;
@@ -1102,7 +1105,6 @@ public final class Settings {
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.BLUETOOTH_ON);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DATA_ROAMING);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DEVICE_PROVISIONED);
- MOVED_TO_SECURE_THEN_GLOBAL.add(Global.INSTALL_NON_MARKET_APPS);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.HTTP_PROXY);
@@ -2573,10 +2575,10 @@ public final class Settings {
public static final String HTTP_PROXY = Global.HTTP_PROXY;
/**
- * @deprecated Use {@link android.provider.Settings.Global#INSTALL_NON_MARKET_APPS} instead
+ * @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead
*/
@Deprecated
- public static final String INSTALL_NON_MARKET_APPS = Global.INSTALL_NON_MARKET_APPS;
+ public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
/**
* @deprecated Use {@link android.provider.Settings.Secure#LOCATION_PROVIDERS_ALLOWED}
@@ -2814,7 +2816,6 @@ public final class Settings {
MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_SIZE_FORCED);
MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
- MOVED_TO_GLOBAL.add(Settings.Global.INSTALL_NON_MARKET_APPS);
MOVED_TO_GLOBAL.add(Settings.Global.MOBILE_DATA);
MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_BUCKET_DURATION);
MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_DELETE_AGE);
@@ -3404,10 +3405,13 @@ public final class Settings {
public static final String HTTP_PROXY = Global.HTTP_PROXY;
/**
- * @deprecated Use {@link android.provider.Settings.Global#INSTALL_NON_MARKET_APPS} instead
+ * Whether applications can be installed for this user via the system's
+ * {@link Intent#ACTION_INSTALL_PACKAGE} mechanism.
+ *
+ * <p>1 = permit app installation via the system package installer intent
+ * <p>0 = do not allow use of the package installer
*/
- @Deprecated
- public static final String INSTALL_NON_MARKET_APPS = Global.INSTALL_NON_MARKET_APPS;
+ public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
/**
* Comma-separated list of location providers that activities may access. Do not rely on
@@ -5066,13 +5070,10 @@ public final class Settings {
"download_manager_recommended_max_bytes_over_mobile";
/**
- * Whether the package installer should allow installation of apps downloaded from
- * sources other than Google Play.
- *
- * 1 = allow installing from other sources
- * 0 = only allow installing from Google Play
+ * @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead
*/
- public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ @Deprecated
+ public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
/**
* Whether mobile data connections are allowed by the user. See
@@ -6238,6 +6239,13 @@ public final class Settings {
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL);
+ // Certain settings have been moved from global to the per-user secure namespace
+ private static final HashSet<String> MOVED_TO_SECURE;
+ static {
+ MOVED_TO_SECURE = new HashSet<String>(1);
+ MOVED_TO_SECURE.add(Settings.Global.INSTALL_NON_MARKET_APPS);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -6251,6 +6259,11 @@ public final class Settings {
/** @hide */
public static String getStringForUser(ContentResolver resolver, String name,
int userHandle) {
+ if (MOVED_TO_SECURE.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
+ + " to android.provider.Settings.Secure, returning read-only value.");
+ return Secure.getStringForUser(resolver, name, userHandle);
+ }
return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
@@ -6273,6 +6286,12 @@ public final class Settings {
Log.v(TAG, "Global.putString(name=" + name + ", value=" + value
+ " for " + userHandle);
}
+ // Global and Secure have the same access policy so we can forward writes
+ if (MOVED_TO_SECURE.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
+ + " to android.provider.Settings.Secure, value is unchanged.");
+ return Secure.putStringForUser(resolver, name, value, userHandle);
+ }
return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d4919eb..b3705d8 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -17,15 +17,15 @@
package android.service.notification;
import android.service.notification.StatusBarNotification;
-import android.service.notification.NotificationOrderUpdate;
+import android.service.notification.NotificationRankingUpdate;
/** @hide */
oneway interface INotificationListener
{
- void onListenerConnected(in NotificationOrderUpdate update);
+ void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in StatusBarNotification notification,
- in NotificationOrderUpdate update);
+ in NotificationRankingUpdate update);
void onNotificationRemoved(in StatusBarNotification notification,
- in NotificationOrderUpdate update);
- void onNotificationOrderUpdate(in NotificationOrderUpdate update);
+ in NotificationRankingUpdate update);
+ void onNotificationRankingUpdate(in NotificationRankingUpdate update);
} \ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index e2e9ff4..7f84877 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -24,12 +24,15 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
/**
- * A service that receives calls from the system when new notifications are posted or removed.
+ * A service that receives calls from the system when new notifications are
+ * posted or removed, or their ranking changed.
* <p>To extend this class, you must declare the service in your manifest file with
* the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
@@ -48,7 +51,7 @@ public abstract class NotificationListenerService extends Service {
+ "[" + getClass().getSimpleName() + "]";
private INotificationListenerWrapper mWrapper = null;
- private String[] mNotificationKeys;
+ private Ranking mRanking;
private INotificationManager mNoMan;
@@ -102,11 +105,11 @@ public abstract class NotificationListenerService extends Service {
}
/**
- * Implement this method to be notified when the notification order cahnges.
- *
- * Call {@link #getOrderedNotificationKeys()} to retrieve the new order.
+ * Implement this method to be notified when the notification ranking changes.
+ * <P>
+ * Call {@link #getCurrentRanking()} to retrieve the new ranking.
*/
- public void onNotificationOrderUpdate() {
+ public void onNotificationRankingUpdate() {
// optional
}
@@ -224,6 +227,19 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Request the list of notification keys in their current ranking order.
+ * <p>
+ * You can use the notification keys for subsequent retrieval via
+ * {@link #getActiveNotifications(String[]) or dismissal via
+ * {@link #cancelNotifications(String[]).
+ *
+ * @return An array of active notification keys, in their ranking order.
+ */
+ public String[] getActiveNotificationKeys() {
+ return mRanking.getOrderedKeys();
+ }
+
+ /**
* Request the list of outstanding notifications (that is, those that are visible to the
* current user). Useful when you don't know what's already been posted.
*
@@ -242,15 +258,20 @@ public abstract class NotificationListenerService extends Service {
}
/**
- * Request the list of notification keys in their current natural order.
- * You can use the notification keys for subsequent retrieval via
- * {@link #getActiveNotifications(String[]) or dismissal via
- * {@link #cancelNotifications(String[]).
+ * Returns current ranking information.
+ *
+ * <p>
+ * The returned object represents the current ranking snapshot and only
+ * applies for currently active notifications. Hence you must retrieve a
+ * new Ranking after each notification event such as
+ * {@link #onNotificationPosted(StatusBarNotification)},
+ * {@link #onNotificationRemoved(StatusBarNotification)}, etc.
*
- * @return An array of active notification keys, in their natural order.
+ * @return A {@link NotificationListenerService.Ranking} object providing
+ * access to ranking information
*/
- public String[] getOrderedNotificationKeys() {
- return mNotificationKeys;
+ public Ranking getCurrentRanking() {
+ return mRanking;
}
@Override
@@ -308,59 +329,163 @@ public abstract class NotificationListenerService extends Service {
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(StatusBarNotification sbn,
- NotificationOrderUpdate update) {
- try {
- // protect subclass from concurrent modifications of (@link mNotificationKeys}.
- synchronized (mWrapper) {
- updateNotificationKeys(update);
+ NotificationRankingUpdate update) {
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ applyUpdate(update);
+ try {
NotificationListenerService.this.onNotificationPosted(sbn);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationPosted", t);
}
- } catch (Throwable t) {
- Log.w(TAG, "Error running onOrderedNotificationPosted", t);
}
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn,
- NotificationOrderUpdate update) {
- try {
- // protect subclass from concurrent modifications of (@link mNotificationKeys}.
- synchronized (mWrapper) {
- updateNotificationKeys(update);
+ NotificationRankingUpdate update) {
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ applyUpdate(update);
+ try {
NotificationListenerService.this.onNotificationRemoved(sbn);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationRemoved", t);
}
- } catch (Throwable t) {
- Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
@Override
- public void onListenerConnected(NotificationOrderUpdate update) {
- try {
- // protect subclass from concurrent modifications of (@link mNotificationKeys}.
- synchronized (mWrapper) {
- updateNotificationKeys(update);
- NotificationListenerService.this.onListenerConnected(mNotificationKeys);
+ public void onListenerConnected(NotificationRankingUpdate update) {
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ applyUpdate(update);
+ try {
+ NotificationListenerService.this.onListenerConnected(
+ mRanking.getOrderedKeys());
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onListenerConnected", t);
}
- } catch (Throwable t) {
- Log.w(TAG, "Error running onListenerConnected", t);
}
}
@Override
- public void onNotificationOrderUpdate(NotificationOrderUpdate update)
+ public void onNotificationRankingUpdate(NotificationRankingUpdate update)
throws RemoteException {
- try {
- // protect subclass from concurrent modifications of (@link mNotificationKeys}.
- synchronized (mWrapper) {
- updateNotificationKeys(update);
- NotificationListenerService.this.onNotificationOrderUpdate();
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ applyUpdate(update);
+ try {
+ NotificationListenerService.this.onNotificationRankingUpdate();
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationRankingUpdate", t);
}
- } catch (Throwable t) {
- Log.w(TAG, "Error running onNotificationOrderUpdate", t);
}
}
}
- private void updateNotificationKeys(NotificationOrderUpdate update) {
- // TODO: avoid garbage by comparing the lists
- mNotificationKeys = update.getOrderedKeys();
+ private void applyUpdate(NotificationRankingUpdate update) {
+ mRanking = new Ranking(update);
+ }
+
+ /**
+ * Provides access to ranking information on currently active
+ * notifications.
+ *
+ * <p>
+ * Note that this object represents a ranking snapshot that only applies to
+ * notifications active at the time of retrieval.
+ */
+ public static class Ranking implements Parcelable {
+ private final NotificationRankingUpdate mRankingUpdate;
+
+ private Ranking(NotificationRankingUpdate rankingUpdate) {
+ mRankingUpdate = rankingUpdate;
+ }
+
+ /**
+ * Request the list of notification keys in their current ranking
+ * order.
+ *
+ * @return An array of active notification keys, in their ranking order.
+ */
+ public String[] getOrderedKeys() {
+ return mRankingUpdate.getOrderedKeys();
+ }
+
+ /**
+ * Returns the rank of the notification with the given key, that is the
+ * index of <code>key</code> in the array of keys returned by
+ * {@link #getOrderedKeys()}.
+ *
+ * @return The rank of the notification with the given key; -1 when the
+ * given key is unknown.
+ */
+ public int getIndexOfKey(String key) {
+ // TODO: Optimize.
+ String[] orderedKeys = mRankingUpdate.getOrderedKeys();
+ for (int i = 0; i < orderedKeys.length; i++) {
+ if (orderedKeys[i].equals(key)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns whether the notification with the given key was intercepted
+ * by &quot;Do not disturb&quot;.
+ */
+ public boolean isInterceptedByDoNotDisturb(String key) {
+ // TODO: Optimize.
+ for (String interceptedKey : mRankingUpdate.getDndInterceptedKeys()) {
+ if (interceptedKey.equals(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the notification with the given key is an ambient
+ * notification, that is a notification that doesn't require the user's
+ * immediate attention.
+ */
+ public boolean isAmbient(String key) {
+ // TODO: Optimize.
+ int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex();
+ if (firstAmbientIndex < 0) {
+ return false;
+ }
+ String[] orderedKeys = mRankingUpdate.getOrderedKeys();
+ for (int i = firstAmbientIndex; i < orderedKeys.length; i++) {
+ if (orderedKeys[i].equals(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // ----------- Parcelable
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mRankingUpdate, flags);
+ }
+
+ public static final Creator<Ranking> CREATOR = new Creator<Ranking>() {
+ @Override
+ public Ranking createFromParcel(Parcel source) {
+ NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
+ return new Ranking(rankingUpdate);
+ }
+
+ @Override
+ public Ranking[] newArray(int size) {
+ return new Ranking[size];
+ }
+ };
}
}
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java
deleted file mode 100644
index 20e19a3..0000000
--- a/core/java/android/service/notification/NotificationOrderUpdate.java
+++ /dev/null
@@ -1,62 +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.service.notification;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class NotificationOrderUpdate implements Parcelable {
- // TODO replace this with an update instead of the whole array
- private final String[] mKeys;
-
- /** @hide */
- public NotificationOrderUpdate(String[] keys) {
- this.mKeys = keys;
- }
-
- public NotificationOrderUpdate(Parcel in) {
- this.mKeys = in.readStringArray();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeStringArray(this.mKeys);
- }
-
- public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR
- = new Parcelable.Creator<NotificationOrderUpdate>() {
- public NotificationOrderUpdate createFromParcel(Parcel parcel) {
- return new NotificationOrderUpdate(parcel);
- }
-
- public NotificationOrderUpdate[] newArray(int size) {
- return new NotificationOrderUpdate[size];
- }
- };
-
- /**
- * @hide
- * @return ordered list of keys
- */
- String[] getOrderedKeys() {
- return mKeys;
- }
-}
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationRankingUpdate.aidl
index 5d50641..1393cb9 100644
--- a/core/java/android/service/notification/NotificationOrderUpdate.aidl
+++ b/core/java/android/service/notification/NotificationRankingUpdate.aidl
@@ -16,4 +16,4 @@
package android.service.notification;
-parcelable NotificationOrderUpdate;
+parcelable NotificationRankingUpdate;
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
new file mode 100644
index 0000000..4b13d95
--- /dev/null
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -0,0 +1,77 @@
+/*
+ * 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.service.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class NotificationRankingUpdate implements Parcelable {
+ // TODO: Support incremental updates.
+ private final String[] mKeys;
+ private final String[] mDndInterceptedKeys;
+ private final int mFirstAmbientIndex;
+
+ public NotificationRankingUpdate(String[] keys, String[] dndInterceptedKeys,
+ int firstAmbientIndex) {
+ mKeys = keys;
+ mFirstAmbientIndex = firstAmbientIndex;
+ mDndInterceptedKeys = dndInterceptedKeys;
+ }
+
+ public NotificationRankingUpdate(Parcel in) {
+ mKeys = in.readStringArray();
+ mFirstAmbientIndex = in.readInt();
+ mDndInterceptedKeys = in.readStringArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStringArray(mKeys);
+ out.writeInt(mFirstAmbientIndex);
+ out.writeStringArray(mDndInterceptedKeys);
+ }
+
+ public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
+ = new Parcelable.Creator<NotificationRankingUpdate>() {
+ public NotificationRankingUpdate createFromParcel(Parcel parcel) {
+ return new NotificationRankingUpdate(parcel);
+ }
+
+ public NotificationRankingUpdate[] newArray(int size) {
+ return new NotificationRankingUpdate[size];
+ }
+ };
+
+ public String[] getOrderedKeys() {
+ return mKeys;
+ }
+
+ public int getFirstAmbientIndex() {
+ return mFirstAmbientIndex;
+ }
+
+ public String[] getDndInterceptedKeys() {
+ return mDndInterceptedKeys;
+ }
+}
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index 0bd46bc..b17f502 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -115,7 +115,7 @@ public class QwertyKeyListener extends BaseKeyListener {
if (count > 0 && selStart == selEnd && selStart > 0) {
char c = content.charAt(selStart - 1);
- if (c == i || c == Character.toUpperCase(i) && view != null) {
+ if ((c == i || c == Character.toUpperCase(i)) && view != null) {
if (showCharacterPicker(view, content, c, false, count)) {
resetMetaState(content);
return true;
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 7721575..59b6386 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -149,6 +149,7 @@ public class TvView extends SurfaceView {
if (mSession != null) {
release();
}
+ mSessionCallback = null;
}
/**
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index ae5f37e..358ae8a 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -234,6 +234,14 @@ public final class InputDevice implements Parcelable {
public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
/**
+ * The input source is a device connected through HDMI-based bus.
+ *
+ * The key comes in through HDMI-CEC or MHL signal line, and is treated as if it were
+ * generated by a locally connected DPAD or keyboard.
+ */
+ public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
+
+ /**
* A special input source constant that is used when filtering input devices
* to match devices that provide any type of input source.
*/
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index b8e1b89..8a996d2 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -644,14 +644,26 @@ public class KeyEvent extends InputEvent implements Parcelable {
* devices or game controllers, especially if no other input mode is
* available. */
public static final int KEYCODE_PAIRING = 225;
-
- private static final int LAST_KEYCODE = KEYCODE_PAIRING;
+ /** Key code constant: Media Top Menu key.
+ * Goes to the top of media menu. */
+ public static final int KEYCODE_MEDIA_TOP_MENU = 226;
+ /** Key code constant: '11' key. */
+ public static final int KEYCODE_11 = 227;
+ /** Key code constant: '12' key. */
+ public static final int KEYCODE_12 = 228;
+ /** Key code constant: Last Channel key.
+ * Goes to the last viewed channel. */
+ public static final int KEYCODE_LAST_CHANNEL = 229;
+ /** Key code constant: TV data service key.
+ * Displays data services like weather, sports. */
+ public static final int KEYCODE_TV_DATA_SERVICE = 230;
+
+ private static final int LAST_KEYCODE = KEYCODE_TV_DATA_SERVICE;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// frameworks/native/include/android/keycodes.h
- // frameworks/base/include/androidfw/InputEventAttributes.h
- // external/webkit/WebKit/android/plugins/ANPKeyCodes.h
+ // frameworks/native/include/input/InputEventLabels.h
// frameworks/base/core/res/res/values/attrs.xml
// emulator?
// LAST_KEYCODE
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c1a4fee..e918119 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -48,6 +48,8 @@ public final class RenderNodeAnimator extends Animator {
public static final int Y = 9;
public static final int Z = 10;
public static final int ALPHA = 11;
+ // The last value in the enum, used for array size initialization
+ public static final int LAST_VALUE = ALPHA;
// Keep in sync with enum PaintFields in Animator.h
public static final int PAINT_STROKE_WIDTH = 0;
@@ -86,7 +88,7 @@ public final class RenderNodeAnimator extends Animator {
private boolean mStarted = false;
private boolean mFinished = false;
- public int mapViewPropertyToRenderProperty(int viewProperty) {
+ public static int mapViewPropertyToRenderProperty(int viewProperty) {
return sViewPropertyAnimatorMap.get(viewProperty);
}
@@ -125,11 +127,15 @@ public final class RenderNodeAnimator extends Animator {
}
}
+ static boolean isNativeInterpolator(TimeInterpolator interpolator) {
+ return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
+ }
+
private void applyInterpolator() {
if (mInterpolator == null) return;
long ni;
- if (mInterpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class)) {
+ if (isNativeInterpolator(mInterpolator)) {
ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
} else {
long duration = nGetDuration(mNativePtr.get());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fb7d57d..6dc7286 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4789,25 +4789,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param v previous or the next focus holder, or null if none
*/
private void manageFocusHotspot(boolean focused, View v) {
- if (mBackground != null && mBackground.supportsHotspots()) {
- final Rect r = new Rect();
- if (!focused && v != null) {
- v.getBoundsOnScreen(r);
- final int[] location = new int[2];
- getLocationOnScreen(location);
- r.offset(-location[0], -location[1]);
- } else {
- r.set(0, 0, mRight - mLeft, mBottom - mTop);
- }
-
- final float x = r.exactCenterX();
- final float y = r.exactCenterY();
- mBackground.setHotspot(R.attr.state_focused, x, y);
+ if (mBackground == null) {
+ return;
+ }
- if (!focused) {
- mBackground.removeHotspot(R.attr.state_focused);
- }
+ final Rect r = new Rect();
+ if (!focused && v != null) {
+ v.getBoundsOnScreen(r);
+ final int[] location = new int[2];
+ getLocationOnScreen(location);
+ r.offset(-location[0], -location[1]);
+ } else {
+ r.set(0, 0, mRight - mLeft, mBottom - mTop);
}
+
+ final float x = r.exactCenterX();
+ final float y = r.exactCenterY();
+ mBackground.setHotspot(x, y);
}
/**
@@ -6763,7 +6761,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private void setPressed(boolean pressed, float x, float y) {
if (pressed) {
- setHotspot(R.attr.state_pressed, x, y);
+ setHotspot(x, y);
}
setPressed(pressed);
@@ -6787,10 +6785,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_PRESSED;
}
- if (!pressed) {
- clearHotspot(R.attr.state_pressed);
- }
-
if (needsRefresh) {
refreshDrawableState();
}
@@ -9106,21 +9100,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
- setHotspot(R.attr.state_pressed, x, y);
+ setHotspot(x, y);
setPressed(true);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
- clearHotspot(R.attr.state_pressed);
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break;
case MotionEvent.ACTION_MOVE:
- setHotspot(R.attr.state_pressed, x, y);
+ setHotspot(x, y);
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
@@ -9142,17 +9135,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return false;
}
- private void setHotspot(int id, float x, float y) {
- final Drawable bg = mBackground;
- if (bg != null && bg.supportsHotspots()) {
- bg.setHotspot(id, x, y);
- }
- }
-
- private void clearHotspot(int id) {
- final Drawable bg = mBackground;
- if (bg != null && bg.supportsHotspots()) {
- bg.removeHotspot(id);
+ private void setHotspot(float x, float y) {
+ if (mBackground != null) {
+ mBackground.setHotspot(x, y);
}
}
@@ -12903,10 +12888,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
- if (mBackground != null) {
- mBackground.clearHotspots();
- }
-
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 11d2622..3104862 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -19,6 +19,7 @@ package android.view;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.TimeInterpolator;
+import android.os.Build;
import java.util.ArrayList;
import java.util.HashMap;
@@ -109,6 +110,11 @@ public class ViewPropertyAnimator {
private ValueAnimator mTempValueAnimator;
/**
+ * A RenderThread-driven backend that may intercept startAnimation
+ */
+ private ViewPropertyAnimatorRT mRTBackend;
+
+ /**
* This listener is the mechanism by which the underlying Animator causes changes to the
* properties currently being animated, as well as the cleanup after an animation is
* complete.
@@ -227,7 +233,7 @@ public class ViewPropertyAnimator {
* values are used to calculate the animated value for a given animation fraction
* during the animation.
*/
- private static class NameValuesHolder {
+ static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
@@ -247,6 +253,9 @@ public class ViewPropertyAnimator {
ViewPropertyAnimator(View view) {
mView = view;
view.ensureTransformationInfo();
+ if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+ mRTBackend = new ViewPropertyAnimatorRT(view);
+ }
}
/**
@@ -371,6 +380,10 @@ public class ViewPropertyAnimator {
return this;
}
+ Animator.AnimatorListener getListener() {
+ return mListener;
+ }
+
/**
* Sets a listener for update events in the underlying ValueAnimator that runs
* the property animations. Note that the underlying animator is animating between
@@ -390,6 +403,10 @@ public class ViewPropertyAnimator {
return this;
}
+ ValueAnimator.AnimatorUpdateListener getUpdateListener() {
+ return mUpdateListener;
+ }
+
/**
* Starts the currently pending property animations immediately. Calling <code>start()</code>
* is optional because all animations start automatically at the next opportunity. However,
@@ -825,12 +842,22 @@ public class ViewPropertyAnimator {
return this;
}
+ boolean hasActions() {
+ return mPendingSetupAction != null
+ || mPendingCleanupAction != null
+ || mPendingOnStartAction != null
+ || mPendingOnEndAction != null;
+ }
+
/**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
+ if (mRTBackend != null && mRTBackend.startAnimation(this)) {
+ return;
+ }
mView.setHasTransientState(true);
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
new file mode 100644
index 0000000..709efdb
--- /dev/null
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -0,0 +1,118 @@
+/*
+ * 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.view;
+
+import android.animation.TimeInterpolator;
+import android.view.ViewPropertyAnimator.NameValuesHolder;
+
+import com.android.internal.view.animation.FallbackLUTInterpolator;
+
+import java.util.ArrayList;
+
+
+/**
+ * This is a RenderThread driven backend for ViewPropertyAnimator.
+ */
+class ViewPropertyAnimatorRT {
+
+ private final View mView;
+
+ private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
+
+ ViewPropertyAnimatorRT(View view) {
+ mView = view;
+ }
+
+ /**
+ * @return true if ViewPropertyAnimatorRT handled the animation,
+ * false if ViewPropertyAnimator needs to handle it
+ */
+ public boolean startAnimation(ViewPropertyAnimator parent) {
+ cancelAnimators(parent.mPendingAnimations);
+ if (!canHandleAnimator(parent)) {
+ return false;
+ }
+ doStartAnimation(parent);
+ return true;
+ }
+
+ private void doStartAnimation(ViewPropertyAnimator parent) {
+ int size = parent.mPendingAnimations.size();
+
+ long startDelay = parent.getStartDelay();
+ long duration = parent.getDuration();
+ TimeInterpolator interpolator = parent.getInterpolator();
+ if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+ interpolator = new FallbackLUTInterpolator(interpolator, duration);
+ }
+ for (int i = 0; i < size; i++) {
+ NameValuesHolder holder = parent.mPendingAnimations.get(i);
+ int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
+
+ RenderNodeAnimator animator = new RenderNodeAnimator(property, holder.mFromValue + holder.mDeltaValue);
+ animator.setStartDelay(startDelay);
+ animator.setDuration(duration);
+ animator.setInterpolator(interpolator);
+ animator.setTarget(mView);
+ animator.start();
+ }
+
+ parent.mPendingAnimations.clear();
+ }
+
+ private boolean canHandleAnimator(ViewPropertyAnimator parent) {
+ // TODO: Can we eliminate this entirely?
+ // If RenderNode.animatorProperties() can be toggled to point at staging
+ // instead then RNA can be used as the animators for software as well
+ // as the updateListener fallback paths. If this can be toggled
+ // at the top level somehow, combined with requiresUiRedraw, we could
+ // ensure that RT does not self-animate, allowing for safe driving of
+ // the animators from the UI thread using the same mechanisms
+ // ViewPropertyAnimator does, just with everything sitting on a single
+ // animator subsystem instead of multiple.
+
+ if (parent.getUpdateListener() != null) {
+ return false;
+ }
+ if (parent.getListener() != null) {
+ // TODO support
+ return false;
+ }
+ if (!mView.isHardwareAccelerated()) {
+ // TODO handle this maybe?
+ return false;
+ }
+ if (parent.hasActions()) {
+ return false;
+ }
+ // Here goes nothing...
+ return true;
+ }
+
+ private void cancelAnimators(ArrayList<NameValuesHolder> mPendingAnimations) {
+ int size = mPendingAnimations.size();
+ for (int i = 0; i < size; i++) {
+ NameValuesHolder holder = mPendingAnimations.get(i);
+ int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
+ if (mAnimators[property] != null) {
+ mAnimators[property].cancel();
+ mAnimators[property] = null;
+ }
+ }
+ }
+
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f91ef1a..c9eb130 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2502,22 +2502,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
positionSelector(position, sel);
final Drawable selector = mSelector;
- if (selector != null && selector.supportsHotspots() && position != INVALID_POSITION) {
+ if (selector != null && position != INVALID_POSITION) {
final Rect bounds = mSelectorRect;
final float x = bounds.exactCenterX();
final float y = bounds.exactCenterY();
- selector.setHotspot(R.attr.state_focused, x, y);
+ selector.setHotspot(x, y);
}
}
void positionSelector(int position, View sel) {
if (position != INVALID_POSITION) {
- if (mSelectorPosition != position) {
- final Drawable selector = mSelector;
- if (selector != null && selector.supportsHotspots()) {
- selector.clearHotspots();
- }
- }
mSelectorPosition = position;
}
@@ -3245,9 +3239,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
((TransitionDrawable) d).resetTransition();
}
}
- if (d.supportsHotspots()) {
- d.setHotspot(R.attr.state_pressed, x, y);
- }
+ d.setHotspot(x, y);
}
if (longClickable) {
@@ -3783,9 +3775,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (d != null && d instanceof TransitionDrawable) {
((TransitionDrawable) d).resetTransition();
}
- if (mSelector.supportsHotspots()) {
- mSelector.setHotspot(R.attr.state_pressed, x, ev.getY());
- }
+ mSelector.setHotspot(x, ev.getY());
}
if (mTouchModeReset != null) {
removeCallbacks(mTouchModeReset);
@@ -3797,9 +3787,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mTouchMode = TOUCH_MODE_REST;
child.setPressed(false);
setPressed(false);
- if (mSelector != null && mSelector.supportsHotspots()) {
- mSelector.removeHotspot(R.attr.state_pressed);
- }
if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {
performClick.run();
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 4f2d9c6..1152e17 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -31,8 +31,6 @@ import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.internal.R;
-
public abstract class AbsSeekBar extends ProgressBar {
private final Rect mTempRect = new Rect();
@@ -348,7 +346,7 @@ public abstract class AbsSeekBar extends ProgressBar {
final int right = left + thumbWidth;
final Drawable background = getBackground();
- if (background != null && background.supportsHotspots()) {
+ if (background != null) {
final Rect bounds = mThumb.getBounds();
final int offsetX = mPaddingLeft - mThumbOffset;
final int offsetY = mPaddingTop;
@@ -499,17 +497,10 @@ public abstract class AbsSeekBar extends ProgressBar {
return true;
}
- private void setHotspot(int id, float x, float y) {
- final Drawable bg = getBackground();
- if (bg != null && bg.supportsHotspots()) {
- bg.setHotspot(id, x, y);
- }
- }
-
- private void clearHotspot(int id) {
+ private void setHotspot(float x, float y) {
final Drawable bg = getBackground();
- if (bg != null && bg.supportsHotspots()) {
- bg.removeHotspot(id);
+ if (bg != null) {
+ bg.setHotspot(x, y);
}
}
@@ -541,7 +532,7 @@ public abstract class AbsSeekBar extends ProgressBar {
final int max = getMax();
progress += scale * max;
- setHotspot(R.attr.state_pressed, x, (int) event.getY());
+ setHotspot(x, (int) event.getY());
setProgress((int) progress, true);
}
@@ -567,7 +558,6 @@ public abstract class AbsSeekBar extends ProgressBar {
* canceled.
*/
void onStopTrackingTouch() {
- clearHotspot(R.attr.state_pressed);
mIsDragging = false;
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 9e17cca..6aff4f4 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -285,7 +285,7 @@ public abstract class CompoundButton extends Button implements Checkable {
buttonDrawable.setBounds(left, top, right, bottom);
final Drawable background = getBackground();
- if (background != null && background.supportsHotspots()) {
+ if (background != null) {
background.setHotspotBounds(left, top, right, bottom);
}
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 74a3eec..ad1a023 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -828,7 +828,7 @@ public class Switch extends CompoundButton {
thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
final Drawable background = getBackground();
- if (background != null && background.supportsHotspots()) {
+ if (background != null) {
background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8f073de..a4a9680 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8194,7 +8194,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final boolean isPassword = hasPasswordTransformationMethod();
info.setPassword(isPassword);
- if (!isPassword) {
+ if (!isPassword || shouldSpeakPasswordsForAccessibility()) {
info.setText(getTextForAccessibility());
}
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
index 1feb943..06838c9 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -24,10 +24,13 @@ import android.view.Choreographer;
* Interpolator that builds a lookup table to use. This is a fallback for
* building a native interpolator from a TimeInterpolator that is not marked
* with {@link HasNativeInterpolator}
+ *
+ * This implements TimeInterpolator to allow for easier interop with Animators
*/
@HasNativeInterpolator
-public class FallbackLUTInterpolator implements NativeInterpolatorFactory {
+public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+ private TimeInterpolator mSourceInterpolator;
private final float mLut[];
/**
@@ -35,6 +38,7 @@ public class FallbackLUTInterpolator implements NativeInterpolatorFactory {
* interpolator creation
*/
public FallbackLUTInterpolator(TimeInterpolator interpolator, long duration) {
+ mSourceInterpolator = interpolator;
mLut = createLUT(interpolator, duration);
}
@@ -63,4 +67,9 @@ public class FallbackLUTInterpolator implements NativeInterpolatorFactory {
float[] lut = createLUT(interpolator, duration);
return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
}
+
+ @Override
+ public float getInterpolation(float input) {
+ return mSourceInterpolator.getInterpolation(input);
+ }
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 43e80dc..8418162 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -853,7 +853,7 @@ public:
}
else {
SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
- paint->setLooper(new SkBlurDrawLooper((SkColor)color, sigma, dx, dy))->unref();
+ paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
}
}
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
index 01eaec4..a3c7b0a 100644
--- a/core/jni/android_hardware_UsbRequest.cpp
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -100,18 +100,19 @@ android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz,
}
request->buffer_length = length;
+ // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
+ request->client_data = (void *)env->NewGlobalRef(thiz);
+
if (usb_request_queue(request)) {
if (request->buffer) {
// free our buffer if usb_request_queue fails
free(request->buffer);
request->buffer = NULL;
}
+ env->DeleteGlobalRef((jobject)request->client_data);
return false;
- } else {
- // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
- request->client_data = (void *)env->NewGlobalRef(thiz);
- return true;
}
+ return true;
}
static jint
@@ -152,16 +153,17 @@ android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz,
}
request->buffer_length = length;
+ // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
+ // we also need this to make sure our native buffer is not deallocated
+ // while IO is active
+ request->client_data = (void *)env->NewGlobalRef(thiz);
+
if (usb_request_queue(request)) {
request->buffer = NULL;
+ env->DeleteGlobalRef((jobject)request->client_data);
return false;
- } else {
- // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
- // we also need this to make sure our native buffer is not deallocated
- // while IO is active
- request->client_data = (void *)env->NewGlobalRef(thiz);
- return true;
}
+ return true;
}
static jint
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index 2e3c515..eaf2550 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed">
<item android:id="@id/mask"
android:drawable="@drawable/btn_qntm_alpha" />
-</touch-feedback>
+</ripple>
diff --git a/core/res/res/drawable/btn_default_quantum.xml b/core/res/res/drawable/btn_default_quantum.xml
index c6a3a33..d8ab667 100644
--- a/core/res/res/drawable/btn_default_quantum.xml
+++ b/core/res/res/drawable/btn_default_quantum.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed">
<item>
<selector>
@@ -31,4 +31,4 @@
</item>
</selector>
</item>
-</touch-feedback>
+</ripple>
diff --git a/core/res/res/drawable/edit_text_quantum.xml b/core/res/res/drawable/edit_text_quantum.xml
index c42c7b7..67339e8 100644
--- a/core/res/res/drawable/edit_text_quantum.xml
+++ b/core/res/res/drawable/edit_text_quantum.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorControlActivated">
<item>
<selector>
@@ -36,4 +36,4 @@
</item>
</selector>
</item>
-</touch-feedback>
+</ripple>
diff --git a/core/res/res/drawable/item_background_quantum.xml b/core/res/res/drawable/item_background_quantum.xml
index 11e1f67..631d3e4 100644
--- a/core/res/res/drawable/item_background_quantum.xml
+++ b/core/res/res/drawable/item_background_quantum.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed"
android:pinned="true" />
diff --git a/core/res/res/drawable/list_selector_quantum.xml b/core/res/res/drawable/list_selector_quantum.xml
index c007117..0e185aa 100644
--- a/core/res/res/drawable/list_selector_quantum.xml
+++ b/core/res/res/drawable/list_selector_quantum.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
-<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed">
<item android:id="@id/mask">
<color android:color="@color/white" />
</item>
-</touch-feedback>
+</ripple>
diff --git a/core/res/res/drawable/notification_bg_dim.xml b/core/res/res/drawable/notification_bg_dim.xml
index ec20368..ae03d82 100644
--- a/core/res/res/drawable/notification_bg_dim.xml
+++ b/core/res/res/drawable/notification_bg_dim.xml
@@ -15,9 +15,9 @@
~ limitations under the License
-->
-<touch-feedback
+<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#ff444444"
>
<item android:drawable="@drawable/notification_bg_normal" />
-</touch-feedback> \ No newline at end of file
+</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable/notification_quantum_bg_dim.xml b/core/res/res/drawable/notification_quantum_bg_dim.xml
index ab0e049..eb9a4ab 100644
--- a/core/res/res/drawable/notification_quantum_bg_dim.xml
+++ b/core/res/res/drawable/notification_quantum_bg_dim.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<touch-feedback
+<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#ffffffff"
android:tintMode="src_over"
@@ -26,4 +26,4 @@
<corners android:radius="@dimen/notification_quantum_rounded_rect_radius" />
</shape>
</item>
-</touch-feedback> \ No newline at end of file
+</ripple> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 51dff74..4b03f31 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1669,6 +1669,11 @@
<enum name="KEYCODE_MEDIA_SLEEP" value="223" />
<enum name="KEYCODE_MEDIA_WAKEUP" value="224" />
<enum name="KEYCODE_PAIRING" value="225" />
+ <enum name="KEYCODE_MEDIA_TOP_MENU" value="226" />
+ <enum name="KEYCODE_11" value="227" />
+ <enum name="KEYCODE_12" value="228" />
+ <enum name="KEYCODE_LAST_CHANNEL" value="229" />
+ <enum name="KEYCODE_TV_DATA_SERVICE" value="230" />
</attr>
<!-- ***************************************************************** -->
@@ -4492,7 +4497,7 @@
RTL (right-to-left). -->
<attr name="autoMirrored" />
<!-- Indicates how layer padding should affect the bounds of subsequent layers.
- The default value is nest. -->
+ The default padding mode value is nest. -->
<attr name="paddingMode">
<!-- Nest each layer inside the padding of the previous layer. -->
<enum name="nest" value="0" />
@@ -4647,7 +4652,7 @@
</declare-styleable>
<!-- Drawable used to show animated touch feedback. -->
- <declare-styleable name="TouchFeedbackDrawable">
+ <declare-styleable name="RippleDrawable">
<!-- The tint to use for feedback ripples. This attribute is required. -->
<attr name="tint" />
<!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 6ea0fae..c966a12 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -83,5 +83,4 @@
<item type="id" name="current_scene" />
<item type="id" name="scene_layoutid_cache" />
<item type="id" name="mask" />
- <item type="id" name="shared_element" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 44b25f2b..32ce568 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2177,6 +2177,7 @@
<public type="attr" name="contentInsetEnd" />
<public type="attr" name="contentInsetLeft" />
<public type="attr" name="contentInsetRight" />
+ <public type="attr" name="paddingMode" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2186,7 +2187,6 @@
<public-padding type="id" name="l_resource_pad" end="0x01020040" />
<public type="id" name="mask" />
- <public type="id" name="shared_element" />
<public-padding type="style" name="l_resource_pad" end="0x01030200" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
index 67203b2..22dce39 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -20,6 +20,8 @@ import android.app.Instrumentation;
import android.content.Context;
import android.os.Bundle;
+import java.util.Set;
+
public class BluetoothInstrumentation extends Instrumentation {
private BluetoothTestUtils mUtils = null;
@@ -66,6 +68,8 @@ public class BluetoothInstrumentation extends Instrumentation {
getName();
} else if ("getAddress".equals(command)) {
getAddress();
+ } else if ("getBondedDevices".equals(command)) {
+ getBondedDevices();
} else {
finish(null);
}
@@ -98,6 +102,16 @@ public class BluetoothInstrumentation extends Instrumentation {
finish(mSuccessResult);
}
+ public void getBondedDevices() {
+ Set<BluetoothDevice> devices = getBluetoothAdapter().getBondedDevices();
+ int i = 0;
+ for (BluetoothDevice device : devices) {
+ mSuccessResult.putString(String.format("device-%02d", i), device.getAddress());
+ i++;
+ }
+ finish(mSuccessResult);
+ }
+
public void finish(Bundle result) {
if (result == null) {
result = new Bundle();
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java
new file mode 100644
index 0000000..eb6c419
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.bluetooth;
+
+import android.os.ParcelUuid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+/**
+ * Unit test cases for {@link BluetoothLeAdvertiseScanData}.
+ * <p>
+ * To run this test, use adb shell am instrument -e class
+ * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class BluetoothLeAdvertiseScanDataTest extends TestCase {
+
+ @SmallTest
+ public void testParser() {
+ byte[] scanRecord = new byte[] {
+ 0x02, 0x01, 0x1a, // advertising flags
+ 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
+ 0x04, 0x09, 0x50, 0x65, 0x64, // name
+ 0x02, 0x0A, (byte) 0xec, // tx power level
+ 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
+ 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
+ 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
+ };
+ BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord
+ .getParser().parseFromScanRecord(scanRecord);
+ assertEquals(0x1a, data.getAdvertiseFlags());
+ ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+ assertTrue(data.getServiceUuids().contains(uuid1));
+ assertTrue(data.getServiceUuids().contains(uuid2));
+
+ assertEquals("Ped", data.getLocalName());
+ assertEquals(-20, data.getTxPowerLevel());
+
+ assertEquals(224, data.getManufacturerId());
+ assertArrayEquals(new byte[] {
+ (byte) 0xe0, 0x00, 0x02, 0x15 }, data.getManufacturerSpecificData());
+
+ assertEquals(uuid2, data.getServiceDataUuid());
+ assertArrayEquals(new byte[] {
+ 0x0b, 0x11, 0x50, 0x64 }, data.getServiceData());
+ }
+
+ // Assert two byte arrays are equal.
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ fail("expected:<" + Arrays.toString(expected) +
+ "> but was:<" + Arrays.toString(actual) + ">");
+ }
+
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java
new file mode 100644
index 0000000..ec35d85
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for Bluetooth LE scan filters.
+ * <p>
+ * To run this test, use adb shell am instrument -e class
+ * 'android.bluetooth.BluetoothLeScanFilterTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class BluetoothLeScanFilterTest extends TestCase {
+
+ private static final String DEVICE_MAC = "01:02:03:04:05:AB";
+ private ScanResult mScanResult;
+ private BluetoothLeScanFilter.Builder mFilterBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ byte[] scanRecord = new byte[] {
+ 0x02, 0x01, 0x1a, // advertising flags
+ 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
+ 0x04, 0x09, 0x50, 0x65, 0x64, // name
+ 0x02, 0x0A, (byte) 0xec, // tx power level
+ 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
+ 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
+ 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
+ };
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
+ mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L);
+ mFilterBuilder = BluetoothLeScanFilter.newBuilder();
+ }
+
+ @SmallTest
+ public void testNameFilter() {
+ BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build();
+ assertTrue("name filter fails", filter.matches(mScanResult));
+
+ filter = mFilterBuilder.name("Pem").build();
+ assertFalse("name filter fails", filter.matches(mScanResult));
+
+ }
+
+ @SmallTest
+ public void testDeviceFilter() {
+ BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build();
+ assertTrue("device filter fails", filter.matches(mScanResult));
+
+ filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
+ assertFalse("device filter fails", filter.matches(mScanResult));
+ }
+
+ @SmallTest
+ public void testServiceUuidFilter() {
+ BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid(
+ ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
+ assertTrue("uuid filter fails", filter.matches(mScanResult));
+
+ filter = mFilterBuilder.serviceUuid(
+ ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
+ assertFalse("uuid filter fails", filter.matches(mScanResult));
+
+ filter = mFilterBuilder
+ .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"))
+ .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
+ .build();
+ assertTrue("uuid filter fails", filter.matches(mScanResult));
+ }
+
+ @SmallTest
+ public void testServiceDataFilter() {
+ byte[] serviceData = new byte[] {
+ 0x0b, 0x11, 0x50, 0x64 };
+ BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build();
+ assertTrue("service data filter fails", filter.matches(mScanResult));
+
+ byte[] nonMatchData = new byte[] {
+ 0x0b, 0x01, 0x50, 0x64 };
+ filter = mFilterBuilder.serviceData(nonMatchData).build();
+ assertFalse("service data filter fails", filter.matches(mScanResult));
+
+ byte[] mask = new byte[] {
+ (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
+ filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build();
+ assertTrue("partial service data filter fails", filter.matches(mScanResult));
+ }
+
+ @SmallTest
+ public void testManufacturerSpecificData() {
+ byte[] manufacturerData = new byte[] {
+ (byte) 0xE0, 0x00, 0x02, 0x15 };
+ int manufacturerId = 224;
+ BluetoothLeScanFilter filter =
+ mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
+ assertTrue("manufacturerData filter fails", filter.matches(mScanResult));
+
+ byte[] nonMatchData = new byte[] {
+ (byte) 0xF0, 0x00, 0x02, 0x15 };
+ filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build();
+ assertFalse("manufacturerData filter fails", filter.matches(mScanResult));
+
+ byte[] mask = new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+ };
+ filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData)
+ .manufacturerDataMask(mask).build();
+ assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult));
+ }
+
+ @SmallTest
+ public void testReadWriteParcel() {
+ BluetoothLeScanFilter filter = mFilterBuilder.build();
+ testReadWriteParcelForFilter(filter);
+
+ filter = mFilterBuilder.name("Ped").build();
+ testReadWriteParcelForFilter(filter);
+
+ filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
+ testReadWriteParcelForFilter(filter);
+
+ filter = mFilterBuilder.serviceUuid(
+ ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
+ testReadWriteParcelForFilter(filter);
+
+ filter = mFilterBuilder.serviceUuidMask(
+ ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
+ testReadWriteParcelForFilter(filter);
+
+ byte[] serviceData = new byte[] {
+ 0x0b, 0x11, 0x50, 0x64 };
+
+ filter = mFilterBuilder.serviceData(serviceData).build();
+ testReadWriteParcelForFilter(filter);
+
+ byte[] serviceDataMask = new byte[] {
+ (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
+ filter = mFilterBuilder.serviceDataMask(serviceDataMask).build();
+ testReadWriteParcelForFilter(filter);
+
+ byte[] manufacturerData = new byte[] {
+ (byte) 0xE0, 0x00, 0x02, 0x15 };
+ int manufacturerId = 224;
+ filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
+ testReadWriteParcelForFilter(filter);
+
+ byte[] manufacturerDataMask = new byte[] {
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+ };
+ filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build();
+ testReadWriteParcelForFilter(filter);
+ }
+
+ private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) {
+ Parcel parcel = Parcel.obtain();
+ filter.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ BluetoothLeScanFilter filterFromParcel =
+ BluetoothLeScanFilter.CREATOR.createFromParcel(parcel);
+ System.out.println(filterFromParcel);
+ assertEquals(filter, filterFromParcel);
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java
new file mode 100644
index 0000000..8064ba8
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for Bluetooth LE scans.
+ * <p>
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest'
+ * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class BluetoothLeScannerTest extends TestCase {
+
+ /**
+ * Test read and write parcel of ScanResult
+ */
+ @SmallTest
+ public void testScanResultParceling() {
+ BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06");
+ byte[] scanRecord = new byte[] {
+ 1, 2, 3 };
+ int rssi = -10;
+ long timestampMicros = 10000L;
+
+ ScanResult result = new ScanResult(device, scanRecord, rssi, timestampMicros);
+ Parcel parcel = Parcel.obtain();
+ result.writeToParcel(parcel, 0);
+ // Need to reset parcel data position to the beginning.
+ parcel.setDataPosition(0);
+ ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel);
+ assertEquals(result, resultFromParcel);
+ }
+
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
index 0f3ea0e..536d7226 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
@@ -47,4 +47,20 @@ public class BluetoothUuidTest extends TestCase {
assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"),
BluetoothUuid.parseUuidFrom(uuid128));
}
+
+ @SmallTest
+ public void testUuidType() {
+ assertTrue(BluetoothUuid.is16BitUuid(
+ ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
+ assertFalse(BluetoothUuid.is32BitUuid(
+ ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
+
+ assertFalse(BluetoothUuid.is16BitUuid(
+ ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
+ assertTrue(BluetoothUuid.is32BitUuid(
+ ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
+ assertFalse(BluetoothUuid.is32BitUuid(
+ ParcelUuid.fromString("FE33110B-1000-1000-8000-00805F9B34FB")));
+
+ }
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b939636..911fb96 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -484,68 +484,40 @@ public abstract class Drawable {
}
/**
- * Indicates whether the drawable supports hotspots. Hotspots are uniquely
- * identifiable coordinates the may be added, updated and removed within a
- * drawable.
- *
- * @return true if hotspots are supported
- * @see #setHotspot(int, float, float)
- * @see #removeHotspot(int)
- * @see #clearHotspots()
- */
- public boolean supportsHotspots() {
- return false;
- }
-
- /**
- * Specifies a hotspot's location within the drawable.
- * <p>
- * The specified key should be an id declared in the resources of the
- * application to ensure it is unique (see the <a
- * href={@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>).
+ * Specifies the hotspot's location within the drawable.
*
- * @param key The key identifying the hotspot
* @param x The X coordinate of the center of the hotspot
* @param y The Y coordinate of the center of the hotspot
*/
- public void setHotspot(int key, float x, float y) {}
-
- /**
- * Removes the hotspot with the specified key from the drawable.
- *
- * @param key The key identifying the hotspot
- */
- public void removeHotspot(int key) {}
+ public void setHotspot(float x, float y) {}
/**
- * Immediately removes all hotspots from the drawable.
- */
- public void clearHotspots() {}
-
- /**
- * Sets the bounds to which hotspots are constrained.
- *
- * @hide until we finalize these APIs
+ * Sets the bounds to which the hotspot is constrained, if they should be
+ * different from the drawable bounds.
+ *
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
*/
public void setHotspotBounds(int left, int top, int right, int bottom) {}
/**
* Whether this drawable requests projection.
*
- * @hide until we finalize these APIs
+ * @hide magic!
*/
public boolean isProjected() {
return false;
}
/**
- * Indicates whether this view will change its appearance based on state.
- * Clients can use this to determine whether it is necessary to calculate
- * their state and call setState.
- *
- * @return True if this view changes its appearance based on state, false
- * otherwise.
+ * Indicates whether this drawable will change its appearance based on
+ * state. Clients can use this to determine whether it is necessary to
+ * calculate their state and call setState.
*
+ * @return True if this drawable changes its appearance based on state,
+ * false otherwise.
* @see #setState(int[])
*/
public boolean isStateful() {
@@ -1032,6 +1004,11 @@ public abstract class Drawable {
return createFromXmlInnerThemed(r, parser, attrs, null);
}
+ /**
+ * Create a themed drawable from inside an XML document. Called on a parser
+ * positioned at a tag in an XML document, tries to create a Drawable from
+ * that tag. Returns null if the tag is not a valid drawable.
+ */
public static Drawable createFromXmlInnerThemed(Resources r, XmlPullParser parser,
AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
final Drawable drawable;
@@ -1047,8 +1024,8 @@ public abstract class Drawable {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
drawable = new TransitionDrawable();
- } else if (name.equals("touch-feedback")) {
- drawable = new TouchFeedbackDrawable();
+ } else if (name.equals("ripple")) {
+ drawable = new RippleDrawable();
} else if (name.equals("color")) {
drawable = new ColorDrawable();
} else if (name.equals("shape")) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 08fc99d..ec5c6c6 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -238,35 +238,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
@Override
- public void setHotspot(int key, float x, float y) {
+ public void setHotspot(float x, float y) {
if (mCurrDrawable != null) {
- mCurrDrawable.setHotspot(key, x, y);
+ mCurrDrawable.setHotspot(x, y);
}
}
@Override
- public void removeHotspot(int key) {
- if (mCurrDrawable != null) {
- mCurrDrawable.removeHotspot(key);
- }
- }
-
- @Override
- public void clearHotspots() {
- if (mCurrDrawable != null) {
- mCurrDrawable.clearHotspots();
- }
- }
-
- @Override
- public boolean supportsHotspots() {
- if (mCurrDrawable != null) {
- return mCurrDrawable.supportsHotspots();
- }
- return false;
- }
-
- @Override
protected boolean onStateChange(int[] state) {
if (mLastDrawable != null) {
return mLastDrawable.setState(state);
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
deleted file mode 100644
index 6ab33f8..0000000
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ /dev/null
@@ -1,305 +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.graphics.drawable;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.Xfermode;
-
-/**
- * A Drawable that wraps another Drawable.
- */
-public class DrawableWrapper extends Drawable implements Drawable.Callback {
- private WrapperState mWrapperState;
-
- /** Local drawable backed by its own constant state. */
- private Drawable mWrappedDrawable;
-
- private boolean mMutated;
-
- /** @hide */
- @Override
- public boolean isProjected() {
- return mWrappedDrawable.isProjected();
- }
-
- @Override
- public void setAutoMirrored(boolean mirrored) {
- mWrappedDrawable.setAutoMirrored(mirrored);
- }
-
- @Override
- public boolean isAutoMirrored() {
- return mWrappedDrawable.isAutoMirrored();
- }
-
- @Override
- public int getMinimumWidth() {
- return mWrappedDrawable.getMinimumWidth();
- }
-
- @Override
- public int getMinimumHeight() {
- return mWrappedDrawable.getMinimumHeight();
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mWrappedDrawable.getIntrinsicWidth();
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mWrappedDrawable.getIntrinsicHeight();
- }
-
- @Override
- public Drawable getCurrent() {
- return mWrappedDrawable.getCurrent();
- }
-
- @Override
- public void invalidateDrawable(Drawable who) {
- final Callback callback = getCallback();
- if (callback != null) {
- callback.invalidateDrawable(this);
- }
- }
-
- @Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
- final Callback callback = getCallback();
- if (callback != null) {
- callback.scheduleDrawable(this, what, when);
- }
- }
-
- @Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
- final Callback callback = getCallback();
- if (callback != null) {
- callback.unscheduleDrawable(this, what);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- mWrappedDrawable.draw(canvas);
- }
-
- @Override
- public int getChangingConfigurations() {
- return mWrappedDrawable.getChangingConfigurations();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- return mWrappedDrawable.getPadding(padding);
- }
-
- @Override
- public Rect getDirtyBounds() {
- return mWrappedDrawable.getDirtyBounds();
- }
-
- @Override
- public boolean supportsHotspots() {
- return mWrappedDrawable.supportsHotspots();
- }
-
- @Override
- public void setHotspot(int id, float x, float y) {
- mWrappedDrawable.setHotspot(id, x, y);
- }
-
- @Override
- public void removeHotspot(int id) {
- mWrappedDrawable.removeHotspot(id);
- }
-
- @Override
- public void clearHotspots() {
- mWrappedDrawable.clearHotspots();
- }
-
- @Override
- public boolean setVisible(boolean visible, boolean restart) {
- // Must call through to super().
- super.setVisible(visible, restart);
- return mWrappedDrawable.setVisible(visible, restart);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mWrappedDrawable.setAlpha(alpha);
- }
-
- @Override
- public int getAlpha() {
- return mWrappedDrawable.getAlpha();
- }
-
- /** {@hide} */
- @Override
- public void setLayoutDirection(int layoutDirection) {
- mWrappedDrawable.setLayoutDirection(layoutDirection);
- }
-
- /** {@hide} */
- @Override
- public int getLayoutDirection() {
- return mWrappedDrawable.getLayoutDirection();
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- mWrappedDrawable.setColorFilter(cf);
- }
-
- @Override
- public ColorFilter getColorFilter() {
- return mWrappedDrawable.getColorFilter();
- }
-
- @Override
- public void setFilterBitmap(boolean filter) {
- mWrappedDrawable.setFilterBitmap(filter);
- }
-
- @Override
- public void setXfermode(Xfermode mode) {
- mWrappedDrawable.setXfermode(mode);
- }
-
- @Override
- public int getOpacity() {
- return mWrappedDrawable.getOpacity();
- }
-
- @Override
- public boolean isStateful() {
- return mWrappedDrawable.isStateful();
- }
-
- @Override
- public final boolean setState(int[] stateSet) {
- return super.setState(stateSet);
- }
-
- @Override
- public final int[] getState() {
- return super.getState();
- }
-
- @Override
- protected boolean onStateChange(int[] state) {
- // Don't override setState(), getState().
- return mWrappedDrawable.setState(state);
- }
-
- @Override
- protected boolean onLevelChange(int level) {
- // Don't override setLevel(), getLevel().
- return mWrappedDrawable.setLevel(level);
- }
-
- @Override
- public final void setBounds(int left, int top, int right, int bottom) {
- super.setBounds(left, top, right, bottom);
- }
-
- @Override
- public final void setBounds(Rect bounds) {
- super.setBounds(bounds);
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- // Don't override setBounds(), getBounds().
- mWrappedDrawable.setBounds(bounds);
- }
-
- protected void setConstantState(WrapperState wrapperState, Resources res) {
- mWrapperState = wrapperState;
-
- // Load a new drawable from the constant state.
- if (wrapperState == null || wrapperState.mWrappedConstantState == null) {
- mWrappedDrawable = null;
- } else if (res != null) {
- mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable(res);
- } else {
- mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable();
- }
- }
-
- @Override
- public ConstantState getConstantState() {
- return mWrapperState;
- }
-
- @Override
- public Drawable mutate() {
- if (!mMutated) {
- mWrappedDrawable = mWrappedDrawable.mutate();
- mMutated = true;
- }
- return this;
- }
-
- /**
- * Sets the wrapped drawable and update the constant state.
- *
- * @param drawable
- * @param res
- */
- protected final void setDrawable(Drawable drawable, Resources res) {
- if (mWrappedDrawable != null) {
- mWrappedDrawable.setCallback(null);
- }
-
- mWrappedDrawable = drawable;
-
- if (drawable != null) {
- drawable.setCallback(this);
-
- mWrapperState.mWrappedConstantState = drawable.getConstantState();
- } else {
- mWrapperState.mWrappedConstantState = null;
- }
- }
-
- protected final Drawable getDrawable() {
- return mWrappedDrawable;
- }
-
- public static abstract class WrapperState extends ConstantState {
- ConstantState mWrappedConstantState;
-
- WrapperState(WrapperState orig) {
- if (orig != null) {
- mWrappedConstantState = orig.mWrappedConstantState;
- }
- }
-
- @Override
- public int getChangingConfigurations() {
- return mWrappedConstantState.getChangingConfigurations();
- }
- }
-}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 9384caf..3749339 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -131,6 +131,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
// overrides from Drawable.Callback
+ @Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
@@ -138,6 +139,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
@@ -145,6 +147,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
@@ -184,23 +187,8 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
}
@Override
- public boolean supportsHotspots() {
- return mInsetState.mDrawable.supportsHotspots();
- }
-
- @Override
- public void setHotspot(int id, float x, float y) {
- mInsetState.mDrawable.setHotspot(id, x, y);
- }
-
- @Override
- public void removeHotspot(int id) {
- mInsetState.mDrawable.removeHotspot(id);
- }
-
- @Override
- public void clearHotspots() {
- mInsetState.mDrawable.clearHotspots();
+ public void setHotspot(float x, float y) {
+ mInsetState.mDrawable.setHotspot(x, y);
}
@Override
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 7847aad..f446000 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -38,10 +38,12 @@ import java.io.IOException;
* order, so the element with the largest index will be drawn on top.
* <p>
* It can be defined in an XML file with the <code>&lt;layer-list></code> element.
- * Each Drawable in the layer is defined in a nested <code>&lt;item></code>. For more
- * information, see the guide to <a
- * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
+ * <p>
+ * For more information, see the guide to
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*
+ * @attr ref android.R.styleable#LayerDrawable_paddingMode
* @attr ref android.R.styleable#LayerDrawableItem_left
* @attr ref android.R.styleable#LayerDrawableItem_top
* @attr ref android.R.styleable#LayerDrawableItem_right
@@ -53,10 +55,16 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
/**
* Padding mode used to nest each layer inside the padding of the previous
* layer.
+ *
+ * @see #setPaddingMode(int)
*/
public static final int PADDING_MODE_NEST = 0;
- /** Padding mode used to stack each layer directly atop the previous layer. */
+ /**
+ * Padding mode used to stack each layer directly atop the previous layer.
+ *
+ * @see #setPaddingMode(int)
+ */
public static final int PADDING_MODE_STACK = 1;
LayerState mLayerState;
@@ -127,9 +135,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(
- r, theme, attrs, R.styleable.LayerDrawable);
- inflateStateFromTypedArray(a);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
+ updateStateFromTypedArray(a);
a.recycle();
inflateLayers(r, parser, attrs, theme);
@@ -141,25 +148,19 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
/**
* Initializes the constant state from the values in the typed array.
*/
- private void inflateStateFromTypedArray(TypedArray a) {
+ private void updateStateFromTypedArray(TypedArray a) {
final LayerState state = mLayerState;
// Extract the theme attributes, if any.
final int[] themeAttrs = a.extractThemeAttrs();
state.mThemeAttrs = themeAttrs;
- if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawable_opacity] == 0) {
- mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, PixelFormat.UNKNOWN);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawable_autoMirrored] == 0) {
- state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored, false);
- }
+ mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
- if (themeAttrs == null || themeAttrs[R.styleable.LayerDrawableItem_drawable] == 0) {
- state.mPaddingMode = a.getInteger(
- R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
- }
+ state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
+ state.mAutoMirrored);
+ state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
+ state.mPaddingMode);
}
/**
@@ -181,9 +182,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
continue;
}
- a = obtainAttributes(
- r, theme, attrs, R.styleable.LayerDrawableItem);
+ a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
+ final int[] themeAttrs = a.extractThemeAttrs();
final int left = a.getDimensionPixelOffset(
R.styleable.LayerDrawableItem_left, 0);
final int top = a.getDimensionPixelOffset(
@@ -197,7 +198,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final int id = a.getResourceId(
R.styleable.LayerDrawableItem_id, View.NO_ID);
- // TODO: Cache typed array, if necessary.
a.recycle();
final Drawable dr;
@@ -214,7 +214,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
}
- addLayer(dr, id, left, top, right, bottom);
+ addLayer(dr, themeAttrs, id, left, top, right, bottom);
}
}
@@ -224,7 +224,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final LayerState state = mLayerState;
if (state == null) {
- throw new RuntimeException("Can't apply theme to <layer-list> with no constant state");
+ return;
}
final int[] themeAttrs = state.mThemeAttrs;
@@ -239,9 +239,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
- final Drawable layer = array[i].mDrawable;
- if (layer.canApplyTheme()) {
- layer.applyTheme(t);
+ final ChildDrawable layer = array[i];
+ final Drawable d = layer.mDrawable;
+ if (d.canApplyTheme()) {
+ d.applyTheme(t);
}
}
@@ -249,26 +250,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
onStateChange(getState());
}
- /**
- * Updates the constant state from the values in the typed array.
- */
- private void updateStateFromTypedArray(TypedArray a) {
- final LayerState state = mLayerState;
-
- if (a.hasValue(R.styleable.LayerDrawable_opacity)) {
- mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, PixelFormat.UNKNOWN);
- }
-
- if (a.hasValue(R.styleable.LayerDrawable_autoMirrored)) {
- state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored, false);
- }
-
- if (a.hasValue(R.styleable.LayerDrawableItem_drawable)) {
- state.mPaddingMode = a.getInteger(
- R.styleable.LayerDrawableItem_drawable, PADDING_MODE_NEST);
- }
- }
-
@Override
public boolean canApplyTheme() {
final LayerState state = mLayerState;
@@ -283,14 +264,15 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final ChildDrawable[] array = state.mChildren;
final int N = state.mNum;
for (int i = 0; i < N; i++) {
- if (array[i].mDrawable.canApplyTheme()) {
+ final ChildDrawable layer = array[i];
+ if (layer.mThemeAttrs != null || layer.mDrawable.canApplyTheme()) {
return true;
}
}
return false;
}
-
+
/**
* @hide
*/
@@ -315,13 +297,15 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* Add a new layer to this drawable. The new layer is identified by an id.
*
* @param layer The drawable to add as a layer.
+ * @param themeAttrs Theme attributes extracted from the layer.
* @param id The id of the new layer.
* @param left The left padding of the new layer.
* @param top The top padding of the new layer.
* @param right The right padding of the new layer.
* @param bottom The bottom padding of the new layer.
*/
- private void addLayer(Drawable layer, int id, int left, int top, int right, int bottom) {
+ private void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
+ int bottom) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
final int i = st.mNum;
@@ -339,6 +323,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
final ChildDrawable childDrawable = new ChildDrawable();
st.mChildren[i] = childDrawable;
childDrawable.mId = id;
+ childDrawable.mThemeAttrs = themeAttrs;
childDrawable.mDrawable = layer;
childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
childDrawable.mInsetL = left;
@@ -471,8 +456,14 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
*
* @param mode padding mode, one of:
* <ul>
- * <li>{@link #PADDING_MODE_NEST} <li>{@link #PADDING_MODE_STACK}
+ * <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
+ * padding of the previous layer
+ * <li>{@link #PADDING_MODE_STACK} to stack each layer directly
+ * atop the previous layer
* </ul>
+ *
+ * @see #getPaddingMode()
+ * @attr ref android.R.styleable#LayerDrawable_paddingMode
*/
public void setPaddingMode(int mode) {
if (mLayerState.mPaddingMode != mode) {
@@ -482,7 +473,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
/**
* @return the current padding mode
+ *
* @see #setPaddingMode(int)
+ * @attr ref android.R.styleable#LayerDrawable_paddingMode
*/
public int getPaddingMode() {
return mLayerState.mPaddingMode;
@@ -569,42 +562,11 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
@Override
- public boolean supportsHotspots() {
- final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
- for (int i = 0; i < N; i++) {
- if (array[i].mDrawable.supportsHotspots()) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public void setHotspot(int id, float x, float y) {
- final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
- for (int i = 0; i < N; i++) {
- array[i].mDrawable.setHotspot(id, x, y);
- }
- }
-
- @Override
- public void removeHotspot(int id) {
- final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
- for (int i = 0; i < N; i++) {
- array[i].mDrawable.removeHotspot(id);
- }
- }
-
- @Override
- public void clearHotspots() {
+ public void setHotspot(float x, float y) {
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
- array[i].mDrawable.clearHotspots();
+ array[i].mDrawable.setHotspot(x, y);
}
}
@@ -936,7 +898,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
private boolean mHaveIsStateful;
private boolean mIsStateful;
- private boolean mAutoMirrored;
+ private boolean mAutoMirrored = false;
private int mPaddingMode = PADDING_MODE_NEST;
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index a55a4b2..6776e66 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -22,6 +22,7 @@ import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PointF;
@@ -44,7 +45,7 @@ import java.io.IOException;
/**
* Drawable that shows a ripple effect in response to state changes. The
* anchoring position of the ripple for a given state may be specified by
- * calling {@link #setHotspot(int, float, float)} with the corresponding state
+ * calling {@link #setHotspot(float, float)} with the corresponding state
* attribute identifier.
* <p>
* A touch feedback drawable may contain multiple child layers, including a
@@ -56,19 +57,19 @@ import java.io.IOException;
* <p>
* If no mask layer is set, the ripple effect is simply blended onto the
* composite of the child layers using the specified
- * {@link android.R.styleable#TouchFeedbackDrawable_tintMode}.
+ * {@link android.R.styleable#RippleDrawable_tintMode}.
* <p>
* If no child layers or mask is specified and the ripple is set as a View
* background, the ripple will be blended onto the first available parent
* background within the View's hierarchy using the specified
- * {@link android.R.styleable#TouchFeedbackDrawable_tintMode}. In this case, the
+ * {@link android.R.styleable#RippleDrawable_tintMode}. In this case, the
* drawing region may extend outside of the Drawable bounds.
*
* @attr ref android.R.styleable#DrawableStates_state_focused
* @attr ref android.R.styleable#DrawableStates_state_pressed
*/
-public class TouchFeedbackDrawable extends LayerDrawable {
- private static final String LOG_TAG = TouchFeedbackDrawable.class.getSimpleName();
+public class RippleDrawable extends LayerDrawable {
+ private static final String LOG_TAG = RippleDrawable.class.getSimpleName();
private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
private static final PorterDuffXfermode DST_ATOP = new PorterDuffXfermode(Mode.DST_ATOP);
private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
@@ -88,17 +89,17 @@ public class TouchFeedbackDrawable extends LayerDrawable {
/** Current dirty bounds, union of current and previous drawing bounds. */
private final Rect mDirtyBounds = new Rect();
- private final TouchFeedbackState mState;
+ private final RippleState mState;
/**
* Lazily-created map of pending hotspot locations. These may be modified by
- * calls to {@link #setHotspot(int, float, float)}.
+ * calls to {@link #setHotspot(float, float)}.
*/
private SparseArray<PointF> mPendingHotspots;
/**
* Lazily-created map of active hotspot locations. These may be modified by
- * calls to {@link #setHotspot(int, float, float)}.
+ * calls to {@link #setHotspot(float, float)}.
*/
private SparseArray<Ripple> mActiveHotspots;
@@ -121,8 +122,18 @@ public class TouchFeedbackDrawable extends LayerDrawable {
/** Whether bounds are being overridden. */
private boolean mOverrideBounds;
- TouchFeedbackDrawable() {
- this(new TouchFeedbackState(null, null, null), null, null);
+ RippleDrawable() {
+ this(new RippleState(null, null, null), null, null);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+
}
@Override
@@ -233,7 +244,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(
- r, theme, attrs, R.styleable.TouchFeedbackDrawable);
+ r, theme, attrs, R.styleable.RippleDrawable);
updateStateFromTypedArray(a);
a.recycle();
@@ -267,22 +278,22 @@ public class TouchFeedbackDrawable extends LayerDrawable {
* Initializes the constant state from the values in the typed array.
*/
private void updateStateFromTypedArray(TypedArray a) {
- final TouchFeedbackState state = mState;
+ final RippleState state = mState;
// Extract the theme attributes, if any.
state.mTouchThemeAttrs = a.extractThemeAttrs();
- final ColorStateList tint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+ final ColorStateList tint = a.getColorStateList(R.styleable.RippleDrawable_tint);
if (tint != null) {
mState.mTint = tint;
}
- final int tintMode = a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1);
+ final int tintMode = a.getInt(R.styleable.RippleDrawable_tintMode, -1);
if (tintMode != -1) {
mState.setTintMode(Drawable.parseTintMode(tintMode, Mode.SRC_ATOP));
}
- mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, mState.mPinned);
+ mState.mPinned = a.getBoolean(R.styleable.RippleDrawable_pinned, mState.mPinned);
}
/**
@@ -301,13 +312,13 @@ public class TouchFeedbackDrawable extends LayerDrawable {
public void applyTheme(Theme t) {
super.applyTheme(t);
- final TouchFeedbackState state = mState;
+ final RippleState state = mState;
if (state == null || state.mTouchThemeAttrs == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
- R.styleable.TouchFeedbackDrawable);
+ R.styleable.RippleDrawable);
updateStateFromTypedArray(a);
a.recycle();
}
@@ -318,17 +329,14 @@ public class TouchFeedbackDrawable extends LayerDrawable {
}
@Override
- public boolean supportsHotspots() {
- return true;
- }
-
- @Override
- public void setHotspot(int id, float x, float y) {
+ public void setHotspot(float x, float y) {
if (mState.mPinned && !circleContains(mHotspotBounds, x, y)) {
x = mHotspotBounds.exactCenterX();
y = mHotspotBounds.exactCenterY();
}
+ // TODO: We should only have a single pending/active hotspot.
+ final int id = R.attr.state_pressed;
final int[] stateSet = getState();
if (!Arrays.contains(stateSet, id)) {
// The hotspot is not active, so just modify the pending location.
@@ -423,8 +431,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
mActiveHotspots.put(id, newRipple);
}
- @Override
- public void removeHotspot(int id) {
+ private void removeHotspot(int id) {
if (mActiveHotspots == null) {
return;
}
@@ -437,8 +444,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
}
}
- @Override
- public void clearHotspots() {
+ private void clearHotspots() {
if (mActiveHotspots != null) {
mActiveHotspots.clear();
}
@@ -632,7 +638,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
return mState;
}
- static class TouchFeedbackState extends LayerState {
+ static class RippleState extends LayerState {
int[] mTouchThemeAttrs;
ColorStateList mTint = null;
PorterDuffXfermode mTintXfermode = SRC_ATOP;
@@ -640,8 +646,8 @@ public class TouchFeedbackDrawable extends LayerDrawable {
Drawable mMask;
boolean mPinned = false;
- public TouchFeedbackState(
- TouchFeedbackState orig, TouchFeedbackDrawable owner, Resources res) {
+ public RippleState(
+ RippleState orig, RippleDrawable owner, Resources res) {
super(orig, owner, res);
if (orig != null) {
@@ -655,7 +661,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
}
public void setTintMode(Mode mode) {
- final Mode invertedMode = TouchFeedbackState.invertPorterDuffMode(mode);
+ final Mode invertedMode = RippleState.invertPorterDuffMode(mode);
mTintXfermodeInverse = new PorterDuffXfermode(invertedMode);
mTintXfermode = new PorterDuffXfermode(mode);
}
@@ -675,17 +681,17 @@ public class TouchFeedbackDrawable extends LayerDrawable {
@Override
public Drawable newDrawable() {
- return new TouchFeedbackDrawable(this, null, null);
+ return new RippleDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new TouchFeedbackDrawable(this, res, null);
+ return new RippleDrawable(this, res, null);
}
@Override
public Drawable newDrawable(Resources res, Theme theme) {
- return new TouchFeedbackDrawable(this, res, theme);
+ return new RippleDrawable(this, res, theme);
}
/**
@@ -716,20 +722,20 @@ public class TouchFeedbackDrawable extends LayerDrawable {
}
}
- private TouchFeedbackDrawable(TouchFeedbackState state, Resources res, Theme theme) {
+ private RippleDrawable(RippleState state, Resources res, Theme theme) {
boolean needsTheme = false;
- final TouchFeedbackState ns;
+ final RippleState ns;
if (theme != null && state != null && state.canApplyTheme()) {
- ns = new TouchFeedbackState(state, this, res);
+ ns = new RippleState(state, this, res);
needsTheme = true;
} else if (state == null) {
- ns = new TouchFeedbackState(null, this, res);
+ ns = new RippleState(null, this, res);
} else {
// We always need a new state since child drawables contain local
// state but live within the parent's constant state.
// TODO: Move child drawables into local state.
- ns = new TouchFeedbackState(state, this, res);
+ ns = new RippleState(state, this, res);
}
if (res != null) {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index edf1091..5f9d1cd 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -145,6 +145,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
* Sets the start angle for rotation.
*
* @param fromDegrees Starting angle in degrees
+ *
+ * @see #getFromDegrees()
+ * @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
public void setFromDegrees(float fromDegrees) {
if (mState.mFromDegrees != fromDegrees) {
@@ -155,6 +158,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return The starting angle for rotation in degrees
+ *
+ * @see #setFromDegrees(float)
+ * @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
public float getFromDegrees() {
return mState.mFromDegrees;
@@ -164,6 +170,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
* Sets the end angle for rotation.
*
* @param toDegrees Ending angle in degrees
+ *
+ * @see #getToDegrees()
+ * @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public void setToDegrees(float toDegrees) {
if (mState.mToDegrees != toDegrees) {
@@ -174,6 +183,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return The ending angle for rotation in degrees
+ *
+ * @see #setToDegrees(float)
+ * @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public float getToDegrees() {
return mState.mToDegrees;
@@ -186,7 +198,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
* relative, the position represents a fraction of the drawable
* width. Otherwise, the position represents an absolute value in
* pixels.
+ *
* @see #setPivotXRelative(boolean)
+ * @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public void setPivotX(float pivotX) {
if (mState.mPivotX == pivotX) {
@@ -197,7 +211,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return X position around which to rotate
+ *
* @see #setPivotX(float)
+ * @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public float getPivotX() {
return mState.mPivotX;
@@ -209,6 +225,8 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
*
* @param relative True if the X pivot represents a fraction of the drawable
* width, or false if it represents an absolute value in pixels
+ *
+ * @see #isPivotXRelative()
*/
public void setPivotXRelative(boolean relative) {
if (mState.mPivotXRel == relative) {
@@ -220,6 +238,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return True if the X pivot represents a fraction of the drawable width,
* or false if it represents an absolute value in pixels
+ *
* @see #setPivotXRelative(boolean)
*/
public boolean isPivotXRelative() {
@@ -233,7 +252,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
* relative, the position represents a fraction of the drawable
* height. Otherwise, the position represents an absolute value
* in pixels.
- * @see #setPivotYRelative(boolean)
+ *
+ * @see #getPivotY()
+ * @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public void setPivotY(float pivotY) {
if (mState.mPivotY == pivotY) {
@@ -244,7 +265,9 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return Y position around which to rotate
+ *
* @see #setPivotY(float)
+ * @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public float getPivotY() {
return mState.mPivotY;
@@ -256,6 +279,8 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
*
* @param relative True if the Y pivot represents a fraction of the drawable
* height, or false if it represents an absolute value in pixels
+ *
+ * @see #isPivotYRelative()
*/
public void setPivotYRelative(boolean relative) {
if (mState.mPivotYRel == relative) {
@@ -267,6 +292,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
/**
* @return True if the Y pivot represents a fraction of the drawable height,
* or false if it represents an absolute value in pixels
+ *
* @see #setPivotYRelative(boolean)
*/
public boolean isPivotYRelative() {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3a3f76d..575667d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2557,60 +2557,191 @@ public class AudioManager {
// class is not used by other parts of the framework, which instead use definitions and methods
// from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService.
- /** {@hide} The audio output device code for the small speaker at the front of the device used
+ /** @hide
+ * The audio output device code for the small speaker at the front of the device used
* when placing calls. Does not refer to an in-ear headphone without attached microphone,
* such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a
* {@link #DEVICE_OUT_WIRED_HEADPHONE}.
*/
public static final int DEVICE_OUT_EARPIECE = AudioSystem.DEVICE_OUT_EARPIECE;
- /** {@hide} The audio output device code for the built-in speaker */
+ /** @hide
+ * The audio output device code for the built-in speaker */
public static final int DEVICE_OUT_SPEAKER = AudioSystem.DEVICE_OUT_SPEAKER;
- /** {@hide} The audio output device code for a wired headset with attached microphone */
+ /** @hide
+ * The audio output device code for a wired headset with attached microphone */
public static final int DEVICE_OUT_WIRED_HEADSET = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
- /** {@hide} The audio output device code for a wired headphone without attached microphone */
+ /** @hide
+ * The audio output device code for a wired headphone without attached microphone */
public static final int DEVICE_OUT_WIRED_HEADPHONE = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
- /** {@hide} The audio output device code for generic Bluetooth SCO, for voice */
+ /** @hide
+ * The audio output device code for generic Bluetooth SCO, for voice */
public static final int DEVICE_OUT_BLUETOOTH_SCO = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
- /** {@hide} The audio output device code for Bluetooth SCO Headset Profile (HSP) and
- * Hands-Free Profile (HFP), for voice
+ /** @hide
+ * The audio output device code for Bluetooth SCO Headset Profile (HSP) and
+ * Hands-Free Profile (HFP), for voice
*/
public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET =
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- /** {@hide} The audio output device code for Bluetooth SCO car audio, for voice */
+ /** @hide
+ * The audio output device code for Bluetooth SCO car audio, for voice */
public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT =
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- /** {@hide} The audio output device code for generic Bluetooth A2DP, for music */
+ /** @hide
+ * The audio output device code for generic Bluetooth A2DP, for music */
public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
- /** {@hide} The audio output device code for Bluetooth A2DP headphones, for music */
+ /** @hide
+ * The audio output device code for Bluetooth A2DP headphones, for music */
public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES =
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- /** {@hide} The audio output device code for Bluetooth A2DP external speaker, for music */
+ /** @hide
+ * The audio output device code for Bluetooth A2DP external speaker, for music */
public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER =
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
- /** {@hide} The audio output device code for S/PDIF or HDMI */
+ /** @hide
+ * The audio output device code for S/PDIF (legacy) or HDMI
+ * Deprecated: replaced by {@link #DEVICE_OUT_HDMI} */
public static final int DEVICE_OUT_AUX_DIGITAL = AudioSystem.DEVICE_OUT_AUX_DIGITAL;
- /** {@hide} The audio output device code for an analog wired headset attached via a
+ /** @hide
+ * The audio output device code for HDMI */
+ public static final int DEVICE_OUT_HDMI = AudioSystem.DEVICE_OUT_HDMI;
+ /** @hide
+ * The audio output device code for an analog wired headset attached via a
* docking station
*/
public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
- /** {@hide} The audio output device code for a digital wired headset attached via a
+ /** @hide
+ * The audio output device code for a digital wired headset attached via a
* docking station
*/
public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
- /** {@hide} The audio output device code for a USB audio accessory. The accessory is in USB host
+ /** @hide
+ * The audio output device code for a USB audio accessory. The accessory is in USB host
* mode and the Android device in USB device mode
*/
public static final int DEVICE_OUT_USB_ACCESSORY = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
- /** {@hide} The audio output device code for a USB audio device. The device is in USB device
+ /** @hide
+ * The audio output device code for a USB audio device. The device is in USB device
* mode and the Android device in USB host mode
*/
public static final int DEVICE_OUT_USB_DEVICE = AudioSystem.DEVICE_OUT_USB_DEVICE;
- /** {@hide} This is not used as a returned value from {@link #getDevicesForStream}, but could be
+ /** @hide
+ * The audio output device code for projection output.
+ */
+ public static final int DEVICE_OUT_REMOTE_SUBMIX = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ /** @hide
+ * The audio output device code the telephony voice TX path.
+ */
+ public static final int DEVICE_OUT_TELEPHONY_TX = AudioSystem.DEVICE_OUT_TELEPHONY_TX;
+ /** @hide
+ * The audio output device code for an analog jack with line impedance detected.
+ */
+ public static final int DEVICE_OUT_LINE = AudioSystem.DEVICE_OUT_LINE;
+ /** @hide
+ * The audio output device code for HDMI Audio Return Channel.
+ */
+ public static final int DEVICE_OUT_HDMI_ARC = AudioSystem.DEVICE_OUT_HDMI_ARC;
+ /** @hide
+ * The audio output device code for S/PDIF digital connection.
+ */
+ public static final int DEVICE_OUT_SPDIF = AudioSystem.DEVICE_OUT_SPDIF;
+ /** @hide
+ * The audio output device code for built-in FM transmitter.
+ */
+ public static final int DEVICE_OUT_FM = AudioSystem.DEVICE_OUT_FM;
+ /** @hide
+ * This is not used as a returned value from {@link #getDevicesForStream}, but could be
* used in the future in a set method to select whatever default device is chosen by the
* platform-specific implementation.
*/
public static final int DEVICE_OUT_DEFAULT = AudioSystem.DEVICE_OUT_DEFAULT;
+ /** @hide
+ * The audio input device code for default built-in microphone
+ */
+ public static final int DEVICE_IN_BUILTIN_MIC = AudioSystem.DEVICE_IN_BUILTIN_MIC;
+ /** @hide
+ * The audio input device code for a Bluetooth SCO headset
+ */
+ public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET =
+ AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ /** @hide
+ * The audio input device code for wired headset microphone
+ */
+ public static final int DEVICE_IN_WIRED_HEADSET =
+ AudioSystem.DEVICE_IN_WIRED_HEADSET;
+ /** @hide
+ * The audio input device code for HDMI
+ */
+ public static final int DEVICE_IN_HDMI =
+ AudioSystem.DEVICE_IN_HDMI;
+ /** @hide
+ * The audio input device code for telephony voice RX path
+ */
+ public static final int DEVICE_IN_TELEPHONY_RX =
+ AudioSystem.DEVICE_IN_TELEPHONY_RX;
+ /** @hide
+ * The audio input device code for built-in microphone pointing to the back
+ */
+ public static final int DEVICE_IN_BACK_MIC =
+ AudioSystem.DEVICE_IN_BACK_MIC;
+ /** @hide
+ * The audio input device code for analog from a docking station
+ */
+ public static final int DEVICE_IN_ANLG_DOCK_HEADSET =
+ AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET;
+ /** @hide
+ * The audio input device code for digital from a docking station
+ */
+ public static final int DEVICE_IN_DGTL_DOCK_HEADSET =
+ AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET;
+ /** @hide
+ * The audio input device code for a USB audio accessory. The accessory is in USB host
+ * mode and the Android device in USB device mode
+ */
+ public static final int DEVICE_IN_USB_ACCESSORY =
+ AudioSystem.DEVICE_IN_USB_ACCESSORY;
+ /** @hide
+ * The audio input device code for a USB audio device. The device is in USB device
+ * mode and the Android device in USB host mode
+ */
+ public static final int DEVICE_IN_USB_DEVICE =
+ AudioSystem.DEVICE_IN_USB_DEVICE;
+ /** @hide
+ * The audio input device code for a FM radio tuner
+ */
+ public static final int DEVICE_IN_FM_TUNER = AudioSystem.DEVICE_IN_FM_TUNER;
+ /** @hide
+ * The audio input device code for a TV tuner
+ */
+ public static final int DEVICE_IN_TV_TUNER = AudioSystem.DEVICE_IN_TV_TUNER;
+ /** @hide
+ * The audio input device code for an analog jack with line impedance detected
+ */
+ public static final int DEVICE_IN_LINE = AudioSystem.DEVICE_IN_LINE;
+ /** @hide
+ * The audio input device code for a S/PDIF digital connection
+ */
+ public static final int DEVICE_IN_SPDIF = AudioSystem.DEVICE_IN_SPDIF;
+
+ /**
+ * Return true if the device code corresponds to an output device.
+ * @hide
+ */
+ public static boolean isOutputDevice(int device)
+ {
+ return (device & AudioSystem.DEVICE_BIT_IN) == 0;
+ }
+
+ /**
+ * Return true if the device code corresponds to an input device.
+ * @hide
+ */
+ public static boolean isInputDevice(int device)
+ {
+ return (device & AudioSystem.DEVICE_BIT_IN) == AudioSystem.DEVICE_BIT_IN;
+ }
+
+
/**
* Return the enabled devices for the specified output stream type.
*
@@ -2635,9 +2766,17 @@ public class AudioManager {
* {@link #DEVICE_OUT_BLUETOOTH_A2DP},
* {@link #DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES},
* {@link #DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER},
- * {@link #DEVICE_OUT_AUX_DIGITAL},
+ * {@link #DEVICE_OUT_HDMI},
* {@link #DEVICE_OUT_ANLG_DOCK_HEADSET},
* {@link #DEVICE_OUT_DGTL_DOCK_HEADSET}.
+ * {@link #DEVICE_OUT_USB_ACCESSORY}.
+ * {@link #DEVICE_OUT_USB_DEVICE}.
+ * {@link #DEVICE_OUT_REMOTE_SUBMIX}.
+ * {@link #DEVICE_OUT_TELEPHONY_TX}.
+ * {@link #DEVICE_OUT_LINE}.
+ * {@link #DEVICE_OUT_HDMI_ARC}.
+ * {@link #DEVICE_OUT_SPDIF}.
+ * {@link #DEVICE_OUT_FM}.
* {@link #DEVICE_OUT_DEFAULT} is not used here.
*
* The implementation may support additional device codes beyond those listed, so
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index bb8cfa6..6e623a5 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -423,7 +423,7 @@ public class AudioService extends IAudioService.Stub {
public final static int STREAM_REMOTE_MUSIC = -200;
// Devices for which the volume is fixed and VolumePanel slider should be disabled
- final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_ALL_USB;
@@ -2895,7 +2895,7 @@ public class AudioService extends IAudioService.Stub {
public String getSettingNameForDevice(int device) {
String name = mVolumeIndexSettingName;
- String suffix = AudioSystem.getDeviceName(device);
+ String suffix = AudioSystem.getOutputDeviceName(device);
if (suffix.isEmpty()) {
return name;
}
@@ -3935,7 +3935,7 @@ public class AudioService extends IAudioService.Stub {
// sent if none of these devices is connected.
int mBecomingNoisyIntentDevices =
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
- AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_ALL_USB;
@@ -3992,7 +3992,7 @@ public class AudioService extends IAudioService.Stub {
} else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
- } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
+ } else if (device == AudioSystem.DEVICE_OUT_HDMI) {
connType = AudioRoutesInfo.MAIN_HDMI;
intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 5ddb198..0c45443 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -234,11 +234,17 @@ public class AudioSystem
public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+ public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL;
public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
+ public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000;
+ public static final int DEVICE_OUT_LINE = 0x20000;
+ public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
+ public static final int DEVICE_OUT_SPDIF = 0x80000;
+ public static final int DEVICE_OUT_FM = 0x100000;
public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
@@ -252,12 +258,17 @@ public class AudioSystem
DEVICE_OUT_BLUETOOTH_A2DP |
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
- DEVICE_OUT_AUX_DIGITAL |
+ DEVICE_OUT_HDMI |
DEVICE_OUT_ANLG_DOCK_HEADSET |
DEVICE_OUT_DGTL_DOCK_HEADSET |
DEVICE_OUT_USB_ACCESSORY |
DEVICE_OUT_USB_DEVICE |
DEVICE_OUT_REMOTE_SUBMIX |
+ DEVICE_OUT_TELEPHONY_TX |
+ DEVICE_OUT_LINE |
+ DEVICE_OUT_HDMI_ARC |
+ DEVICE_OUT_SPDIF |
+ DEVICE_OUT_FM |
DEVICE_OUT_DEFAULT);
public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
@@ -275,13 +286,20 @@ public class AudioSystem
public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = DEVICE_BIT_IN | 0x8;
public static final int DEVICE_IN_WIRED_HEADSET = DEVICE_BIT_IN | 0x10;
public static final int DEVICE_IN_AUX_DIGITAL = DEVICE_BIT_IN | 0x20;
+ public static final int DEVICE_IN_HDMI = DEVICE_IN_AUX_DIGITAL;
public static final int DEVICE_IN_VOICE_CALL = DEVICE_BIT_IN | 0x40;
+ public static final int DEVICE_IN_TELEPHONY_RX = DEVICE_IN_VOICE_CALL;
public static final int DEVICE_IN_BACK_MIC = DEVICE_BIT_IN | 0x80;
public static final int DEVICE_IN_REMOTE_SUBMIX = DEVICE_BIT_IN | 0x100;
public static final int DEVICE_IN_ANLG_DOCK_HEADSET = DEVICE_BIT_IN | 0x200;
public static final int DEVICE_IN_DGTL_DOCK_HEADSET = DEVICE_BIT_IN | 0x400;
public static final int DEVICE_IN_USB_ACCESSORY = DEVICE_BIT_IN | 0x800;
public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000;
+ public static final int DEVICE_IN_FM_TUNER = DEVICE_BIT_IN | 0x2000;
+ public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000;
+ public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000;
+ public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000;
+
public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION |
@@ -289,14 +307,18 @@ public class AudioSystem
DEVICE_IN_BUILTIN_MIC |
DEVICE_IN_BLUETOOTH_SCO_HEADSET |
DEVICE_IN_WIRED_HEADSET |
- DEVICE_IN_AUX_DIGITAL |
- DEVICE_IN_VOICE_CALL |
+ DEVICE_IN_HDMI |
+ DEVICE_IN_TELEPHONY_RX |
DEVICE_IN_BACK_MIC |
DEVICE_IN_REMOTE_SUBMIX |
DEVICE_IN_ANLG_DOCK_HEADSET |
DEVICE_IN_DGTL_DOCK_HEADSET |
DEVICE_IN_USB_ACCESSORY |
DEVICE_IN_USB_DEVICE |
+ DEVICE_IN_FM_TUNER |
+ DEVICE_IN_TV_TUNER |
+ DEVICE_IN_LINE |
+ DEVICE_IN_SPDIF |
DEVICE_IN_DEFAULT);
public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY |
@@ -318,13 +340,19 @@ public class AudioSystem
public static final String DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME = "bt_a2dp_hp";
public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
+ public static final String DEVICE_OUT_HDMI_NAME = "hdmi";
public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
+ public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx";
+ public static final String DEVICE_OUT_LINE_NAME = "line";
+ public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc";
+ public static final String DEVICE_OUT_SPDIF_NAME = "spdif";
+ public static final String DEVICE_OUT_FM_NAME = "fm_transmitter";
- public static String getDeviceName(int device)
+ public static String getOutputDeviceName(int device)
{
switch(device) {
case DEVICE_OUT_EARPIECE:
@@ -347,8 +375,8 @@ public class AudioSystem
return DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME;
case DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
return DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME;
- case DEVICE_OUT_AUX_DIGITAL:
- return DEVICE_OUT_AUX_DIGITAL_NAME;
+ case DEVICE_OUT_HDMI:
+ return DEVICE_OUT_HDMI_NAME;
case DEVICE_OUT_ANLG_DOCK_HEADSET:
return DEVICE_OUT_ANLG_DOCK_HEADSET_NAME;
case DEVICE_OUT_DGTL_DOCK_HEADSET:
@@ -359,12 +387,23 @@ public class AudioSystem
return DEVICE_OUT_USB_DEVICE_NAME;
case DEVICE_OUT_REMOTE_SUBMIX:
return DEVICE_OUT_REMOTE_SUBMIX_NAME;
+ case DEVICE_OUT_TELEPHONY_TX:
+ return DEVICE_OUT_TELEPHONY_TX_NAME;
+ case DEVICE_OUT_LINE:
+ return DEVICE_OUT_LINE_NAME;
+ case DEVICE_OUT_HDMI_ARC:
+ return DEVICE_OUT_HDMI_ARC_NAME;
+ case DEVICE_OUT_SPDIF:
+ return DEVICE_OUT_SPDIF_NAME;
+ case DEVICE_OUT_FM:
+ return DEVICE_OUT_FM_NAME;
case DEVICE_OUT_DEFAULT:
default:
return "";
}
}
+
// phone state, match audio_mode???
public static final int PHONE_STATE_OFFCALL = 0;
public static final int PHONE_STATE_RINGING = 1;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 34c5520..c7b3fc9 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -212,6 +212,7 @@ final public class MediaCodec {
* <li>"video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm)
* <li>"video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm)
* <li>"video/avc" - H.264/AVC video
+ * <li>"video/hevc" - H.265/HEVC video
* <li>"video/mp4v-es" - MPEG4 video
* <li>"video/3gpp" - H.263 video
* <li>"audio/3gpp" - AMR narrowband audio
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 90c12c6..b5d0a57 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -264,6 +264,37 @@ public final class MediaCodecInfo {
// from OMX_VIDEO_VP8PROFILETYPE
public static final int VP8ProfileMain = 0x01;
+ // from OMX_VIDEO_HEVCPROFILETYPE
+ public static final int HEVCProfileMain = 0x01;
+ public static final int HEVCProfileMain10 = 0x02;
+
+ // from OMX_VIDEO_HEVCLEVELTYPE
+ public static final int HEVCMainTierLevel1 = 0x1;
+ public static final int HEVCHighTierLevel1 = 0x2;
+ public static final int HEVCMainTierLevel2 = 0x4;
+ public static final int HEVCHighTierLevel2 = 0x8;
+ public static final int HEVCMainTierLevel21 = 0x10;
+ public static final int HEVCHighTierLevel21 = 0x20;
+ public static final int HEVCMainTierLevel3 = 0x40;
+ public static final int HEVCHighTierLevel3 = 0x80;
+ public static final int HEVCMainTierLevel31 = 0x100;
+ public static final int HEVCHighTierLevel31 = 0x200;
+ public static final int HEVCMainTierLevel4 = 0x400;
+ public static final int HEVCHighTierLevel4 = 0x800;
+ public static final int HEVCMainTierLevel41 = 0x1000;
+ public static final int HEVCHighTierLevel41 = 0x2000;
+ public static final int HEVCMainTierLevel5 = 0x4000;
+ public static final int HEVCHighTierLevel5 = 0x8000;
+ public static final int HEVCMainTierLevel51 = 0x10000;
+ public static final int HEVCHighTierLevel51 = 0x20000;
+ public static final int HEVCMainTierLevel52 = 0x40000;
+ public static final int HEVCHighTierLevel52 = 0x80000;
+ public static final int HEVCMainTierLevel6 = 0x100000;
+ public static final int HEVCHighTierLevel6 = 0x200000;
+ public static final int HEVCMainTierLevel61 = 0x400000;
+ public static final int HEVCHighTierLevel61 = 0x800000;
+ public static final int HEVCMainTierLevel62 = 0x1000000;
+ public static final int HEVCHighTierLevel62 = 0x2000000;
/**
* Defined in the OpenMAX IL specs, depending on the type of media
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 37f45c2..26ae3cc 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -407,6 +407,19 @@ public class RemoteControlClient
}
/**
+ * Get a {@link MediaSession} associated with this RCC. It will only have a
+ * session while it is registered with
+ * {@link AudioManager#registerRemoteControlClient}. The session returned
+ * should not be modified directly by the application but may be used with
+ * other APIs that require a session.
+ *
+ * @return A media session object or null.
+ */
+ public MediaSession getMediaSession() {
+ return mSession;
+ }
+
+ /**
* Class used to modify metadata in a {@link RemoteControlClient} object.
* Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor,
* on which you set the metadata for the RemoteControlClient instance. Once all the information
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 84028b7..d21b442 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -351,7 +351,7 @@ android_media_MediaScanner_extractAlbumArt(
if (!data) {
return NULL;
}
- long len = *((long*)data);
+ jsize len = *((uint32_t*)data);
jbyteArray array = env->NewByteArray(len);
if (array != NULL) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 2d17b7b..aae92e8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -149,14 +149,16 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
if (mLockPatternUtils.checkPassword(entry)) {
mCallback.reportUnlockAttempt(true);
mCallback.dismiss(true);
- } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- mCallback.reportUnlockAttempt(false);
- int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
- if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
- handleAttemptLockout(deadline);
+ } else {
+ if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mCallback.reportUnlockAttempt(false);
+ int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
+ if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ }
}
mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 909c32e..286921e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 102;
+ private static final int DATABASE_VERSION = 103;
private Context mContext;
private int mUserHandle;
@@ -1632,6 +1632,34 @@ public class DatabaseHelper extends SQLiteOpenHelper {
upgradeVersion = 102;
}
+ if (upgradeVersion == 102) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ // The INSTALL_NON_MARKET_APPS setting is becoming per-user rather
+ // than device-global.
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ // In the owner user, the global table exists so we can migrate the
+ // entry from there to the secure table, preserving its value.
+ String[] globalToSecure = {
+ Settings.Secure.INSTALL_NON_MARKET_APPS
+ };
+ moveSettingsToNewTable(db, TABLE_GLOBAL, TABLE_SECURE, globalToSecure, true);
+ } else {
+ // Secondary users' dbs don't have the global table, so institute the
+ // default.
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
+ R.bool.def_install_non_market_apps);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 103;
+ }
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -2191,6 +2219,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadStringSetting(stmt, Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
R.string.def_immersive_mode_confirmations);
+ loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
+ R.bool.def_install_non_market_apps);
+
} finally {
if (stmt != null) stmt.close();
}
@@ -2289,9 +2320,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.Global.NETSTATS_ENABLED,
R.bool.def_netstats_enabled);
- loadBooleanSetting(stmt, Settings.Global.INSTALL_NON_MARKET_APPS,
- R.bool.def_install_non_market_apps);
-
loadBooleanSetting(stmt, Settings.Global.USB_MASS_STORAGE_ENABLED,
R.bool.def_usb_mass_storage_enabled);
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
index 744795e..9c1165d 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:height="16dp"
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
index 96bfbe1..a8afeb3 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_light.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:height="16dp"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index b24d4ad..85de645 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -23,6 +23,7 @@
android:background="@drawable/qs_panel_background" >
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
+ android:background="#0000"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index a68ad2f..97051ff 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -22,21 +22,6 @@
android:id="@+id/task_view_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.systemui.recents.views.TaskInfoView
- android:id="@+id/task_view_info_pane"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible"
- android:background="@color/recents_task_bar_default_background_color">
- <Button
- android:id="@+id/task_view_app_info_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="20dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="top|center_horizontal"
- android:text="@string/recents_app_info_button_label" />
- </com.android.systemui.recents.views.TaskInfoView>
<com.android.systemui.recents.views.TaskBarView
android:id="@+id/task_view_bar"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2837cd6..5fabd3e 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -27,7 +27,7 @@
<Button
android:id="@+id/veto"
android:layout_width="48dp"
- android:layout_height="match_parent"
+ android:layout_height="0dp"
android:gravity="end"
android:layout_marginEnd="-80dp"
android:background="@null"
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 79545b3..fcbd0f4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -31,8 +31,6 @@ public class Constants {
public static final boolean EnableTaskStackClipping = false;
// Enables the use of theme colors as the task bar background
public static final boolean EnableTaskBarThemeColors = true;
- // Enables the info pane on long-pressing the task
- public static final boolean EnableInfoPane = false;
// Enables app-info pane on long-pressing the icon
public static final boolean EnableDevAppInfoOnLongPress = true;
// Enables the search bar layout
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index bae8a99..de696db 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -394,16 +394,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
public void onBackPressed() {
boolean interceptedByInfoPanelClose = false;
- // Try and return from any open info panes
- if (Constants.DebugFlags.App.EnableInfoPane) {
- interceptedByInfoPanelClose = mRecentsView.closeOpenInfoPanes();
- }
-
- // If we haven't been intercepted already, then unfilter any stacks
- if (!interceptedByInfoPanelClose) {
- if (!mRecentsView.unfilterFilteredStacks()) {
- super.onBackPressed();
- }
+ // Unfilter any stacks
+ if (!mRecentsView.unfilterFilteredStacks()) {
+ super.onBackPressed();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c63e688..cad54fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -301,24 +301,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return insets.consumeSystemWindowInsets(false, false, false, true);
}
- /** Closes any open info panes */
- public boolean closeOpenInfoPanes() {
- if (mBSP != null) {
- // Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child instanceof TaskStackView) {
- TaskStackView stackView = (TaskStackView) child;
- if (stackView.closeOpenInfoPanes()) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
/** Unfilters any filtered stacks */
public boolean unfilterFilteredStacks() {
if (mBSP != null) {
@@ -346,9 +328,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mCb.onTaskLaunching();
}
- // Close any open info panes
- closeOpenInfoPanes();
-
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
deleted file mode 100644
index f1c362a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ /dev/null
@@ -1,185 +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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.TouchFeedbackDrawable;
-import android.util.AttributeSet;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
-import com.android.systemui.recents.model.Task;
-
-
-/* The task info view */
-class TaskInfoView extends FrameLayout {
-
- Button mAppInfoButton;
-
- // Circular clip animation
- boolean mCircularClipEnabled;
- Path mClipPath = new Path();
- float mClipRadius;
- float mMaxClipRadius;
- Point mClipOrigin = new Point();
- ObjectAnimator mCircularClipAnimator;
-
- public TaskInfoView(Context context) {
- this(context, null);
- }
-
- public TaskInfoView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskInfoView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskInfoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onFinishInflate() {
- // Initialize the buttons on the info panel
- mAppInfoButton = (Button) findViewById(R.id.task_view_app_info_button);
- }
-
- /** Updates the positions of each of the items to fit in the rect specified */
- void updateContents(Rect visibleRect) {
- // Offset the app info button
- mAppInfoButton.setTranslationY(visibleRect.top +
- (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2);
- }
-
- /** Sets the circular clip radius on this panel */
- public void setCircularClipRadius(float r) {
- mClipRadius = r;
- invalidate();
- }
-
- /** Gets the circular clip radius on this panel */
- public float getCircularClipRadius() {
- return mClipRadius;
- }
-
- /** Animates the circular clip radius on the icon */
- void animateCircularClip(Point o, float fromRadius, float toRadius,
- final Runnable postRunnable, boolean animateInContent) {
- if (mCircularClipAnimator != null) {
- mCircularClipAnimator.cancel();
- }
-
- // Calculate the max clip radius to each of the corners
- int w = getMeasuredWidth() - o.x;
- int h = getMeasuredHeight() - o.y;
- // origin to tl, tr, br, bl
- mMaxClipRadius = (int) Math.ceil(Math.sqrt(o.x * o.x + o.y * o.y));
- mMaxClipRadius = (int) Math.max(mMaxClipRadius, Math.ceil(Math.sqrt(w * w + o.y * o.y)));
- mMaxClipRadius = (int) Math.max(mMaxClipRadius, Math.ceil(Math.sqrt(w * w + h * h)));
- mMaxClipRadius = (int) Math.max(mMaxClipRadius, Math.ceil(Math.sqrt(o.x * o.x + h * h)));
-
- mClipOrigin.set(o.x, o.y);
- mClipRadius = fromRadius;
- int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius);
- mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius);
- mCircularClipAnimator.setDuration(duration);
- mCircularClipAnimator.setInterpolator(
- RecentsConfiguration.getInstance().defaultBezierInterpolator);
- mCircularClipAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCircularClipEnabled = false;
- if (postRunnable != null) {
- postRunnable.run();
- }
- }
- });
- mCircularClipAnimator.start();
- mCircularClipEnabled = true;
-
- if (animateInContent) {
- animateAppInfoButtonIn(duration);
- }
- }
-
- /** Cancels the circular clip animation. */
- void cancelCircularClipAnimation() {
- if (mCircularClipAnimator != null) {
- mCircularClipAnimator.cancel();
- }
- }
-
- void animateAppInfoButtonIn(int duration) {
- mAppInfoButton.setScaleX(0.75f);
- mAppInfoButton.setScaleY(0.75f);
- mAppInfoButton.animate()
- .scaleX(1f)
- .scaleY(1f)
- .setDuration(duration)
- .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator)
- .withLayer()
- .start();
- }
-
- /** Binds the info view to the task */
- void rebindToTask(Task t, boolean animate) {
- RecentsConfiguration configuration = RecentsConfiguration.getInstance();
- if (Constants.DebugFlags.App.EnableTaskBarThemeColors && t.colorPrimary != 0) {
- setBackgroundColor(t.colorPrimary);
- // Workaround: The button currently doesn't support setting a custom background tint
- // not defined in the theme. Just lower the alpha on the button to make it blend more
- // into the background.
- if (mAppInfoButton.getBackground() instanceof TouchFeedbackDrawable) {
- TouchFeedbackDrawable d = (TouchFeedbackDrawable) mAppInfoButton.getBackground();
- if (d != null) {
- d.setAlpha(96);
- }
- }
- } else {
- setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- int saveCount = 0;
- if (mCircularClipEnabled) {
- saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- mClipPath.reset();
- mClipPath.addCircle(mClipOrigin.x, mClipOrigin.y, mClipRadius * mMaxClipRadius,
- Path.Direction.CW);
- canvas.clipPath(mClipPath);
- }
- super.draw(canvas);
- if (mCircularClipEnabled) {
- canvas.restoreToCount(saveCount);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 37c3c35..0687222 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -54,7 +54,7 @@ import java.util.Set;
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
- View.OnClickListener, View.OnLongClickListener, RecentsPackageMonitor.PackageCallbacks {
+ View.OnClickListener, RecentsPackageMonitor.PackageCallbacks {
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
@@ -79,7 +79,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int mMinScroll;
int mMaxScroll;
int mStashedScroll;
- int mLastInfoPaneStackScroll;
int mFocusedTaskIndex = -1;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
@@ -290,17 +289,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void setStackScroll(int value) {
mStackScroll = value;
requestSynchronizeStackViewsWithModel();
-
- // Close any open info panes if the user has scrolled away from them
- boolean isAnimatingScroll = (mScrollAnimator != null && mScrollAnimator.isRunning());
- if (mLastInfoPaneStackScroll > -1 && !isAnimatingScroll) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (Math.abs(mStackScroll - mLastInfoPaneStackScroll) >
- config.taskStackScrollDismissInfoPaneDistance) {
- // Close any open info panes
- closeOpenInfoPanes();
- }
- }
}
/** Sets the current stack scroll without synchronizing the stack view with the model */
public void setStackScrollRaw(int value) {
@@ -455,21 +443,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
- /** Closes any open info panes. */
- boolean closeOpenInfoPanes() {
- if (!Constants.DebugFlags.App.EnableInfoPane) return false;
-
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- if (tv.isInfoPaneVisible()) {
- tv.hideInfoPane();
- return true;
- }
- }
- return false;
- }
-
/** Focuses the task at the specified index in the stack */
void focusTask(int taskIndex, boolean scrollToNewPosition) {
Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "" + taskIndex);
@@ -949,9 +922,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
Task filteredTask) {
- // Close any open info panes
- closeOpenInfoPanes();
-
// Stash the scroll and filtered task for us to restore to when we unfilter
mStashedScroll = getStackScroll();
@@ -976,9 +946,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
- // Close any open info panes
- closeOpenInfoPanes();
-
// Calculate the current task transforms
final ArrayList<TaskViewTransform> curTaskTransforms =
getStackTransforms(curTasks, getStackScroll(), null, true);
@@ -1058,9 +1025,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Set the callbacks and listeners for this new view
tv.setOnClickListener(this);
- if (Constants.DebugFlags.App.EnableInfoPane) {
- tv.setOnLongClickListener(this);
- }
tv.setCallbacks(this);
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
@@ -1094,17 +1058,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
@Override
- public void onTaskInfoPanelShown(TaskView tv) {
- // Do nothing
- }
-
- @Override
- public void onTaskInfoPanelHidden(TaskView tv) {
- // Unset the saved scroll
- mLastInfoPaneStackScroll = -1;
- }
-
- @Override
public void onTaskAppInfoClicked(TaskView tv) {
if (mCb != null) {
mCb.onTaskAppInfoLaunched(tv.getTask());
@@ -1129,52 +1082,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
task + " cb: " + mCb);
- // Close any open info panes if the user taps on another task
- if (closeOpenInfoPanes()) {
- return;
- }
-
if (mCb != null) {
mCb.onTaskLaunched(this, tv, mStack, task);
}
}
- @Override
- public boolean onLongClick(View v) {
- if (!Constants.DebugFlags.App.EnableInfoPane) return false;
-
- TaskView tv = (TaskView) v;
-
- // Close any other task info panels if we launch another info pane
- closeOpenInfoPanes();
-
- // Scroll the task view so that it is maximally visible
- float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height();
- int taskIndex = mStack.indexOfTask(tv.getTask());
- int curScroll = getStackScroll();
- int newScroll = (int) Math.max(mMinScroll, Math.min(mMaxScroll, taskIndex * overlapHeight));
- TaskViewTransform transform = getStackTransform(taskIndex, curScroll);
- Rect nonOverlapRect = new Rect(transform.rect);
- if (taskIndex < (mStack.getTaskCount() - 1)) {
- nonOverlapRect.bottom = nonOverlapRect.top + (int) overlapHeight;
- }
-
- // XXX: Use HW Layers
- if (transform.t < 0f) {
- animateScroll(curScroll, newScroll, null);
- } else if (nonOverlapRect.bottom > mStackRectSansPeek.bottom) {
- // Check if we are out of bounds, if so, just scroll it in such that the bottom of the
- // task view is visible
- newScroll = curScroll - (mStackRectSansPeek.bottom - nonOverlapRect.bottom);
- animateScroll(curScroll, newScroll, null);
- }
- mLastInfoPaneStackScroll = newScroll;
-
- // Show the info pane for this task view
- tv.showInfoPane(new Rect(0, 0, 0, (int) overlapHeight));
- return true;
- }
-
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
@@ -1550,13 +1462,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // If the info panel is currently showing on this view, then we need to dismiss it
- if (Constants.DebugFlags.App.EnableInfoPane) {
- TaskView tv = (TaskView) v;
- if (tv.isInfoPaneVisible()) {
- tv.hideInfoPane();
- }
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 46af4c1..780f274 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -44,8 +44,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** The TaskView callbacks */
interface TaskViewCallbacks {
public void onTaskIconClicked(TaskView tv);
- public void onTaskInfoPanelShown(TaskView tv);
- public void onTaskInfoPanelHidden(TaskView tv);
public void onTaskAppInfoClicked(TaskView tv);
public void onTaskDismissed(TaskView tv);
@@ -58,14 +56,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
Task mTask;
boolean mTaskDataLoaded;
- boolean mTaskInfoPaneVisible;
boolean mIsFocused;
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
- TaskInfoView mInfoView;
TaskViewCallbacks mCb;
@@ -94,7 +90,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
// Bind the views
mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
- mInfoView = (TaskInfoView) findViewById(R.id.task_view_info_pane);
if (mTaskDataLoaded) {
onTaskDataLoaded(false);
@@ -280,63 +275,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
return outRect;
}
- /** Returns whether this task has an info pane visible */
- boolean isInfoPaneVisible() {
- return mTaskInfoPaneVisible;
- }
-
- /** Shows the info pane if it is not visible. */
- void showInfoPane(Rect taskVisibleRect) {
- if (mTaskInfoPaneVisible) return;
-
- // Remove the bar view from the visible rect and update the info pane contents
- taskVisibleRect.top += mBarView.getMeasuredHeight();
- mInfoView.updateContents(taskVisibleRect);
-
- // Show the info pane and animate it into view
- mInfoView.setVisibility(View.VISIBLE);
- mInfoView.animateCircularClip(mLastTouchDown, 0f, 1f, null, true);
- mInfoView.setOnClickListener(this);
- mTaskInfoPaneVisible = true;
-
- // Notify any callbacks
- if (mCb != null) {
- mCb.onTaskInfoPanelShown(this);
- }
- }
-
- /** Hides the info pane if it is visible. */
- void hideInfoPane() {
- if (!mTaskInfoPaneVisible) return;
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
- // Cancel any circular clip animation
- mInfoView.cancelCircularClipAnimation();
-
- // Animate the info pane out
- mInfoView.animate()
- .alpha(0f)
- .setDuration(config.taskViewInfoPaneAnimDuration)
- .setInterpolator(config.defaultBezierInterpolator)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mInfoView.setVisibility(View.INVISIBLE);
- mInfoView.setOnClickListener(null);
-
- mInfoView.setAlpha(1f);
- }
- })
- .start();
- mTaskInfoPaneVisible = false;
-
- // Notify any callbacks
- if (mCb != null) {
- mCb.onTaskInfoPanelHidden(this);
- }
- }
-
/** Enable the hw layers on this task view */
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
@@ -408,11 +346,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
@Override
public void onTaskDataLoaded(boolean reloadingTaskData) {
- if (mThumbnailView != null && mBarView != null && mInfoView != null) {
+ if (mThumbnailView != null && mBarView != null) {
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(mTask, reloadingTaskData);
mBarView.rebindToTask(mTask, reloadingTaskData);
- mInfoView.rebindToTask(mTask, reloadingTaskData);
// Rebind any listeners
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
@@ -424,16 +361,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
mBarView.mApplicationIcon.setOnLongClickListener(this);
}
}
- if (Constants.DebugFlags.App.EnableInfoPane) {
- mInfoView.mAppInfoButton.setOnClickListener(this);
- }
}
mTaskDataLoaded = true;
}
@Override
public void onTaskDataUnloaded() {
- if (mThumbnailView != null && mBarView != null && mInfoView != null) {
+ if (mThumbnailView != null && mBarView != null) {
// Unbind each of the views from the task data and remove the task callback
mTask.setCallbacks(null);
mThumbnailView.unbindFromTask();
@@ -444,18 +378,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mBarView.mApplicationIcon.setOnLongClickListener(null);
}
- if (Constants.DebugFlags.App.EnableInfoPane) {
- mInfoView.mAppInfoButton.setOnClickListener(null);
- }
}
mTaskDataLoaded = false;
}
@Override
public void onClick(View v) {
- if (v == mInfoView) {
- hideInfoPane();
- } else if (v == mBarView.mApplicationIcon) {
+ if (v == mBarView.mApplicationIcon) {
mCb.onTaskIconClicked(this);
} else if (v == mBarView.mDismissButton) {
// Animate out the view and call the callback
@@ -466,8 +395,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
mCb.onTaskDismissed(tv);
}
});
- } else if (v == mInfoView.mAppInfoButton) {
- mCb.onTaskAppInfoClicked(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 724b6a4..ac16164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -89,7 +89,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
super.onFinishInflate();
mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
- updateBackgroundResource();
+ updateBackgroundResources();
}
private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -215,9 +215,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (mDimmed != dimmed) {
mDimmed = dimmed;
if (fade) {
- fadeBackgroundResource();
+ fadeBackground();
} else {
- updateBackgroundResource();
+ updateBackground();
}
}
}
@@ -233,14 +233,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBgTint = bgTint;
mDimmedBgResId = dimmedBgResId;
mDimmedBgTint = dimmedTint;
- updateBackgroundResource();
+ updateBackgroundResources();
}
public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) {
setBackgroundResourceIds(bgResId, 0, dimmedBgResId, 0);
}
- private void fadeBackgroundResource() {
+ private void fadeBackground() {
if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
} else {
@@ -256,7 +256,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBackgroundAnimator.removeAllListeners();
mBackgroundAnimator.cancel();
if (duration <= 0) {
- updateBackgroundResource();
+ updateBackground();
return;
}
}
@@ -279,19 +279,22 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBackgroundAnimator.start();
}
- private void updateBackgroundResource() {
+ private void updateBackground() {
if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
- mBackgroundDimmed.setCustomBackground(mDimmedBgResId, mDimmedBgTint);
mBackgroundNormal.setVisibility(View.INVISIBLE);
} else {
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
- mBackgroundNormal.setCustomBackground(mBgResId, mBgTint);
mBackgroundNormal.setAlpha(1f);
}
}
+ private void updateBackgroundResources() {
+ mBackgroundDimmed.setCustomBackground(mDimmedBgResId, mDimmedBgTint);
+ mBackgroundNormal.setCustomBackground(mBgResId, mBgTint);
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index eb4e77a..e699dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -619,7 +619,6 @@ public abstract class BaseStatusBar extends SystemUI implements
protected void hideRecents(boolean triggeredFromAltTab) {
if (mRecents != null) {
- sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
mRecents.hideRecents(triggeredFromAltTab);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index f6c80fc..84005d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -53,6 +53,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private NotificationContentView mPrivateLayout;
private int mMaxExpandHeight;
private boolean mIsBelowSpeedBump;
+ private View mVetoButton;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -63,6 +64,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
super.onFinishInflate();
mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
+ mVetoButton = findViewById(R.id.veto);
}
@Override
@@ -224,7 +226,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public void setActualHeight(int height, boolean notifyListeners) {
- mPrivateLayout.setActualHeight(height, notifyListeners);
+ mPrivateLayout.setActualHeight(height);
invalidate();
super.setActualHeight(height, notifyListeners);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 061396d..eaaac10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -20,29 +20,78 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
/**
* An abstract view for expandable views.
*/
public abstract class ExpandableView extends FrameLayout {
+ private final int mMaxNotificationHeight;
+
private OnHeightChangedListener mOnHeightChangedListener;
protected int mActualHeight;
protected int mClipTopAmount;
private boolean mActualHeightInitialized;
+ private ArrayList<View> mMatchParentViews = new ArrayList<View>();
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mMaxNotificationHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_max_height);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int ownMaxHeight = mMaxNotificationHeight;
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+ boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+ if (hasFixedHeight || isHeightLimited) {
+ int size = MeasureSpec.getSize(heightMeasureSpec);
+ ownMaxHeight = Math.min(ownMaxHeight, size);
+ }
+ int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
+ int maxChildHeight = 0;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ int childHeightSpec = newHeightSpec;
+ ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
+ if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
+ if (layoutParams.height >= 0) {
+ // An actual height is set
+ childHeightSpec = layoutParams.height > ownMaxHeight
+ ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
+ : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
+ }
+ child.measure(widthMeasureSpec, childHeightSpec);
+ int childHeight = child.getMeasuredHeight();
+ maxChildHeight = Math.max(maxChildHeight, childHeight);
+ } else {
+ mMatchParentViews.add(child);
+ }
+ }
+ int ownHeight = hasFixedHeight ? ownMaxHeight : maxChildHeight;
+ newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
+ for (View child : mMatchParentViews) {
+ child.measure(widthMeasureSpec, newHeightSpec);
+ }
+ mMatchParentViews.clear();
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ setMeasuredDimension(width, ownHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!mActualHeightInitialized && mActualHeight == 0) {
- mActualHeight = getInitialHeight();
+ setActualHeight(getInitialHeight());
}
- mActualHeightInitialized = true;
}
protected int getInitialHeight() {
@@ -77,6 +126,7 @@ public abstract class ExpandableView extends FrameLayout {
}
public void setActualHeight(int actualHeight) {
+ mActualHeightInitialized = true;
setActualHeight(actualHeight, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index e49ec64..3c080fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -31,7 +31,6 @@ public class NotificationBackgroundView extends View {
private Drawable mBackground;
private int mClipTopAmount;
private int mActualHeight;
- private boolean mActualHeightInitialized;
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -39,15 +38,6 @@ public class NotificationBackgroundView extends View {
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (!mActualHeightInitialized && mActualHeight == 0) {
- mActualHeight = getHeight();
- }
- mActualHeightInitialized = true;
- }
-
- @Override
protected void onDraw(Canvas canvas) {
draw(canvas, mBackground);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9df2701..f9baecb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -22,6 +22,7 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
+import android.widget.FrameLayout;
import com.android.systemui.R;
/**
@@ -29,7 +30,7 @@ import com.android.systemui.R;
* expanded layout. This class is responsible for clipping the content and and switching between the
* expanded and contracted view depending on its clipped size.
*/
-public class NotificationContentView extends ExpandableView {
+public class NotificationContentView extends FrameLayout {
private final Rect mClipBounds = new Rect();
@@ -37,6 +38,8 @@ public class NotificationContentView extends ExpandableView {
private View mExpandedChild;
private int mSmallHeight;
+ private int mClipTopAmount;
+ private int mActualHeight;
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -69,28 +72,24 @@ public class NotificationContentView extends ExpandableView {
selectLayout();
}
- @Override
- public void setActualHeight(int actualHeight, boolean notifyListeners) {
- super.setActualHeight(actualHeight, notifyListeners);
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
selectLayout();
updateClipping();
}
- @Override
public int getMaxHeight() {
// The maximum height is just the laid out height.
return getHeight();
}
- @Override
public int getMinHeight() {
return mSmallHeight;
}
- @Override
public void setClipTopAmount(int clipTopAmount) {
- super.setClipTopAmount(clipTopAmount);
+ mClipTopAmount = clipTopAmount;
updateClipping();
}
@@ -127,7 +126,6 @@ public class NotificationContentView extends ExpandableView {
selectLayout();
}
- @Override
public boolean isContentExpandable() {
return mExpandedChild != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 2bc6f9c..f5d4889 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -167,7 +168,7 @@ public class SignalClusterView
}
private void applyInetProblem(ImageView iv) {
- iv.setColorFilter(mInetProblem ? PROBLEM_FILTER : null);
+ iv.setColorFilter(Build.IS_DEBUGGABLE && mInetProblem ? PROBLEM_FILTER : null);
}
// Run after each indicator change.
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java
index 49e1072..bfbd60d 100644
--- a/policy/src/com/android/internal/policy/impl/BarController.java
+++ b/policy/src/com/android/internal/policy/impl/BarController.java
@@ -121,7 +121,7 @@ public class BarController {
}
} else {
vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
- vis = (vis & View.SYSTEM_UI_TRANSPARENT) | (oldVis & View.SYSTEM_UI_TRANSPARENT);
+ vis = (vis & ~View.SYSTEM_UI_TRANSPARENT) | (oldVis & View.SYSTEM_UI_TRANSPARENT);
}
}
return vis;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 168742f..4ab8a01 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -22,7 +22,6 @@ import android.app.AlarmManager;
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -40,7 +39,6 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -104,13 +102,21 @@ class AlarmManagerService extends SystemService {
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
boolean mLastWakeLockUnimportantForLogging;
+ ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<Alarm>();
ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
final AlarmHandler mHandler = new AlarmHandler();
ClockReceiver mClockReceiver;
+ InteractiveStateReceiver mInteractiveStateReceiver;
private UninstallReceiver mUninstallReceiver;
final ResultReceiver mResultReceiver = new ResultReceiver();
PendingIntent mTimeTickSender;
PendingIntent mDateChangeSender;
+ boolean mInteractive = true;
+ long mNonInteractiveStartTime;
+ long mNonInteractiveTime;
+ long mLastAlarmDeliveryTime;
+ long mStartCurrentDelayTime;
+ long mNextNonWakeupDeliveryTime;
class WakeupEvent {
public long when;
@@ -318,7 +324,11 @@ class AlarmManagerService extends SystemService {
final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
@Override
public int compare(Alarm lhs, Alarm rhs) {
- if (lhs.wakeup != rhs.wakeup) {
+ if ((!lhs.operation.getCreatorPackage().equals(rhs.operation.getCreatorPackage()))
+ && lhs.wakeup != rhs.wakeup) {
+ // We want to put wakeup alarms before non-wakeup alarms, since they are
+ // the things that drive most activity in the alarm manager. However,
+ // alarms from the same package should always be ordered strictly by time.
return lhs.wakeup ? -1 : 1;
}
if (lhs.whenElapsed < rhs.whenElapsed) {
@@ -424,24 +434,21 @@ class AlarmManagerService extends SystemService {
static final class InFlight extends Intent {
final PendingIntent mPendingIntent;
final WorkSource mWorkSource;
- final Pair<String, ComponentName> mTarget;
+ final String mTag;
final BroadcastStats mBroadcastStats;
final FilterStats mFilterStats;
final int mAlarmType;
InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
- int alarmType) {
+ int alarmType, String tag) {
mPendingIntent = pendingIntent;
mWorkSource = workSource;
- Intent intent = pendingIntent.getIntent();
- mTarget = intent != null
- ? new Pair<String, ComponentName>(intent.getAction(), intent.getComponent())
- : null;
+ mTag = tag;
mBroadcastStats = service.getStatsLocked(pendingIntent);
- FilterStats fs = mBroadcastStats.filterStats.get(mTarget);
+ FilterStats fs = mBroadcastStats.filterStats.get(mTag);
if (fs == null) {
- fs = new FilterStats(mBroadcastStats, mTarget);
- mBroadcastStats.filterStats.put(mTarget, fs);
+ fs = new FilterStats(mBroadcastStats, mTag);
+ mBroadcastStats.filterStats.put(mTag, fs);
}
mFilterStats = fs;
mAlarmType = alarmType;
@@ -450,7 +457,7 @@ class AlarmManagerService extends SystemService {
static final class FilterStats {
final BroadcastStats mBroadcastStats;
- final Pair<String, ComponentName> mTarget;
+ final String mTag;
long aggregateTime;
int count;
@@ -458,9 +465,9 @@ class AlarmManagerService extends SystemService {
long startTime;
int nesting;
- FilterStats(BroadcastStats broadcastStats, Pair<String, ComponentName> target) {
+ FilterStats(BroadcastStats broadcastStats, String tag) {
mBroadcastStats = broadcastStats;
- mTarget = target;
+ mTag = tag;
}
}
@@ -473,8 +480,7 @@ class AlarmManagerService extends SystemService {
int numWakeup;
long startTime;
int nesting;
- final ArrayMap<Pair<String, ComponentName>, FilterStats> filterStats
- = new ArrayMap<Pair<String, ComponentName>, FilterStats>();
+ final ArrayMap<String, FilterStats> filterStats = new ArrayMap<String, FilterStats>();
BroadcastStats(int uid, String packageName) {
mUid = uid;
@@ -484,7 +490,11 @@ class AlarmManagerService extends SystemService {
final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
= new SparseArray<ArrayMap<String, BroadcastStats>>();
-
+
+ int mNumDelayedAlarms = 0;
+ long mTotalDelayTime = 0;
+ long mMaxDelayTime = 0;
+
@Override
public void onStart() {
mNativeData = init();
@@ -511,6 +521,7 @@ class AlarmManagerService extends SystemService {
mClockReceiver = new ClockReceiver();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
+ mInteractiveStateReceiver = new InteractiveStateReceiver();
mUninstallReceiver = new UninstallReceiver();
if (mNativeData != 0) {
@@ -735,13 +746,29 @@ class AlarmManagerService extends SystemService {
pw.print("nowRTC="); pw.print(nowRTC);
pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
- pw.print(" nowELAPSED="); pw.println(nowELAPSED);
+ pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw);
+ pw.println();
+ if (!mInteractive) {
+ pw.print("Time since non-interactive: ");
+ TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw);
+ pw.println();
+ pw.print("Max wakeup delay: ");
+ TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
+ pw.println();
+ pw.print("Time since last dispatch: ");
+ TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
+ pw.println();
+ pw.print("Next non-wakeup delivery time: ");
+ TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
+ pw.println();
+ }
long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
- pw.print("Next alarm: "); pw.print(mNextNonWakeup);
+ pw.print("Next non-wakeup alarm: ");
+ TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
- pw.print("Next wakeup: "); pw.print(mNextWakeup);
+ pw.print("Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
if (mAlarmBatches.size() > 0) {
@@ -750,11 +777,27 @@ class AlarmManagerService extends SystemService {
pw.println(mAlarmBatches.size());
for (Batch b : mAlarmBatches) {
pw.print(b); pw.println(':');
- dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC);
+ dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC, sdf);
}
}
pw.println();
+ pw.print("Past-due non-wakeup alarms: ");
+ if (mPendingNonWakeupAlarms.size() > 0) {
+ pw.println(mPendingNonWakeupAlarms.size());
+ dumpAlarmList(pw, mPendingNonWakeupAlarms, " ", nowELAPSED, nowRTC, sdf);
+ } else {
+ pw.println("(none)");
+ }
+ pw.print(" Number of delayed alarms: "); pw.print(mNumDelayedAlarms);
+ pw.print(", total delay time: "); TimeUtils.formatDuration(mTotalDelayTime, pw);
+ pw.println();
+ pw.print(" Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw);
+ pw.print(", max non-interactive time: ");
+ TimeUtils.formatDuration(mNonInteractiveTime, pw);
+ pw.println();
+
+ pw.println();
pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount);
pw.println();
@@ -811,13 +854,7 @@ class AlarmManagerService extends SystemService {
pw.print(" alarms: "); UserHandle.formatUid(pw, fs.mBroadcastStats.mUid);
pw.print(":"); pw.print(fs.mBroadcastStats.mPackageName);
pw.println();
- pw.print(" ");
- if (fs.mTarget.first != null) {
- pw.print(" act="); pw.print(fs.mTarget.first);
- }
- if (fs.mTarget.second != null) {
- pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
- }
+ pw.print(" "); pw.print(fs.mTag);
pw.println();
}
}
@@ -849,13 +886,8 @@ class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" "); pw.print(fs.numWakeup);
pw.print(" wakes " ); pw.print(fs.count);
- pw.print(" alarms:");
- if (fs.mTarget.first != null) {
- pw.print(" act="); pw.print(fs.mTarget.first);
- }
- if (fs.mTarget.second != null) {
- pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
- }
+ pw.print(" alarms: ");
+ pw.print(fs.mTag);
pw.println();
}
}
@@ -883,7 +915,7 @@ class AlarmManagerService extends SystemService {
}
}
- private void logBatchesLocked() {
+ private void logBatchesLocked(SimpleDateFormat sdf) {
ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
PrintWriter pw = new PrintWriter(bs);
final long nowRTC = System.currentTimeMillis();
@@ -892,7 +924,7 @@ class AlarmManagerService extends SystemService {
for (int iz = 0; iz < NZ; iz++) {
Batch bz = mAlarmBatches.get(iz);
pw.append("Batch "); pw.print(iz); pw.append(": "); pw.println(bz);
- dumpAlarmList(pw, bz.alarms, " ", nowELAPSED, nowRTC);
+ dumpAlarmList(pw, bz.alarms, " ", nowELAPSED, nowRTC, sdf);
pw.flush();
Slog.v(TAG, bs.toString());
bs.reset();
@@ -910,7 +942,8 @@ class AlarmManagerService extends SystemService {
lastTime = b.start;
} else {
Slog.e(TAG, "CONSISTENCY FAILURE: Batch " + i + " is out of order");
- logBatchesLocked();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ logBatchesLocked(sdf);
return false;
}
}
@@ -932,6 +965,7 @@ class AlarmManagerService extends SystemService {
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
+ long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
@@ -939,11 +973,19 @@ class AlarmManagerService extends SystemService {
mNextWakeup = firstWakeup.start;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
- if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) {
- mNextNonWakeup = firstBatch.start;
- setLocked(ELAPSED_REALTIME, firstBatch.start);
+ if (firstBatch != firstWakeup) {
+ nextNonWakeup = firstBatch.start;
+ }
+ }
+ if (mPendingNonWakeupAlarms.size() > 0) {
+ if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
+ nextNonWakeup = mNextNonWakeupDeliveryTime;
}
}
+ if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) {
+ mNextNonWakeup = nextNonWakeup;
+ setLocked(ELAPSED_REALTIME, nextNonWakeup);
+ }
}
private void removeLocked(PendingIntent operation) {
@@ -1003,6 +1045,32 @@ class AlarmManagerService extends SystemService {
}
}
+ void interactiveStateChangedLocked(boolean interactive) {
+ if (mInteractive != interactive) {
+ mInteractive = interactive;
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ if (interactive) {
+ if (mPendingNonWakeupAlarms.size() > 0) {
+ final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
+ mTotalDelayTime += thisDelayTime;
+ if (mMaxDelayTime < thisDelayTime) {
+ mMaxDelayTime = thisDelayTime;
+ }
+ deliverAlarmsLocked(mPendingNonWakeupAlarms, nowELAPSED);
+ mPendingNonWakeupAlarms.clear();
+ }
+ if (mNonInteractiveStartTime > 0) {
+ long dur = nowELAPSED - mNonInteractiveStartTime;
+ if (dur > mNonInteractiveTime) {
+ mNonInteractiveTime = dur;
+ }
+ }
+ } else {
+ mNonInteractiveStartTime = nowELAPSED;
+ }
+ }
+ }
+
boolean lookForPackageLocked(String packageName) {
for (int i = 0; i < mAlarmBatches.size(); i++) {
Batch b = mAlarmBatches.get(i);
@@ -1037,12 +1105,12 @@ class AlarmManagerService extends SystemService {
}
private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
- String prefix, String label, long now) {
+ String prefix, String label, long nowRTC, long nowELAPSED, SimpleDateFormat sdf) {
for (int i=list.size()-1; i>=0; i--) {
Alarm a = list.get(i);
pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
pw.print(": "); pw.println(a);
- a.dump(pw, prefix + " ", now);
+ a.dump(pw, prefix + " ", nowRTC, nowELAPSED, sdf);
}
}
@@ -1059,14 +1127,13 @@ class AlarmManagerService extends SystemService {
}
private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
- String prefix, long nowELAPSED, long nowRTC) {
+ String prefix, long nowELAPSED, long nowRTC, SimpleDateFormat sdf) {
for (int i=list.size()-1; i>=0; i--) {
Alarm a = list.get(i);
final String label = labelForType(a.type);
- long now = (a.type <= RTC) ? nowRTC : nowELAPSED;
pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
pw.print(": "); pw.println(a);
- a.dump(pw, prefix + " ", now);
+ a.dump(pw, prefix + " ", nowRTC, nowELAPSED, sdf);
}
}
@@ -1077,8 +1144,9 @@ class AlarmManagerService extends SystemService {
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
- void triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
+ boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
final long nowRTC) {
+ boolean hasWakeup = false;
// batches are temporally sorted, so we need only pull from the
// start of the list until we either empty it or hit a batch
// that is not yet deliverable
@@ -1113,8 +1181,14 @@ class AlarmManagerService extends SystemService {
maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
alarm.repeatInterval, alarm.operation, batch.standalone, true,
alarm.workSource);
- }
+ // For now we count this as a wakeup alarm, meaning it needs to be
+ // delivered immediately. In the future we should change this, but
+ // that required delaying when we reschedule the repeat...!
+ hasWakeup = false;
+ } else if (alarm.wakeup) {
+ hasWakeup = true;
+ }
}
}
@@ -1125,6 +1199,8 @@ class AlarmManagerService extends SystemService {
Slog.v(TAG, "Triggering alarm #" + i + ": " + triggerList.get(i));
}
}
+
+ return hasWakeup;
}
/**
@@ -1147,15 +1223,16 @@ class AlarmManagerService extends SystemService {
private static class Alarm {
public final int type;
public final boolean wakeup;
+ public final PendingIntent operation;
+ public final String tag;
+ public final WorkSource workSource;
public int count;
public long when;
public long windowLength;
public long whenElapsed; // 'when' in the elapsed time base
public long maxWhen; // also in the elapsed time base
public long repeatInterval;
- public PendingIntent operation;
- public WorkSource workSource;
-
+
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
long _interval, PendingIntent _op, WorkSource _ws) {
type = _type;
@@ -1167,12 +1244,17 @@ class AlarmManagerService extends SystemService {
maxWhen = _maxWhen;
repeatInterval = _interval;
operation = _op;
+ tag = makeTag(_op, _type);
workSource = _ws;
}
+ public static String makeTag(PendingIntent pi, int type) {
+ return pi.getTag(type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
+ ? "*walarm*:" : "*alarm*:");
+ }
+
@Override
- public String toString()
- {
+ public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Alarm{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -1186,11 +1268,20 @@ class AlarmManagerService extends SystemService {
return sb.toString();
}
- public void dump(PrintWriter pw, String prefix, long now) {
+ public void dump(PrintWriter pw, String prefix, long nowRTC, long nowELAPSED,
+ SimpleDateFormat sdf) {
+ final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+ pw.print(prefix); pw.print("tag="); pw.println(tag);
pw.print(prefix); pw.print("type="); pw.print(type);
- pw.print(" whenElapsed="); pw.print(whenElapsed);
- pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
- pw.print(" window="); pw.print(windowLength);
+ pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
+ nowELAPSED, pw);
+ if (isRtc) {
+ pw.print(" when="); pw.print(sdf.format(new Date(when)));
+ } else {
+ pw.print(" when="); TimeUtils.formatDuration(when, nowELAPSED, pw);
+ }
+ pw.println();
+ pw.print(prefix); pw.print("window="); pw.print(windowLength);
pw.print(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.println(count);
pw.print(prefix); pw.print("operation="); pw.println(operation);
@@ -1216,6 +1307,102 @@ class AlarmManagerService extends SystemService {
}
}
+ long currentNonWakeupFuzzLocked(long nowELAPSED) {
+ long timeSinceOn = nowELAPSED - mNonInteractiveStartTime;
+ if (timeSinceOn < 5*60*1000) {
+ // If the screen has been off for 5 minutes, only delay by at most two minutes.
+ return 2*60*1000;
+ } else if (timeSinceOn < 30*60*1000) {
+ // If the screen has been off for 30 minutes, only delay by at most 15 minutes.
+ return 15*60*1000;
+ } else {
+ // Otherwise, we will delay by at most an hour.
+ return 60*60*1000;
+ }
+ }
+
+ boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) {
+ if (mInteractive) {
+ return false;
+ }
+ if (mLastAlarmDeliveryTime <= 0) {
+ return false;
+ }
+ if (mPendingNonWakeupAlarms.size() > 0 && mNextNonWakeupDeliveryTime > nowELAPSED) {
+ // This is just a little paranoia, if somehow we have pending non-wakeup alarms
+ // and the next delivery time is in the past, then just deliver them all. This
+ // avoids bugs where we get stuck in a loop trying to poll for alarms.
+ return false;
+ }
+ long timeSinceLast = nowELAPSED - mLastAlarmDeliveryTime;
+ return timeSinceLast <= currentNonWakeupFuzzLocked(nowELAPSED);
+ }
+
+ void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
+ mLastAlarmDeliveryTime = nowELAPSED;
+ for (int i=0; i<triggerList.size(); i++) {
+ Alarm alarm = triggerList.get(i);
+ try {
+ if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
+ alarm.operation.send(getContext(), 0,
+ mBackgroundIntent.putExtra(
+ Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mResultReceiver, mHandler);
+
+ // we have an active broadcast so stay awake.
+ if (mBroadcastRefCount == 0) {
+ setWakelockWorkSource(alarm.operation, alarm.workSource,
+ alarm.type, alarm.tag, true);
+ mWakeLock.acquire();
+ }
+ final InFlight inflight = new InFlight(AlarmManagerService.this,
+ alarm.operation, alarm.workSource, alarm.type, alarm.tag);
+ mInFlight.add(inflight);
+ mBroadcastRefCount++;
+
+ final BroadcastStats bs = inflight.mBroadcastStats;
+ bs.count++;
+ if (bs.nesting == 0) {
+ bs.nesting = 1;
+ bs.startTime = nowELAPSED;
+ } else {
+ bs.nesting++;
+ }
+ final FilterStats fs = inflight.mFilterStats;
+ fs.count++;
+ if (fs.nesting == 0) {
+ fs.nesting = 1;
+ fs.startTime = nowELAPSED;
+ } else {
+ fs.nesting++;
+ }
+ if (alarm.type == ELAPSED_REALTIME_WAKEUP
+ || alarm.type == RTC_WAKEUP) {
+ bs.numWakeup++;
+ fs.numWakeup++;
+ if (alarm.workSource != null && alarm.workSource.size() > 0) {
+ for (int wi=0; wi<alarm.workSource.size(); wi++) {
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, alarm.workSource.get(wi),
+ alarm.workSource.getName(wi));
+ }
+ } else {
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, -1, null);
+ }
+ }
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss the hoser.
+ removeImpl(alarm.operation);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure sending alarm.", e);
+ }
+ }
+ }
+
private class AlarmThread extends Thread
{
public AlarmThread()
@@ -1269,70 +1456,35 @@ class AlarmManagerService extends SystemService {
}
}
- triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
- rescheduleKernelAlarmsLocked();
-
- // now deliver the alarm intents
- for (int i=0; i<triggerList.size(); i++) {
- Alarm alarm = triggerList.get(i);
- try {
- if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
- alarm.operation.send(getContext(), 0,
- mBackgroundIntent.putExtra(
- Intent.EXTRA_ALARM_COUNT, alarm.count),
- mResultReceiver, mHandler);
-
- // we have an active broadcast so stay awake.
- if (mBroadcastRefCount == 0) {
- setWakelockWorkSource(alarm.operation, alarm.workSource,
- alarm.type, true);
- mWakeLock.acquire();
- }
- final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.workSource, alarm.type);
- mInFlight.add(inflight);
- mBroadcastRefCount++;
-
- final BroadcastStats bs = inflight.mBroadcastStats;
- bs.count++;
- if (bs.nesting == 0) {
- bs.nesting = 1;
- bs.startTime = nowELAPSED;
- } else {
- bs.nesting++;
- }
- final FilterStats fs = inflight.mFilterStats;
- fs.count++;
- if (fs.nesting == 0) {
- fs.nesting = 1;
- fs.startTime = nowELAPSED;
- } else {
- fs.nesting++;
- }
- if (alarm.type == ELAPSED_REALTIME_WAKEUP
- || alarm.type == RTC_WAKEUP) {
- bs.numWakeup++;
- fs.numWakeup++;
- if (alarm.workSource != null && alarm.workSource.size() > 0) {
- for (int wi=0; wi<alarm.workSource.size(); wi++) {
- ActivityManagerNative.noteWakeupAlarm(
- alarm.operation, alarm.workSource.get(wi),
- alarm.workSource.getName(wi));
- }
- } else {
- ActivityManagerNative.noteWakeupAlarm(
- alarm.operation, -1, null);
- }
- }
- } catch (PendingIntent.CanceledException e) {
- if (alarm.repeatInterval > 0) {
- // This IntentSender is no longer valid, but this
- // is a repeating alarm, so toss the hoser.
- removeImpl(alarm.operation);
+ boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+ if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
+ // if there are no wakeup alarms and the screen is off, we can
+ // delay what we have so far until the future.
+ if (mPendingNonWakeupAlarms.size() == 0) {
+ mStartCurrentDelayTime = nowELAPSED;
+ mNextNonWakeupDeliveryTime = nowELAPSED
+ + ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
+ }
+ mPendingNonWakeupAlarms.addAll(triggerList);
+ mNumDelayedAlarms += triggerList.size();
+ rescheduleKernelAlarmsLocked();
+ } else {
+ // now deliver the alarm intents; if there are pending non-wakeup
+ // alarms, we need to merge them in to the list. note we don't
+ // just deliver them first because we generally want non-wakeup
+ // alarms delivered after wakeup alarms.
+ rescheduleKernelAlarmsLocked();
+ if (mPendingNonWakeupAlarms.size() > 0) {
+ triggerList.addAll(mPendingNonWakeupAlarms);
+ Collections.sort(triggerList, mAlarmDispatchComparator);
+ final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
+ mTotalDelayTime += thisDelayTime;
+ if (mMaxDelayTime < thisDelayTime) {
+ mMaxDelayTime = thisDelayTime;
}
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure sending alarm.", e);
+ mPendingNonWakeupAlarms.clear();
}
+ deliverAlarmsLocked(triggerList, nowELAPSED);
}
}
}
@@ -1344,14 +1496,13 @@ class AlarmManagerService extends SystemService {
* @param pi PendingIntent to attribute blame to if ws is null.
* @param ws WorkSource to attribute blame.
*/
- void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
+ void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
+ boolean first) {
try {
final boolean unimportant = pi == mTimeTickSender;
mWakeLock.setUnimportantForLogging(unimportant);
if (first || mLastWakeLockUnimportantForLogging) {
- mWakeLock.setHistoryTag(pi.getTag(
- type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
- ? "*walarm*:" : "*alarm*:"));
+ mWakeLock.setHistoryTag(tag);
} else {
mWakeLock.setHistoryTag(null);
}
@@ -1462,6 +1613,23 @@ class AlarmManagerService extends SystemService {
}
}
+ class InteractiveStateReceiver extends BroadcastReceiver {
+ public InteractiveStateReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ getContext().registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ interactiveStateChangedLocked(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
+ }
+ }
+ }
+
class UninstallReceiver extends BroadcastReceiver {
public UninstallReceiver() {
IntentFilter filter = new IntentFilter();
@@ -1589,7 +1757,7 @@ class AlarmManagerService extends SystemService {
if (mInFlight.size() > 0) {
InFlight inFlight = mInFlight.get(0);
setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
- inFlight.mAlarmType, false);
+ inFlight.mAlarmType, inFlight.mTag, false);
} else {
// should never happen
mLog.w("Alarm wakelock still held but sent queue empty");
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index be20616..bfa0402 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1253,7 +1253,10 @@ public class AppOpsService extends IAppOpsService.Stub {
public void removeUser(int userHandle) throws RemoteException {
checkSystemUid("removeUser");
mOpRestrictions.remove(userHandle);
- mProfileOwnerUids.removeAt(mProfileOwnerUids.indexOfKey(userHandle));
+ final int index = mProfileOwnerUids.indexOfKey(userHandle);
+ if (index >= 0) {
+ mProfileOwnerUids.removeAt(index);
+ }
}
private void checkSystemUid(String function) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index cc132be..af53fef 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -398,7 +398,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/**
* used internally when registering NetworkFactories
- * obj = Messenger
+ * obj = NetworkFactoryInfo
*/
private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
@@ -434,6 +434,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
+ /**
+ * used internally when registering NetworkFactories
+ * obj = Messenger
+ */
+ private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+
+
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -2889,6 +2896,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return;
}
+ pw.println("NetworkFactories for:");
+ pw.increaseIndent();
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ pw.println(nfi.name);
+ }
+ pw.decreaseIndent();
+ pw.println();
+
NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
pw.print("Active default network: ");
if (defaultNai == null) {
@@ -2983,6 +2998,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (nai == null) {
loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
} else {
+ if (VDBG) log("Update of Linkproperties for " + nai.name());
LinkProperties oldLp = nai.linkProperties;
nai.linkProperties = (LinkProperties)msg.obj;
updateLinkProperties(nai, oldLp);
@@ -3096,18 +3112,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
- if (mNetworkFactories.contains(ac)) {
+ if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isRequest == false) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
(nai != null ? nai.currentScore : 0), 0, nri.request);
}
} else {
loge("Error connecting NetworkFactory");
- mNetworkFactories.remove(ac);
+ mNetworkFactoryInfos.remove(msg.obj);
}
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -3214,8 +3231,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetworkRequests.put(nri.request, nri);
if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
if (DBG) log("sending new NetworkRequest to factories");
- for (AsyncChannel ac : mNetworkFactories) {
- ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
}
}
}
@@ -3236,8 +3253,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
if (nri.isRequest) {
- for (AsyncChannel factory : mNetworkFactories) {
- factory.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
}
if (affectedNetwork != null) {
@@ -3356,7 +3373,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
break;
}
case EVENT_REGISTER_NETWORK_FACTORY: {
- handleRegisterNetworkFactory((Messenger)msg.obj);
+ handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
+ break;
+ }
+ case EVENT_UNREGISTER_NETWORK_FACTORY: {
+ handleUnregisterNetworkFactory((Messenger)msg.obj);
break;
}
case EVENT_REGISTER_NETWORK_AGENT: {
@@ -5222,10 +5243,22 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent);
}
- private final ArrayList<AsyncChannel> mNetworkFactories = new ArrayList<AsyncChannel>();
+ private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
+ new HashMap<Messenger, NetworkFactoryInfo>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
new HashMap<NetworkRequest, NetworkRequestInfo>();
+ private static class NetworkFactoryInfo {
+ public final String name;
+ public final Messenger messenger;
+ public final AsyncChannel asyncChannel;
+
+ public NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel) {
+ this.name = name;
+ this.messenger = messenger;
+ this.asyncChannel = asyncChannel;
+ }
+ }
private class NetworkRequestInfo implements IBinder.DeathRecipient {
static final boolean REQUEST = true;
@@ -5263,6 +5296,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
request + ", " + mBinder + ")");
releaseNetworkRequest(request);
}
+
+ public String toString() {
+ return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
+ mPid + " for " + request;
+ }
}
@Override
@@ -5326,24 +5364,31 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
@Override
- public void registerNetworkFactory(Messenger messenger) {
+ public void registerNetworkFactory(Messenger messenger, String name) {
enforceConnectivityInternalPermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger));
+ NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
}
- private void handleRegisterNetworkFactory(Messenger messenger) {
- if (VDBG) log("Got NetworkFactory Messenger");
- AsyncChannel ac = new AsyncChannel();
- mNetworkFactories.add(ac);
- ac.connect(mContext, mTrackerHandler, messenger);
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.isRequest) {
- int score = 0;
- NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
- if (currentNetwork != null) score = currentNetwork.currentScore;
- ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
- }
+ private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
+ if (VDBG) log("Got NetworkFactory Messenger for " + nfi.name);
+ mNetworkFactoryInfos.put(nfi.messenger, nfi);
+ nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
+ }
+
+ @Override
+ public void unregisterNetworkFactory(Messenger messenger) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+ }
+
+ private void handleUnregisterNetworkFactory(Messenger messenger) {
+ NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
+ if (nfi == null) {
+ if (VDBG) log("Failed to find Messenger in unregisterNetworkFactory");
+ return;
}
+ if (VDBG) log("unregisterNetworkFactory for " + nfi.name);
}
/**
@@ -5535,8 +5580,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
- for (AsyncChannel ac : mNetworkFactories) {
- ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest);
+ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
+ nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest);
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 50553ee..5cb2a8a 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3439,7 +3439,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
- p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
+ p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
}
p.println(" ");
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index c32beda..cd8c13f 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -264,7 +264,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
} else if (headset == BIT_USB_HEADSET_DGTL) {
device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
} else if (headset == BIT_HDMI_AUDIO) {
- device = AudioManager.DEVICE_OUT_AUX_DIGITAL;
+ device = AudioManager.DEVICE_OUT_HDMI;
} else {
Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
return;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7abc75f..7cd4ef8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13837,7 +13837,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
- ProxyInfo proxy = intent.getParcelableExtra("proxy");
+ ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index e0bc718..156bbbe 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -103,7 +103,7 @@ final class NewDeviceAction extends FeatureAction {
requestVendorId();
return true;
} else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
- int requestOpcode = params[1];
+ int requestOpcode = params[1] & 0xff;
if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) {
mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
requestVendorId();
@@ -113,7 +113,8 @@ final class NewDeviceAction extends FeatureAction {
} else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
if (params.length == 3) {
- mVendorId = (params[0] << 16) + (params[1] << 8) + params[2];
+ mVendorId = ((params[0] & 0xff) << 16) + ((params[1] & 0xff) << 8)
+ + (params[2] & 0xff);
} else {
Slog.e(TAG, "Failed to get device vendor ID: ");
}
@@ -121,7 +122,7 @@ final class NewDeviceAction extends FeatureAction {
finish();
return true;
} else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
- int requestOpcode = params[1];
+ int requestOpcode = params[1] & 0xff;
if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
addDeviceInfo();
finish();
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 007032e..dbfb1cf 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -88,6 +88,8 @@ public class ConditionProviders extends ManagedServices {
for (int i = 0; i < mRecords.size(); i++) {
pw.print(" "); pw.println(mRecords.get(i));
}
+ pw.print(" mCountdownHelper: ");
+ pw.println(mCountdownHelper.getCurrentConditionDescription());
}
}
@@ -474,6 +476,16 @@ public class ConditionProviders extends ManagedServices {
}
}
+ public String getCurrentConditionDescription() {
+ if (mCurrent == 0) return null;
+ final long time = mCurrent;
+ final long now = System.currentTimeMillis();
+ final CharSequence span =
+ DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
+ return String.format("Scheduled for %s, %s in the future (%s), now=%s",
+ ts(time), time - now, span, ts(now));
+ }
+
private String ts(long time) {
return new Date(time) + " (" + time + ")";
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1734a33..9569c0d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -63,7 +63,7 @@ import android.service.notification.INotificationListener;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationOrderUpdate;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
@@ -1744,7 +1744,7 @@ public class NotificationManagerService extends SystemService {
sendAccessibilityEvent(notification, pkg);
}
- mListeners.notifyPostedLocked(r.sbn);
+ mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
} else {
Slog.e(TAG, "Not posting notification with icon==0: " + notification);
if (old != null && old.statusBarKey != null) {
@@ -1755,7 +1755,7 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
- mListeners.notifyRemovedLocked(r.sbn);
+ mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
@@ -2041,14 +2041,17 @@ public class NotificationManagerService extends SystemService {
private void handleSendRankingUpdate() {
synchronized (mNotificationList) {
- final int N = mNotificationList.size();
- ArrayList<StatusBarNotification> sbns =
- new ArrayList<StatusBarNotification>(N);
- for (int i = 0; i < N; i++ ) {
- sbns.add(mNotificationList.get(i).sbn);
- }
- mListeners.notifyOrderUpdateLocked(sbns);
+ mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked());
+ }
+ }
+
+ private ArrayList<StatusBarNotification> cloneNotificationListLocked() {
+ final int N = mNotificationList.size();
+ ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N);
+ for (int i = 0; i < N; i++) {
+ sbns.add(mNotificationList.get(i).sbn);
}
+ return sbns;
}
private final class WorkerHandler extends Handler
@@ -2136,7 +2139,7 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
r.statusBarKey = null;
- mListeners.notifyRemovedLocked(r.sbn);
+ mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
}
// sound
@@ -2442,6 +2445,33 @@ public class NotificationManagerService extends SystemService {
}
}
+ /**
+ * Generates a NotificationRankingUpdate from 'sbns', considering only
+ * notifications visible to the given listener.
+ */
+ private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info,
+ ArrayList<StatusBarNotification> sbns) {
+ int speedBumpIndex = -1;
+ ArrayList<String> keys = new ArrayList<String>(sbns.size());
+ ArrayList<String> dndKeys = new ArrayList<String>(sbns.size());
+ for (StatusBarNotification sbn: sbns) {
+ if (!info.enabledAndUserMatches(sbn.getUserId())) {
+ continue;
+ }
+ keys.add(sbn.getKey());
+ if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) {
+ dndKeys.add(sbn.getKey());
+ }
+ if (speedBumpIndex == -1 &&
+ sbn.getNotification().priority == Notification.PRIORITY_MIN) {
+ speedBumpIndex = keys.size() - 1;
+ }
+ }
+ String[] keysAr = keys.toArray(new String[keys.size()]);
+ String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]);
+ return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex);
+ }
+
public class NotificationListeners extends ManagedServices {
public NotificationListeners() {
@@ -2468,9 +2498,12 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
- final String[] keys = getActiveNotificationKeys(listener);
+ final ArrayList<StatusBarNotification> sbns;
+ synchronized (mNotificationList) {
+ sbns = cloneNotificationListLocked();
+ }
try {
- listener.onListenerConnected(new NotificationOrderUpdate(keys));
+ listener.onListenerConnected(makeRankingUpdateForListener(info, sbns));
} catch (RemoteException e) {
// we tried
}
@@ -2479,44 +2512,47 @@ public class NotificationManagerService extends SystemService {
/**
* asynchronously notify all listeners about a new notification
*/
- public void notifyPostedLocked(StatusBarNotification sbn) {
+ public void notifyPostedLocked(StatusBarNotification sbn,
+ final ArrayList<StatusBarNotification> sbns) {
// make a copy in case changes are made to the underlying Notification object
final StatusBarNotification sbnClone = sbn.clone();
for (final ManagedServiceInfo info : mServices) {
- if (info.isEnabledForCurrentProfiles()) {
- final INotificationListener listener = (INotificationListener) info.service;
- final String[] keys = getActiveNotificationKeys(listener);
- if (keys.length > 0) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyPostedIfUserMatch(info, sbnClone, keys);
- }
- });
- }
+ if (!info.isEnabledForCurrentProfiles()) {
+ continue;
}
+ final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns);
+ if (update.getOrderedKeys().length == 0) {
+ continue;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyPostedIfUserMatch(info, sbnClone, update);
+ }
+ });
}
}
/**
* asynchronously notify all listeners about a removed notification
*/
- public void notifyRemovedLocked(StatusBarNotification sbn) {
+ public void notifyRemovedLocked(StatusBarNotification sbn,
+ final ArrayList<StatusBarNotification> sbns) {
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
final StatusBarNotification sbnLight = sbn.cloneLight();
for (final ManagedServiceInfo info : mServices) {
- if (info.isEnabledForCurrentProfiles()) {
- final INotificationListener listener = (INotificationListener) info.service;
- final String[] keys = getActiveNotificationKeys(listener);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyRemovedIfUserMatch(info, sbnLight, keys);
- }
- });
+ if (!info.isEnabledForCurrentProfiles()) {
+ continue;
}
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemovedIfUserMatch(info, sbnLight,
+ makeRankingUpdateForListener(info, sbns));
+ }
+ });
}
}
@@ -2526,60 +2562,52 @@ public class NotificationManagerService extends SystemService {
* must not rely on mutable members of these objects, such as the
* {@link Notification}.
*/
- public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+ public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
for (final ManagedServiceInfo serviceInfo : mServices) {
+ if (!serviceInfo.isEnabledForCurrentProfiles()) {
+ continue;
+ }
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
+ notifyRankingUpdate(serviceInfo,
+ makeRankingUpdateForListener(serviceInfo, sbns));
}
});
}
}
private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
- final StatusBarNotification sbn, String[] keys) {
+ final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener)info.service;
try {
- listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
+ listener.onNotificationPosted(sbn, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
- String[] keys) {
+ NotificationRankingUpdate rankingUpdate) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
- final INotificationListener listener = (INotificationListener)info.service;
+ final INotificationListener listener = (INotificationListener) info.service;
try {
- listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
+ listener.onNotificationRemoved(sbn, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
}
- /**
- * @param sbns an array of {@link StatusBarNotification}s to consider. This code
- * must not rely on mutable members of these objects, such as the
- * {@link Notification}.
- */
- public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
- ArrayList<StatusBarNotification> sbns) {
- ArrayList<String> keys = new ArrayList<String>(sbns.size());
- for (StatusBarNotification sbn: sbns) {
- if (info.enabledAndUserMatches(sbn.getUserId())) {
- keys.add(sbn.getKey());
- }
- }
- final INotificationListener listener = (INotificationListener)info.service;
+ private void notifyRankingUpdate(ManagedServiceInfo info,
+ NotificationRankingUpdate rankingUpdate) {
+ final INotificationListener listener = (INotificationListener) info.service;
try {
- listener.onNotificationOrderUpdate(
- new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
+ listener.onNotificationRankingUpdate(rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61b3a89..46c5482 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11876,6 +11876,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" cmd may be one of:");
pw.println(" l[ibraries]: list known shared libraries");
pw.println(" f[ibraries]: list device features");
+ pw.println(" k[eysets]: print known keysets");
pw.println(" r[esolvers]: dump intent resolvers");
pw.println(" perm[issions]: dump permissions");
pw.println(" pref[erred]: print preferred package settings");
@@ -11886,8 +11887,8 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" m[essages]: print collected runtime messages");
pw.println(" v[erifiers]: print package verifier info");
pw.println(" version: print database version info");
+ pw.println(" write: write current settings now");
pw.println(" <package.name>: info about given package");
- pw.println(" k[eysets]: print known keysets");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
@@ -11938,6 +11939,12 @@ public class PackageManagerService extends IPackageManager.Stub {
dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_KEYSETS);
+ } else if ("write".equals(cmd)) {
+ synchronized (mPackages) {
+ mSettings.writeLPr();
+ pw.println("Settings written.");
+ return;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 131d05b..102b2d4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -511,8 +511,16 @@ public class UserManagerService extends IUserManager.Stub {
* Check if we've hit the limit of how many users can be created.
*/
private boolean isUserLimitReachedLocked() {
- int nUsers = mUsers.size();
- return nUsers >= UserManager.getMaxSupportedUsers();
+ int aliveUserCount = 0;
+ final int totalUserCount = mUsers.size();
+ // Skip over users being removed
+ for (int i = 0; i < totalUserCount; i++) {
+ UserInfo user = mUsers.valueAt(i);
+ if (!mRemovingUserIds.get(user.id)) {
+ aliveUserCount++;
+ }
+ }
+ return aliveUserCount >= UserManager.getMaxSupportedUsers();
}
/**
@@ -781,6 +789,7 @@ public class UserManagerService extends IUserManager.Stub {
writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
serializer.endTag(null, TAG_RESTRICTIONS);
}
serializer.endTag(null, TAG_USER);
@@ -933,6 +942,7 @@ public class UserManagerService extends IUserManager.Stub {
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
}
}
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index c1b9a33..efaa91b 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -324,8 +324,6 @@ public class TrustManagerService extends SystemService {
mTrustListeners.get(i).onTrustChanged(enabled, userId);
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
- mTrustListeners.get(i);
- i--;
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c20e38c..e2d2ac6 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -26,6 +26,7 @@ import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.SystemProperties;
import android.util.Slog;
+import android.view.View;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -34,7 +35,6 @@ import android.view.animation.AnimationUtils;
import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
-
import android.view.animation.TranslateAnimation;
import com.android.internal.util.DumpUtils.Dump;
import com.android.server.AttributeCache;
@@ -500,7 +500,8 @@ public class AppTransition implements Dump {
*/
Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
int appHeight, int orientation, int transit,
- Rect containingFrame, Rect contentInsets) {
+ Rect containingFrame, Rect contentInsets,
+ boolean isFullScreen) {
Animation a;
final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -520,6 +521,9 @@ public class AppTransition implements Dump {
scaledTopDecor = (int) (scale * contentInsets.top);
int unscaledThumbHeight = (int) (thumbHeight / scale);
mTmpFromClipRect.set(containingFrame);
+ if (isFullScreen) {
+ mTmpFromClipRect.top = contentInsets.top;
+ }
mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
mTmpToClipRect.set(containingFrame);
} else {
@@ -527,7 +531,12 @@ public class AppTransition implements Dump {
scale = thumbHeight / (appHeight - contentInsets.top);
scaledTopDecor = (int) (scale * contentInsets.top);
int unscaledThumbWidth = (int) (thumbWidth / scale);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
mTmpFromClipRect.set(containingFrame);
+ if (isFullScreen) {
+ mTmpFromClipRect.top = contentInsets.top;
+ mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
+ }
mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
mTmpToClipRect.set(containingFrame);
}
@@ -575,14 +584,22 @@ public class AppTransition implements Dump {
int unscaledThumbHeight = (int) (thumbHeight / scale);
mTmpFromClipRect.set(containingFrame);
mTmpToClipRect.set(containingFrame);
+ if (isFullScreen) {
+ mTmpToClipRect.top = contentInsets.top;
+ }
mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
} else {
// In landscape, we scale the height and clip to the top/left square
scale = thumbHeight / (appHeight - contentInsets.top);
scaledTopDecor = (int) (scale * contentInsets.top);
int unscaledThumbWidth = (int) (thumbWidth / scale);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
mTmpFromClipRect.set(containingFrame);
mTmpToClipRect.set(containingFrame);
+ if (isFullScreen) {
+ mTmpToClipRect.top = contentInsets.top;
+ mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
+ }
mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
}
@@ -679,7 +696,7 @@ public class AppTransition implements Dump {
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
int appWidth, int appHeight, int orientation,
- Rect containingFrame, Rect contentInsets) {
+ Rect containingFrame, Rect contentInsets, boolean isFullScreen) {
Animation a;
if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -702,7 +719,7 @@ public class AppTransition implements Dump {
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createAlternateThumbnailEnterExitAnimationLocked(
getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
- transit, containingFrame, contentInsets);
+ transit, containingFrame, contentInsets, isFullScreen);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c6fffbf..63a4f52 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -289,6 +289,11 @@ public class WindowManagerService extends IWindowManager.Stub
private static final int MAX_SCREENSHOT_RETRIES = 3;
+ // The flag describing a full screen app window (where the app takes care of drawing under the
+ // SystemUI bars)
+ private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -3193,8 +3198,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ boolean isFullScreen =
+ ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
+ == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
- mCurConfiguration.orientation, containingFrame, contentInsets);
+ mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e2cd4e2..bc7742f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3384,6 +3384,93 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean setApplicationBlocked(ComponentName who, String packageName,
+ boolean blocked) {
+ int callingUserId = UserHandle.getCallingUserId();
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.setApplicationBlockedSettingAsUser(packageName, blocked, callingUserId);
+ } catch (RemoteException re) {
+ // shouldn't happen
+ Slog.e(LOG_TAG, "Failed to setApplicationBlockedSetting", re);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public int setApplicationsBlocked(ComponentName who, Intent intent, boolean blocked) {
+ int callingUserId = UserHandle.getCallingUserId();
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES,
+ callingUserId);
+
+ if (DBG) Slog.d(LOG_TAG, "Enabling activities: " + activitiesToEnable);
+ int numberOfAppsUnblocked = 0;
+ if (activitiesToEnable != null) {
+ for (ResolveInfo info : activitiesToEnable) {
+ if (info.activityInfo != null) {
+ numberOfAppsUnblocked++;
+ pm.setApplicationBlockedSettingAsUser(info.activityInfo.packageName,
+ blocked, callingUserId);
+ }
+ }
+ }
+ return numberOfAppsUnblocked;
+ } catch (RemoteException re) {
+ // shouldn't happen
+ Slog.e(LOG_TAG, "Failed to setApplicationsBlockedSettingsWithIntent", re);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ return 0;
+ }
+ }
+
+ @Override
+ public boolean isApplicationBlocked(ComponentName who, String packageName) {
+ int callingUserId = UserHandle.getCallingUserId();
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.getApplicationBlockedSettingAsUser(packageName, callingUserId);
+ } catch (RemoteException re) {
+ // shouldn't happen
+ Slog.e(LOG_TAG, "Failed to getApplicationBlockedSettingAsUser", re);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ return false;
+ }
+ }
+
+ @Override
public void enableSystemApp(ComponentName who, String packageName) {
synchronized (this) {
if (who == null) {
@@ -3595,4 +3682,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
return false;
}
+
+ @Override
+ public void setGlobalSetting(ComponentName who, String setting, String value) {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ Settings.Global.putString(contentResolver, setting, value);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
+
+ @Override
+ public void setSecureSetting(ComponentName who, String setting, String value) {
+ int callingUserId = UserHandle.getCallingUserId();
+ final ContentResolver contentResolver = mContext.getContentResolver();
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9b3f7ac..55ae9c6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -125,7 +125,7 @@ public final class SystemServer {
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
private static final String WIFI_PASSPOINT_SERVICE_CLASS =
- "com.android.server.wifi.passpoint.WifiPasspointService";
+ "com.android.server.wifi.passpoint.PasspointService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
private static final String HDMI_CEC_SERVICE_CLASS =
@@ -639,15 +639,15 @@ public final class SystemServer {
}
try {
- mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi PasspointService", e);
+ reportWtf("starting Wi-Fi Service", e);
}
try {
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
+ reportWtf("starting Wi-Fi PasspointService", e);
}
try {
diff --git a/tests/RenderThreadTest/Android.mk b/tests/RenderThreadTest/Android.mk
index bdcba2e..e07e943 100644
--- a/tests/RenderThreadTest/Android.mk
+++ b/tests/RenderThreadTest/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/RenderThreadTest/AndroidManifest.xml b/tests/RenderThreadTest/AndroidManifest.xml
index c76cfce..a7f4f6e 100644
--- a/tests/RenderThreadTest/AndroidManifest.xml
+++ b/tests/RenderThreadTest/AndroidManifest.xml
@@ -4,10 +4,6 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="18"
- android:targetSdkVersion="18" />
-
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index b766268..9ea7027 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -783,6 +783,9 @@ public class WifiScanner {
FullScanResult result = (FullScanResult) msg.obj;
((ScanListener) listener).onFullResult(result);
return;
+ case CMD_PERIOD_CHANGED:
+ ((ScanListener) listener).onPeriodChanged(msg.arg1);
+ return;
case CMD_AP_FOUND:
((HotlistListener) listener).onFound(
((ParcelableScanResults) msg.obj).getResults());