Merge "Use offsetLeftAndRight and offsetTopAndBottom in ChangeBounds." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 3edaefc..35bb66c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -325,8 +325,6 @@
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
media/java/android/media/IVolumeController.aidl \
- media/java/android/media/browse/IMediaBrowserService.aidl \
- media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl \
media/java/android/media/projection/IMediaProjection.aidl \
media/java/android/media/projection/IMediaProjectionCallback.aidl \
media/java/android/media/projection/IMediaProjectionManager.aidl \
@@ -346,6 +344,8 @@
media/java/android/media/tv/ITvInputServiceCallback.aidl \
media/java/android/media/tv/ITvInputSession.aidl \
media/java/android/media/tv/ITvInputSessionCallback.aidl \
+ media/java/android/service/media/IMediaBrowserService.aidl \
+ media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl \
telecomm/java/com/android/internal/telecomm/IVideoCallback.aidl \
telecomm/java/com/android/internal/telecomm/IVideoProvider.aidl \
telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 2ff971b..22755b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -905,6 +905,8 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationContentDescription = 16843970; // 0x10104c2
+ field public static final int navigationIcon = 16843969; // 0x10104c1
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
@@ -1757,6 +1759,7 @@
field public static final int list = 16908298; // 0x102000a
field public static final int mask = 16908353; // 0x1020041
field public static final int message = 16908299; // 0x102000b
+ field public static final int navigationBarBackground = 16908355; // 0x1020043
field public static final int paste = 16908322; // 0x1020022
field public static final int primary = 16908300; // 0x102000c
field public static final int progress = 16908301; // 0x102000d
@@ -1765,6 +1768,7 @@
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
field public static final int startSelectingText = 16908328; // 0x1020028
+ field public static final int statusBarBackground = 16908354; // 0x1020042
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
@@ -2025,6 +2029,12 @@
field public static final int TextAppearance_Material_Medium = 16974355; // 0x1030213
field public static final int TextAppearance_Material_Medium_Inverse = 16974356; // 0x1030214
field public static final int TextAppearance_Material_Menu = 16974554; // 0x10302da
+ field public static final int TextAppearance_Material_Notification = 16974560; // 0x10302e0
+ field public static final int TextAppearance_Material_Notification_Emphasis = 16974565; // 0x10302e5
+ field public static final int TextAppearance_Material_Notification_Info = 16974563; // 0x10302e3
+ field public static final int TextAppearance_Material_Notification_Line2 = 16974562; // 0x10302e2
+ field public static final int TextAppearance_Material_Notification_Time = 16974564; // 0x10302e4
+ field public static final int TextAppearance_Material_Notification_Title = 16974561; // 0x10302e1
field public static final int TextAppearance_Material_SearchResult_Subtitle = 16974357; // 0x1030215
field public static final int TextAppearance_Material_SearchResult_Title = 16974358; // 0x1030216
field public static final int TextAppearance_Material_Small = 16974359; // 0x1030217
@@ -2063,13 +2073,6 @@
field public static final int TextAppearance_StatusBar_EventContent = 16973927; // 0x1030067
field public static final int TextAppearance_StatusBar_EventContent_Title = 16973928; // 0x1030068
field public static final int TextAppearance_StatusBar_Icon = 16973926; // 0x1030066
- field public static final int TextAppearance_StatusBar_Material = 16974559; // 0x10302df
- field public static final int TextAppearance_StatusBar_Material_EventContent = 16974560; // 0x10302e0
- field public static final int TextAppearance_StatusBar_Material_EventContent_Emphasis = 16974565; // 0x10302e5
- field public static final int TextAppearance_StatusBar_Material_EventContent_Info = 16974563; // 0x10302e3
- field public static final int TextAppearance_StatusBar_Material_EventContent_Line2 = 16974562; // 0x10302e2
- field public static final int TextAppearance_StatusBar_Material_EventContent_Time = 16974564; // 0x10302e4
- field public static final int TextAppearance_StatusBar_Material_EventContent_Title = 16974561; // 0x10302e1
field public static final int TextAppearance_StatusBar_Title = 16973925; // 0x1030065
field public static final int TextAppearance_SuggestionHighlight = 16974104; // 0x1030118
field public static final int TextAppearance_Theme = 16973888; // 0x1030040
@@ -2628,6 +2631,7 @@
field public static final int Widget_Toolbar = 16974339; // 0x1030203
field public static final int Widget_Toolbar_Button_Navigation = 16974340; // 0x1030204
field public static final int Widget_WebView = 16973875; // 0x1030033
+ field public static final int __removed = 16974559; // 0x10302df
field public static final int l_resource_pad1 = 16974336; // 0x1030200
field public static final int l_resource_pad10 = 16974327; // 0x10301f7
field public static final int l_resource_pad11 = 16974326; // 0x10301f6
@@ -3512,7 +3516,7 @@
method public void onTrimMemory(int);
method public void onUserInteraction();
method protected void onUserLeaveHint();
- method public void onVisibleBehindCancelled();
+ method public void onVisibleBehindCanceled();
method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
method public void onWindowFocusChanged(boolean);
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback);
@@ -3607,8 +3611,7 @@
method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
method public boolean clearApplicationUserData();
method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
- method public int getAppTaskThumbnailHeight();
- method public int getAppTaskThumbnailWidth();
+ method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
method public int getLargeMemoryClass();
@@ -5747,7 +5750,7 @@
}
public final class UsageStatsManager {
- method public android.util.ArrayMap<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
+ method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
field public static final int INTERVAL_BEST = 4; // 0x4
@@ -7148,6 +7151,7 @@
field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
+ field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
field public static final java.lang.String SCHEME_CONTENT = "content";
field public static final java.lang.String SCHEME_FILE = "file";
@@ -7285,8 +7289,8 @@
method public void registerComponentCallbacks(android.content.ComponentCallbacks);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
- method public abstract void removeStickyBroadcast(android.content.Intent);
- method public abstract void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
+ method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void sendBroadcast(android.content.Intent);
method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
@@ -7295,10 +7299,10 @@
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendStickyBroadcast(android.content.Intent);
- method public abstract void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
- method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract deprecated void sendStickyBroadcast(android.content.Intent);
+ method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public abstract deprecated void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract deprecated void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void setTheme(int);
method public abstract deprecated void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method public abstract deprecated void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -14919,6 +14923,31 @@
ctor public MediaCryptoException(java.lang.String);
}
+ public class MediaDescription implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.CharSequence getDescription();
+ method public android.os.Bundle getExtras();
+ method public android.graphics.Bitmap getIconBitmap();
+ method public android.net.Uri getIconUri();
+ method public java.lang.String getMediaId();
+ method public java.lang.CharSequence getSubtitle();
+ method public java.lang.CharSequence getTitle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static class MediaDescription.Builder {
+ ctor public MediaDescription.Builder();
+ method public android.media.MediaDescription build();
+ method public android.media.MediaDescription.Builder setDescription(java.lang.CharSequence);
+ method public android.media.MediaDescription.Builder setExtras(android.os.Bundle);
+ method public android.media.MediaDescription.Builder setIconBitmap(android.graphics.Bitmap);
+ method public android.media.MediaDescription.Builder setIconUri(android.net.Uri);
+ method public android.media.MediaDescription.Builder setMediaId(java.lang.String);
+ method public android.media.MediaDescription.Builder setSubtitle(java.lang.CharSequence);
+ method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
+ }
+
public final class MediaDrm {
ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
method public void closeSession(byte[]);
@@ -15097,7 +15126,7 @@
method public boolean containsKey(java.lang.String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
- method public android.media.MediaMetadata.Description getDescription();
+ method public android.media.MediaDescription getDescription();
method public long getLong(java.lang.String);
method public android.media.Rating getRating(java.lang.String);
method public java.lang.String getString(java.lang.String);
@@ -15125,6 +15154,7 @@
field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+ field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
@@ -15145,14 +15175,6 @@
method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
}
- public final class MediaMetadata.Description {
- method public java.lang.CharSequence getDescription();
- method public android.graphics.Bitmap getIcon();
- method public android.net.Uri getIconUri();
- method public java.lang.CharSequence getSubtitle();
- method public java.lang.CharSequence getTitle();
- }
-
public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
@@ -15703,6 +15725,7 @@
}
public class Ringtone {
+ method public android.media.AudioAttributes getAudioAttributes();
method public deprecated int getStreamType();
method public java.lang.String getTitle(android.content.Context);
method public boolean isPlaying();
@@ -16238,7 +16261,6 @@
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
- method public void loadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowser.IconCallback);
method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(android.net.Uri);
}
@@ -16250,27 +16272,12 @@
method public void onConnectionSuspended();
}
- public static abstract class MediaBrowser.IconCallback {
- ctor public MediaBrowser.IconCallback();
- method public void onError(android.net.Uri);
- method public void onIconLoaded(android.net.Uri, android.graphics.Bitmap);
- }
-
- public static abstract class MediaBrowser.SubscriptionCallback {
- ctor public MediaBrowser.SubscriptionCallback();
- method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowserItem>);
- method public void onError(android.net.Uri);
- }
-
- public final class MediaBrowserItem implements android.os.Parcelable {
+ public static class MediaBrowser.MediaItem implements android.os.Parcelable {
+ ctor public MediaBrowser.MediaItem(int, android.media.MediaDescription);
method public int describeContents();
- method public android.os.Bundle getExtras();
+ method public android.media.MediaDescription getDescription();
method public int getFlags();
- method public int getIconResourceId();
- method public android.net.Uri getIconUri();
- method public java.lang.CharSequence getSummary();
- method public java.lang.CharSequence getTitle();
- method public android.net.Uri getUri();
+ method public java.lang.String getMediaId();
method public boolean isBrowsable();
method public boolean isPlayable();
method public void writeToParcel(android.os.Parcel, int);
@@ -16279,37 +16286,10 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
- public static final class MediaBrowserItem.Builder {
- ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
- method public android.media.browse.MediaBrowserItem build();
- method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
- method public android.media.browse.MediaBrowserItem.Builder setIconResourceId(int);
- method public android.media.browse.MediaBrowserItem.Builder setIconUri(android.net.Uri);
- method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
- }
-
- public abstract class MediaBrowserService extends android.app.Service {
- ctor public MediaBrowserService();
- method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public android.media.session.MediaSession.Token getSessionToken();
- method public void notifyChildrenChanged(android.net.Uri);
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.browse.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
- method public abstract void onLoadChildren(android.net.Uri, android.media.browse.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowserItem>>);
- method public abstract void onLoadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowserService.Result<android.graphics.Bitmap>);
- method public void setSessionToken(android.media.session.MediaSession.Token);
- field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
- }
-
- public static final class MediaBrowserService.BrowserRoot {
- ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
- method public android.os.Bundle getExtras();
- method public android.net.Uri getRootUri();
- }
-
- public class MediaBrowserService.Result {
- method public void detach();
- method public void sendResult(T);
+ public static abstract class MediaBrowser.SubscriptionCallback {
+ ctor public MediaBrowser.SubscriptionCallback();
+ method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+ method public void onError(android.net.Uri);
}
}
@@ -16404,7 +16384,7 @@
method public java.lang.String getPackageName();
method public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
method public android.media.session.PlaybackState getPlaybackState();
- method public java.util.List<android.media.session.MediaSession.Item> getQueue();
+ method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
method public android.app.PendingIntent getSessionActivity();
@@ -16421,7 +16401,7 @@
method public void onExtrasChanged(android.os.Bundle);
method public void onMetadataChanged(android.media.MediaMetadata);
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
- method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Item>);
+ method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
@@ -16441,16 +16421,16 @@
method public void fastForward();
method public void pause();
method public void play();
+ method public void playFromMediaId(java.lang.String, android.os.Bundle);
method public void playFromSearch(java.lang.String, android.os.Bundle);
- method public void playUri(android.net.Uri, android.os.Bundle);
method public void rewind();
method public void seekTo(long);
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
- method public void skipToItem(long);
method public void skipToNext();
method public void skipToPrevious();
+ method public void skipToQueueItem(long);
method public void stop();
}
@@ -16471,7 +16451,7 @@
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
method public void setPlaybackToRemote(android.media.VolumeProvider);
- method public void setQueue(java.util.List<android.media.session.MediaSession.Item>);
+ method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setSessionActivity(android.app.PendingIntent);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
@@ -16486,34 +16466,27 @@
method public boolean onMediaButtonEvent(android.content.Intent);
method public void onPause();
method public void onPlay();
+ method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
- method public void onPlayUri(android.net.Uri, android.os.Bundle);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
- method public void onSkipToItem(long);
method public void onSkipToNext();
method public void onSkipToPrevious();
+ method public void onSkipToQueueItem(long);
method public void onStop();
}
- public static final class MediaSession.Item implements android.os.Parcelable {
+ public static final class MediaSession.QueueItem implements android.os.Parcelable {
+ ctor public MediaSession.QueueItem(android.media.MediaDescription, long);
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public long getId();
- method public android.media.MediaMetadata getMetadata();
- method public android.net.Uri getUri();
+ method public android.media.MediaDescription getDescription();
+ method public long getQueueId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int UNKNOWN_ID = -1; // 0xffffffff
}
- public static final class MediaSession.Item.Builder {
- ctor public MediaSession.Item.Builder(android.media.MediaMetadata, long, android.net.Uri);
- method public android.media.session.MediaSession.Item build();
- method public android.media.session.MediaSession.Item.Builder setExtras(android.os.Bundle);
- }
-
public static final class MediaSession.Token implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -16534,6 +16507,7 @@
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
+ method public long getActiveQueueItemId();
method public long getBufferedPosition();
method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
method public java.lang.CharSequence getErrorMessage();
@@ -16545,15 +16519,15 @@
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
field public static final long ACTION_PLAY = 4L; // 0x4L
+ field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
- field public static final long ACTION_PLAY_URI = 1024L; // 0x400L
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
- field public static final long ACTION_SKIP_TO_ITEM = 4096L; // 0x1000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+ field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
@@ -16567,6 +16541,7 @@
field public static final int STATE_REWINDING = 5; // 0x5
field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+ field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
field public static final int STATE_STOPPED = 1; // 0x1
}
@@ -16577,7 +16552,7 @@
method public android.media.session.PlaybackState.Builder addCustomAction(android.media.session.PlaybackState.CustomAction);
method public android.media.session.PlaybackState build();
method public android.media.session.PlaybackState.Builder setActions(long);
- method public android.media.session.PlaybackState.Builder setActiveItem(long);
+ method public android.media.session.PlaybackState.Builder setActiveQueueItemId(long);
method public android.media.session.PlaybackState.Builder setBufferedPosition(long);
method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
@@ -17226,10 +17201,6 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract interface NetworkBoundURLFactory {
- method public abstract java.net.URL getBoundURL(android.net.Network, java.net.URL) throws java.net.MalformedURLException;
- }
-
public final class NetworkCapabilities implements android.os.Parcelable {
ctor public NetworkCapabilities(android.net.NetworkCapabilities);
method public int describeContents();
@@ -27093,6 +27064,33 @@
}
+package android.service.media {
+
+ public abstract class MediaBrowserService extends android.app.Service {
+ ctor public MediaBrowserService();
+ method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public android.media.session.MediaSession.Token getSessionToken();
+ method public void notifyChildrenChanged(android.net.Uri);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+ method public abstract void onLoadChildren(android.net.Uri, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
+ method public void setSessionToken(android.media.session.MediaSession.Token);
+ field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+ }
+
+ public static final class MediaBrowserService.BrowserRoot {
+ ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
+ method public android.os.Bundle getExtras();
+ method public android.net.Uri getRootUri();
+ }
+
+ public class MediaBrowserService.Result {
+ method public void detach();
+ method public void sendResult(T);
+ }
+
+}
+
package android.service.notification {
public abstract class NotificationListenerService extends android.app.Service {
@@ -27103,9 +27101,11 @@
method public final void cancelNotifications(java.lang.String[]);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+ method public final int getCurrentInterruptionFilter();
method public final int getCurrentListenerHints();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
method public android.os.IBinder onBind(android.content.Intent);
+ method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
method public void onListenerHintsChanged(int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
@@ -27113,13 +27113,12 @@
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+ method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
- field public static final int HINTS_NONE = 0; // 0x0
- field public static final int HINT_HOST_DISABLE_EFFECTS = 4; // 0x4
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1; // 0x1
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3; // 0x3
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2; // 0x2
- field public static final int HOST_INTERRUPTION_LEVEL_MASK = 3; // 0x3
+ field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+ field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+ field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+ field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
@@ -27197,16 +27196,14 @@
package android.service.voice {
public class AlwaysOnHotwordDetector {
- method public android.content.Intent getManageIntent(int);
+ method public android.content.Intent createIntentToEnroll();
+ method public android.content.Intent createIntentToReEnroll();
+ method public android.content.Intent createIntentToUnEnroll();
method public int getSupportedRecognitionModes();
method public boolean startRecognition(int);
method public boolean stopRecognition();
- field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0
- field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1
- field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2
field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
- field public static final int RECOGNITION_FLAG_NONE = 0; // 0x0
field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
@@ -27215,7 +27212,8 @@
field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
}
- public static abstract interface AlwaysOnHotwordDetector.Callback {
+ public static abstract class AlwaysOnHotwordDetector.Callback {
+ ctor public AlwaysOnHotwordDetector.Callback();
method public abstract void onAvailabilityChanged(int);
method public abstract void onDetected(android.service.voice.AlwaysOnHotwordDetector.EventPayload);
method public abstract void onError();
@@ -28422,6 +28420,7 @@
}
public static class PhoneAccount.Builder {
+ ctor public PhoneAccount.Builder();
method public android.telecomm.PhoneAccount build();
method public android.telecomm.PhoneAccount.Builder withAccountHandle(android.telecomm.PhoneAccountHandle);
method public android.telecomm.PhoneAccount.Builder withCapabilities(int);
@@ -32234,17 +32233,23 @@
field public static final android.util.Rational ZERO;
}
- public final class Size {
+ public final class Size implements android.os.Parcelable {
ctor public Size(int, int);
+ method public int describeContents();
method public int getHeight();
method public int getWidth();
method public static android.util.Size parseSize(java.lang.String) throws java.lang.NumberFormatException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
- public final class SizeF {
+ public final class SizeF implements android.os.Parcelable {
ctor public SizeF(float, float);
+ method public int describeContents();
method public float getHeight();
method public float getWidth();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
public class SparseArray implements java.lang.Cloneable {
@@ -39585,22 +39590,6 @@
}
-package com.android.internal.telecomm {
-
- public abstract interface RemoteServiceCallback implements android.os.IInterface {
- method public abstract void onError() throws android.os.RemoteException;
- method public abstract void onResult(java.util.List<android.content.ComponentName>, java.util.List<android.os.IBinder>) throws android.os.RemoteException;
- }
-
- public static abstract class RemoteServiceCallback.Stub extends android.os.Binder implements com.android.internal.telecomm.RemoteServiceCallback {
- ctor public RemoteServiceCallback.Stub();
- method public android.os.IBinder asBinder();
- method public static com.android.internal.telecomm.RemoteServiceCallback asInterface(android.os.IBinder);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- }
-
-}
-
package com.android.internal.util {
public abstract interface Predicate {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 74ccbc2..6e77e13 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -161,17 +161,17 @@
LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
"Error constructing dalvik cache : %s", strerror(errno));
- int result = mkdir(dalvikCacheDir, 0771);
+ int result = mkdir(dalvikCacheDir, 0711);
LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
"Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));
// We always perform these steps because the directory might
// already exist, with wider permissions and a different owner
// than we'd like.
- result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM);
+ result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);
LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));
- result = chmod(dalvikCacheDir, 0771);
+ result = chmod(dalvikCacheDir, 0711);
LOG_ALWAYS_FATAL_IF((result < 0),
"Error changing dalvik-cache permissions : %s", strerror(errno));
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d9b40b1..da34094 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -866,6 +866,7 @@
private void runInstall() {
int installFlags = PackageManager.INSTALL_ALL_USERS;
+ int userId = UserHandle.USER_ALL;
String installerPackageName = null;
String opt;
@@ -909,6 +910,13 @@
}
} else if (opt.equals("--abi")) {
abi = checkAbiArgument(nextOptionData());
+ } else if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextOptionData());
+ if (userId == UserHandle.USER_ALL) {
+ installFlags |= PackageManager.INSTALL_ALL_USERS;
+ } else {
+ installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+ }
} else {
System.err.println("Error: Unknown option: " + opt);
return;
@@ -953,8 +961,8 @@
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
- mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
- verificationParams, abi);
+ mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
+ verificationParams, abi, userId);
synchronized (obs) {
while (!obs.finished) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c80eeb9..2503d17 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5459,7 +5459,7 @@
* this method anytime before a return from {@link #onPause()}. If this call is successful
* then the activity will remain visible when {@link #onPause()} is called, and can continue to
* play media in the background, but it must stop playing and release resources prior to or
- * within the call to {@link #onVisibleBehindCancelled()}. If this call returns false, the
+ * within the call to {@link #onVisibleBehindCanceled()}. If this call returns false, the
* activity will not be visible in the background, and must release any media resources
* immediately.
*
@@ -5475,10 +5475,10 @@
* @return the resulting visibiity state. If true the activity may remain visible beyond
* {@link #onPause()}. If false then the activity may not count on being visible behind
* other translucent activities, and must stop any media playback and release resources.
- * Returning false may occur in lieu of a call to onVisibleBehindCancelled() so the return
+ * Returning false may occur in lieu of a call to onVisibleBehindCanceled() so the return
* value must be checked.
*
- * @see #onVisibleBehindCancelled()
+ * @see #onVisibleBehindCanceled()
* @see #onBackgroundVisibleBehindChanged(boolean)
*/
public boolean requestVisibleBehind(boolean visible) {
@@ -5498,7 +5498,7 @@
/**
* Called when a translucent activity over this activity is becoming opaque or another
* activity is being launched. Activities that override this method must call
- * <code>super.onVisibleBehindCancelled()</code> or a SuperNotCalledException will be thrown.
+ * <code>super.onVisibleBehindCanceled()</code> or a SuperNotCalledException will be thrown.
*
* <p>When this method is called the activity has 500 msec to release any resources it may be
* using while visible in the background.
@@ -5509,7 +5509,7 @@
* @see #requestVisibleBehind(boolean)
* @see #onBackgroundVisibleBehindChanged(boolean)
*/
- public void onVisibleBehindCancelled() {
+ public void onVisibleBehindCanceled() {
mCalled = true;
}
@@ -5521,7 +5521,7 @@
* {@link #requestVisibleBehind(boolean)}, false otherwise.
*
* @see #requestVisibleBehind(boolean)
- * @see #onVisibleBehindCancelled()
+ * @see #onVisibleBehindCanceled()
* @see #onBackgroundVisibleBehindChanged(boolean)
* @hide
*/
@@ -5544,7 +5544,7 @@
* @param visible true if a background activity is visible, false otherwise.
*
* @see #requestVisibleBehind(boolean)
- * @see #onVisibleBehindCancelled()
+ * @see #onVisibleBehindCanceled()
* @hide
*/
@SystemApi
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ffb9c95..bc54055 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -52,6 +52,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
+import android.util.Size;
import android.util.Slog;
import java.io.FileDescriptor;
@@ -1026,24 +1027,13 @@
}
/**
- * Return the current design width for {@link AppTask} thumbnails, for use
+ * Return the current design dimensions for {@link AppTask} thumbnails, for use
* with {@link #addAppTask}.
*/
- public int getAppTaskThumbnailWidth() {
+ public Size getAppTaskThumbnailSize() {
synchronized (this) {
ensureAppTaskThumbnailSizeLocked();
- return mAppTaskThumbnailSize.x;
- }
- }
-
- /**
- * Return the current design height for {@link AppTask} thumbnails, for use
- * with {@link #addAppTask}.
- */
- public int getAppTaskThumbnailHeight() {
- synchronized (this) {
- ensureAppTaskThumbnailSizeLocked();
- return mAppTaskThumbnailSize.y;
+ return new Size(mAppTaskThumbnailSize.x, mAppTaskThumbnailSize.y);
}
}
@@ -1072,9 +1062,9 @@
* set on it.
* @param description Optional additional description information.
* @param thumbnail Thumbnail to use for the recents entry. Should be the size given by
- * {@link #getAppTaskThumbnailWidth()} and {@link #getAppTaskThumbnailHeight()}. If the
- * bitmap is not that exact size, it will be recreated in your process, probably in a way
- * you don't like, before the recents entry is added.
+ * {@link #getAppTaskThumbnailSize()}. If the bitmap is not that exact size, it will be
+ * recreated in your process, probably in a way you don't like, before the recents entry
+ * is added.
*
* @return Returns the task id of the newly added app task, or -1 if the add failed. The
* most likely cause of failure is that there is no more room for more tasks for your app.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d70e5df..38999a8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2485,11 +2485,11 @@
final Activity activity = r.activity;
if (activity.mVisibleBehind) {
activity.mCalled = false;
- activity.onVisibleBehindCancelled();
+ activity.onVisibleBehindCanceled();
// Tick, tick, tick. The activity has 500 msec to return or it will be destroyed.
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
- " did not call through to super.onVisibleBehindCancelled()");
+ " did not call through to super.onVisibleBehindCanceled()");
}
activity.mVisibleBehind = false;
}
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index e4f2b88..a09a2e7 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -217,7 +217,10 @@
}
protected void viewsReady(ArrayMap<String, View> sharedElements) {
- setSharedElements(sharedElements);
+ sharedElements.retainAll(mAllSharedElementNames);
+ mListener.remapSharedElements(mAllSharedElementNames, sharedElements);
+ mSharedElementNames.addAll(sharedElements.keySet());
+ mSharedElements.addAll(sharedElements.values());
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
mTransitioningViews.removeAll(mSharedElements);
@@ -339,32 +342,16 @@
protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
ArrayList<View> localViews) {
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
- if (!mAllSharedElementNames.isEmpty()) {
- if (accepted != null) {
- for (int i = 0; i < accepted.size(); i++) {
- sharedElements.put(accepted.get(i), localViews.get(i));
- }
- } else {
- getDecor().findNamedViews(sharedElements);
+ if (accepted != null) {
+ for (int i = 0; i < accepted.size(); i++) {
+ sharedElements.put(accepted.get(i), localViews.get(i));
}
+ } else {
+ getDecor().findNamedViews(sharedElements);
}
return sharedElements;
}
- private void setSharedElements(ArrayMap<String, View> sharedElements) {
- sharedElements.retainAll(mAllSharedElementNames);
- mListener.remapSharedElements(mAllSharedElementNames, sharedElements);
- sharedElements.retainAll(mAllSharedElementNames);
- for (int i = 0; i < mAllSharedElementNames.size(); i++) {
- String name = mAllSharedElementNames.get(i);
- View sharedElement = sharedElements.get(name);
- if (sharedElement != null) {
- mSharedElementNames.add(name);
- mSharedElements.add(sharedElement);
- }
- }
- }
-
protected void setResultReceiver(ResultReceiver resultReceiver) {
mResultReceiver = resultReceiver;
}
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 1e556d6..f79d32b 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -135,6 +135,9 @@
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
break;
+ case BUTTON_NEGATIVE:
+ cancel();
+ break;
}
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 75ecbd9..5a6898d 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -327,7 +327,7 @@
public void run() {
if (mAnimations++ < MIN_ANIMATION_FRAMES) {
getDecor().postOnAnimation(this);
- } else {
+ } else if (mResultReceiver != null) {
mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
mResultReceiver = null; // all done sending messages.
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 43b9ea8..f31800d 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -174,6 +174,7 @@
});
setGhostVisibility(View.INVISIBLE);
scheduleGhostVisibilityChange(View.INVISIBLE);
+ mListener.setSharedElementEnd(mSharedElementNames, mSharedElements, sharedElementSnapshots);
TransitionManager.beginDelayedTransition(getDecor(), transition);
scheduleGhostVisibilityChange(View.VISIBLE);
setGhostVisibility(View.VISIBLE);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 07e9a94..dbd180f 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
+import android.os.Bundle;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
@@ -58,11 +59,15 @@
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
- ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
+ ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
+ void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
+ int getInterruptionFilterFromListener(in INotificationListener token);
+ void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
ComponentName getEffectsSuppressor();
+ boolean matchesCallFilter(in Bundle extras);
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e1dc8bf..966d2ce 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1424,6 +1424,8 @@
extras.remove(Notification.EXTRA_LARGE_ICON);
extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
extras.remove(Notification.EXTRA_PICTURE);
+ // Prevent light notifications from being rebuilt.
+ extras.remove(Builder.EXTRA_NEEDS_REBUILD);
}
}
@@ -1868,6 +1870,15 @@
private Notification mRebuildNotification = null;
/**
+ * Whether the build notification has three lines. This is used to make the top padding for
+ * both the contracted and expanded layout consistent.
+ *
+ * <p>
+ * This field is only valid during the build phase.
+ */
+ private boolean mHasThreeLines;
+
+ /**
* Constructs a new Builder with the defaults:
*
@@ -2564,19 +2575,23 @@
return this;
}
- private Bitmap getProfileBadge() {
+ private Drawable getProfileBadgeDrawable() {
// Note: This assumes that the current user can read the profile badge of the
// originating user.
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- Drawable badge = userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+ return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+ }
+
+ private Bitmap getProfileBadge() {
+ Drawable badge = getProfileBadgeDrawable();
if (badge == null) {
return null;
}
- final int width = badge.getIntrinsicWidth();
- final int height = badge.getIntrinsicHeight();
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final int size = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_badge_size);
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
- badge.setBounds(0, 0, width, height);
+ badge.setBounds(0, 0, size, size);
badge.draw(canvas);
return bitmap;
}
@@ -2602,6 +2617,12 @@
return false;
}
+ private void shrinkLine3Text(RemoteViews contentView) {
+ float subTextSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_subtext_size);
+ contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+ }
+
private RemoteViews applyStandardTemplate(int resId) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(),
mOriginatingUserId, resId);
@@ -2674,10 +2695,7 @@
if (showLine2) {
// need to shrink all the type to make sure everything fits
- final Resources res = mContext.getResources();
- final float subTextSize = res.getDimensionPixelSize(
- R.dimen.notification_subtext_size);
- contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+ shrinkLine3Text(contentView);
}
if (mWhen != 0 && mShowWhen) {
@@ -2696,7 +2714,7 @@
// Adjust padding depending on line count and font size.
contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
- hasThreeLines(), mContext.getResources().getConfiguration().fontScale),
+ mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
0, 0);
// We want to add badge to first line of text.
@@ -2721,7 +2739,12 @@
* is going to have one or two lines
*/
private boolean hasThreeLines() {
- boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0;
+ boolean contentTextInLine2 = mSubText != null && mContentText != null;
+
+ // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
+ boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
+ boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
+ || badgeInLine3;
boolean hasLine2 = (mSubText != null && mContentText != null) ||
(mSubText == null && (mProgressMax != 0 || mProgressIndeterminate));
return hasLine2 && hasLine3;
@@ -3092,6 +3115,7 @@
if (mRebuildNotification == null) {
throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
}
+ mHasThreeLines = hasThreeLines();
Bundle extras = mRebuildNotification.extras;
@@ -3124,6 +3148,7 @@
}
extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
+ mHasThreeLines = false;
return mRebuildNotification;
}
@@ -3238,6 +3263,7 @@
*/
public Notification build() {
mOriginatingUserId = mContext.getUserId();
+ mHasThreeLines = hasThreeLines();
Notification n = buildUnstyled();
@@ -3259,6 +3285,7 @@
mStyle.addExtras(n.extras);
}
+ mHasThreeLines = false;
return n;
}
@@ -3388,7 +3415,7 @@
*/
protected void applyTopPadding(RemoteViews contentView) {
int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
- mBuilder.hasThreeLines(),
+ mBuilder.mHasThreeLines,
mBuilder.mContext.getResources().getConfiguration().fontScale);
contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
}
@@ -3661,6 +3688,8 @@
applyTopPadding(contentView);
+ mBuilder.shrinkLine3Text(contentView);
+
mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
return contentView;
@@ -3800,6 +3829,8 @@
applyTopPadding(contentView);
+ mBuilder.shrinkLine3Text(contentView);
+
mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
return contentView;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fc047de..7dc1ad64 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -251,5 +252,17 @@
}
}
+ /**
+ * @hide
+ */
+ public boolean matchesCallFilter(Bundle extras) {
+ INotificationManager service = getService();
+ try {
+ return service.matchesCallFilter(extras);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
private Context mContext;
}
diff --git a/core/java/android/app/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java
index e3f0d54..7cbbbc3 100644
--- a/core/java/android/app/backup/BackupHelper.java
+++ b/core/java/android/app/backup/BackupHelper.java
@@ -37,10 +37,9 @@
*/
public interface BackupHelper {
/**
- * Based on <code>oldState</code>, determine which of the files from the
- * application's data directory need to be backed up, write them to
- * <code>data</code>, and fill in <code>newState</code> with the state as it
- * exists now.
+ * Based on <code>oldState</code>, determine what application content
+ * needs to be backed up, write it to <code>data</code>, and fill in
+ * <code>newState</code> with the complete state as it exists now.
* <p>
* Implementing this method is much like implementing
* {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 2431ad0..fb80de2 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -106,7 +106,9 @@
}
/**
- * The time at which this event occurred.
+ * The time at which this event occurred, measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getTimeStamp() {
return mTimeStamp;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index e47a802..abfc435 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -81,28 +81,36 @@
}
/**
- * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents.
+ * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getFirstTimeStamp() {
return mBeginTimeStamp;
}
/**
- * Get the end of the time range this {@link android.app.usage.UsageStats} represents.
+ * Get the end of the time range this {@link android.app.usage.UsageStats} represents,
+ * measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getLastTimeStamp() {
return mEndTimeStamp;
}
/**
- * Get the last time this package was used.
+ * Get the last time this package was used, measured in milliseconds since the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
*/
public long getLastTimeUsed() {
return mLastTimeUsed;
}
/**
- * Get the total time this package spent in the foreground.
+ * Get the total time this package spent in the foreground, measured in milliseconds.
*/
public long getTotalTimeInForeground() {
return mTotalTimeInForeground;
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f9b8928..5830fcf 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Provides access to device usage history and statistics. Usage data is aggregated into
@@ -149,7 +150,6 @@
* @param endTime The exclusive end of the range of events to include in the results.
* @return A {@link UsageEvents}.
*/
- @SuppressWarnings("unchecked")
public UsageEvents queryEvents(long beginTime, long endTime) {
try {
UsageEvents iter = mService.queryEvents(beginTime, endTime,
@@ -170,15 +170,13 @@
*
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
- * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are
+ * @return A {@link java.util.Map} keyed by package name, or null if no stats are
* available.
*/
- public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
+ public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
if (stats.isEmpty()) {
- @SuppressWarnings("unchecked")
- ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY;
- return emptyStats;
+ return Collections.emptyMap();
}
ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b13792b..b2b48e8 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -27,6 +27,7 @@
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.database.IContentObserver;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -161,6 +162,17 @@
public static final String SCHEME_FILE = "file";
/**
+ * An extra {@link Point} describing the optimal size for a requested image
+ * resource, in pixels. If a provider has multiple sizes of the image, it
+ * should return the image closest to this size.
+ *
+ * @see #openTypedAssetFileDescriptor(Uri, String, Bundle)
+ * @see #openTypedAssetFileDescriptor(Uri, String, Bundle,
+ * CancellationSignal)
+ */
+ public static final String EXTRA_SIZE = "android.content.extra.SIZE";
+
+ /**
* This is the Android platform's base MIME type for a content: URI
* containing a Cursor of a single item. Applications should use this
* as the base type along with their own sub-type of their content: URIs
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7d7c25..f979a0c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1486,8 +1486,6 @@
* @see #sendBroadcast(Intent)
* @see #sendBroadcast(Intent, String)
* @see #sendOrderedBroadcast(Intent, String)
- * @see #sendStickyBroadcast(Intent)
- * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
* @see android.content.BroadcastReceiver
* @see #registerReceiver
* @see android.app.Activity#RESULT_OK
@@ -1584,7 +1582,7 @@
@Nullable Bundle initialExtras);
/**
- * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+ * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
* Intent you are sending stays around after the broadcast is complete,
* so that others can quickly retrieve that data through the return
* value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In
@@ -1595,6 +1593,12 @@
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast, and the Intent will be held to
* be re-broadcast to future receivers.
@@ -1602,10 +1606,11 @@
* @see #sendBroadcast(Intent)
* @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
*/
+ @Deprecated
public abstract void sendStickyBroadcast(Intent intent);
/**
- * Version of {@link #sendStickyBroadcast} that allows you to
+ * <p>Version of {@link #sendStickyBroadcast} that allows you to
* receive data back from the broadcast. This is accomplished by
* supplying your own BroadcastReceiver when calling, which will be
* treated as a final receiver at the end of the broadcast -- its
@@ -1622,6 +1627,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
* @param resultReceiver Your own BroadcastReceiver to treat as the final
@@ -1644,31 +1655,45 @@
* @see #registerReceiver
* @see android.app.Activity#RESULT_OK
*/
+ @Deprecated
public abstract void sendStickyOrderedBroadcast(Intent intent,
BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
/**
- * Remove the data previously sent with {@link #sendStickyBroadcast},
+ * <p>Remove the data previously sent with {@link #sendStickyBroadcast},
* so that it is as if the sticky broadcast had never happened.
*
* <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY}
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent that was previously broadcast.
*
* @see #sendStickyBroadcast
*/
+ @Deprecated
public abstract void removeStickyBroadcast(Intent intent);
/**
- * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
+ * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
* that are not pre-installed on the system image. Using it requires holding
* the INTERACT_ACROSS_USERS permission.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast, and the Intent will be held to
* be re-broadcast to future receivers.
@@ -1676,10 +1701,11 @@
*
* @see #sendBroadcast(Intent)
*/
+ @Deprecated
public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user);
/**
- * Version of
+ * <p>Version of
* {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
* that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
@@ -1688,6 +1714,12 @@
*
* <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
* @param user UserHandle to send the intent to.
@@ -1705,13 +1737,14 @@
*
* @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
*/
+ @Deprecated
public abstract void sendStickyOrderedBroadcastAsUser(Intent intent,
UserHandle user, BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
/**
- * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
+ * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
* user the broadcast will be sent to. This is not available to applications
* that are not pre-installed on the system image. Using it requires holding
* the INTERACT_ACROSS_USERS permission.
@@ -1720,11 +1753,18 @@
* permission in order to use this API. If you do not hold that
* permission, {@link SecurityException} will be thrown.
*
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
* @param intent The Intent that was previously broadcast.
* @param user UserHandle to remove the sticky broadcast from.
*
* @see #sendStickyBroadcastAsUser
*/
+ @Deprecated
public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user);
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 44478d4..bd1487c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -198,6 +198,14 @@
in VerificationParams verificationParams,
in String packageAbiOverride);
+ void installPackageAsUser(in String originPath,
+ in IPackageInstallObserver2 observer,
+ int flags,
+ in String installerPackageName,
+ in VerificationParams verificationParams,
+ in String packageAbiOverride,
+ int userId);
+
void finishPackageInstall(int token);
void setInstallerPackageName(in String targetPackage, in String installerPackageName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c928a18..44e24b1 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -623,7 +623,7 @@
try {
final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
offsetBytes, lengthBytes);
- return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
+ return new FileBridge.FileBridgeOutputStream(clientSocket);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index ec4bc7d..0895fe3 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -19,6 +19,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -48,13 +49,23 @@
*/
public final class TotalCaptureResult extends CaptureResult {
+ private final List<CaptureResult> mPartialResults;
+
/**
- * Takes ownership of the passed-in properties object
+ * Takes ownership of the passed-in camera metadata and the partial results
+ *
+ * @param partials a list of partial results; {@code null} will be substituted for an empty list
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras) {
+ CaptureResultExtras extras, List<CaptureResult> partials) {
super(results, parent, extras);
+
+ if (partials == null) {
+ mPartialResults = new ArrayList<>();
+ } else {
+ mPartialResults = partials;
+ }
}
/**
@@ -65,6 +76,8 @@
*/
public TotalCaptureResult(CameraMetadataNative results, int sequenceId) {
super(results, sequenceId);
+
+ mPartialResults = new ArrayList<>();
}
/**
@@ -73,14 +86,13 @@
* <p>The list is returned is unmodifiable; attempting to modify it will result in a
* {@code UnsupportedOperationException} being thrown.</p>
*
- * <p>The list size will be inclusive between {@code 1} and
- * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order
+ * <p>The list size will be inclusive between {@code 0} and
+ * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, with elements in ascending order
* of when {@link CameraCaptureSession.CaptureListener#onCaptureProgressed} was invoked.</p>
*
* @return unmodifiable list of partial results
*/
public List<CaptureResult> getPartialResults() {
- // TODO
- return Collections.unmodifiableList(null);
+ return Collections.unmodifiableList(mPartialResults);
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 513d222..f5666bf 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -41,6 +41,7 @@
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -721,6 +722,13 @@
checkIfCameraClosedOrInError();
mDeviceHandler.post(mCallOnBusy);
+
+ // If already idle, just do a busy->idle transition immediately, don't actually
+ // flush.
+ if (mIdle) {
+ mDeviceHandler.post(mCallOnIdle);
+ return;
+ }
try {
LongParcelable lastFrameNumberRef = new LongParcelable();
mRemoteDevice.flush(/*out*/lastFrameNumberRef);
@@ -960,6 +968,8 @@
private long mCompletedFrameNumber = -1;
private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+ /** Map frame numbers to list of partial results */
+ private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
private void update() {
Iterator<Long> iter = mFutureErrorSet.iterator();
@@ -976,8 +986,8 @@
/**
* This function is called every time when a result or an error is received.
- * @param frameNumber: the frame number corresponding to the result or error
- * @param isError: true if it is an error, false if it is not an error
+ * @param frameNumber the frame number corresponding to the result or error
+ * @param isError true if it is an error, false if it is not an error
*/
public void updateTracker(long frameNumber, boolean isError) {
if (isError) {
@@ -999,6 +1009,55 @@
update();
}
+ /**
+ * This function is called every time a result has been completed.
+ *
+ * <p>It keeps a track of all the partial results already created for a particular
+ * frame number.</p>
+ *
+ * @param frameNumber the frame number corresponding to the result
+ * @param result the total or partial result
+ * @param partial {@true} if the result is partial, {@code false} if total
+ */
+ public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
+
+ if (!partial) {
+ // Update the total result's frame status as being successful
+ updateTracker(frameNumber, /*isError*/false);
+ // Don't keep a list of total results, we don't need to track them
+ return;
+ }
+
+ if (result == null) {
+ // Do not record blank results; this also means there will be no total result
+ // so it doesn't matter that the partials were not recorded
+ return;
+ }
+
+ // Partial results must be aggregated in-order for that frame number
+ List<CaptureResult> partials = mPartialResults.get(frameNumber);
+ if (partials == null) {
+ partials = new ArrayList<>();
+ mPartialResults.put(frameNumber, partials);
+ }
+
+ partials.add(result);
+ }
+
+ /**
+ * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
+ *
+ * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
+ * is called again with new partials for that frame number).</p>
+ *
+ * @param frameNumber the frame number corresponding to the result
+ * @return a list of partial results for that frame with at least 1 element,
+ * or {@code null} if there were no partials recorded for that frame
+ */
+ public List<CaptureResult> popPartialResults(long frameNumber) {
+ return mPartialResults.remove(frameNumber);
+ }
+
public long getCompletedFrameNumber() {
return mCompletedFrameNumber;
}
@@ -1237,12 +1296,6 @@
boolean isPartialResult =
(resultExtras.getPartialResultCount() < mTotalPartialCount);
- // Update tracker (increment counter) when it's not a partial result.
- if (!isPartialResult) {
- mFrameNumberTracker.updateTracker(frameNumber,
- /*error*/false);
- }
-
// Check if we have a listener for this
if (holder == null) {
if (DEBUG) {
@@ -1250,6 +1303,9 @@
"holder is null, early return at frame "
+ frameNumber);
}
+
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+
return;
}
@@ -1259,6 +1315,8 @@
"camera is closed, early return at frame "
+ frameNumber);
}
+
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
return;
}
@@ -1266,6 +1324,8 @@
Runnable resultDispatch = null;
+ CaptureResult finalResult;
+
// Either send a partial result or the final capture completed result
if (isPartialResult) {
final CaptureResult resultAsCapture =
@@ -1283,9 +1343,14 @@
}
}
};
+
+ finalResult = resultAsCapture;
} else {
+ List<CaptureResult> partialResults =
+ mFrameNumberTracker.popPartialResults(frameNumber);
+
final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras);
+ new TotalCaptureResult(result, request, resultExtras, partialResults);
// Final capture result
resultDispatch = new Runnable() {
@@ -1299,10 +1364,15 @@
}
}
};
+
+ finalResult = resultAsCapture;
}
holder.getHandler().post(resultDispatch);
+ // Collect the partials for a total result; or mark the frame as totally completed
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
+
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
checkAndFireSequenceComplete();
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index febb015..f47ce79 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -67,7 +67,6 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -655,6 +654,15 @@
private Face[] getFaces() {
Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+ byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
+ Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
+ int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
+ int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
+
+ if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) {
+ return null;
+ }
+
if (faceDetectMode == null) {
Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
@@ -670,8 +678,6 @@
}
// Face scores and rectangles are required by SIMPLE and FULL mode.
- byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
- Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
if (faceScores == null || faceRectangles == null) {
Log.w(TAG, "Expect face scores and rectangles to be non-null");
return new Face[0];
@@ -683,8 +689,6 @@
// To be safe, make number of faces is the minimal of all face info metadata length.
int numFaces = Math.min(faceScores.length, faceRectangles.length);
// Face id and landmarks are only required by FULL mode.
- int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
- int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
if (faceIds == null || faceLandmarks == null) {
Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
@@ -755,22 +759,32 @@
private LensShadingMap getLensShadingMap() {
float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+ Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+ // Do not warn if lsmArray is null while s is not. This is valid.
if (lsmArray == null) {
- Log.w(TAG, "getLensShadingMap - Lens shading map was null.");
return null;
}
- Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+ if (s == null) {
+ Log.w(TAG, "getLensShadingMap - Lens shading map size was null.");
+ return null;
+ }
+
LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
return map;
}
private Location getGpsLocation() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
- Location l = new Location(translateProcessToLocationProvider(processingMethod));
-
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
+ if (areValuesAllNull(processingMethod, coords, timeStamp)) {
+ return null;
+ }
+
+ Location l = new Location(translateProcessToLocationProvider(processingMethod));
if (timeStamp != null) {
l.setTime(timeStamp);
} else {
@@ -873,7 +887,13 @@
float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
+
+ if (areValuesAllNull(red, green, blue)) {
+ return null;
+ }
+
if (red == null || green == null || blue == null) {
+ Log.w(TAG, "getTonemapCurve - missing tone curve components");
return null;
}
TonemapCurve tc = new TonemapCurve(red, green, blue);
@@ -1208,6 +1228,18 @@
}
}
+ /** Check if input arguments are all {@code null}.
+ *
+ * @param objs Input arguments for null check
+ * @return {@code true} if input arguments are all {@code null}, otherwise {@code false}
+ */
+ private static boolean areValuesAllNull(Object... objs) {
+ for (Object o : objs) {
+ if (o != null) return false;
+ }
+ return true;
+ }
+
static {
/*
* We use a class initializer to allow the native code to cache some field offsets
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 85e7531..2ec6126 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -166,7 +166,7 @@
public String toString() {
StringBuffer s = new StringBuffer();
s.append("port_id: ").append(mId).append(", ");
- s.append("address: ").append(mAddress).append(", ");
+ s.append("address: ").append(String.format("0x%04x", mAddress)).append(", ");
s.append("cec: ").append(mCecSupported).append(", ");
s.append("arc: ").append(mArcSupported).append(", ");
s.append("mhl: ").append(mMhlSupported);
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 22da90e..8df9916 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -45,7 +45,7 @@
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
private static final boolean DBG = true;
- private static final boolean VDBG = true;
+ private static final boolean VDBG = false;
private final Context mContext;
private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
@@ -134,7 +134,7 @@
throw new IllegalArgumentException();
}
- if (DBG) log("Registering NetworkAgent");
+ if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
@@ -148,7 +148,7 @@
if (mAsyncChannel != null) {
log("Received new connection while already connected!");
} else {
- if (DBG) log("NetworkAgent fully connected");
+ if (VDBG) log("NetworkAgent fully connected");
AsyncChannel ac = new AsyncChannel();
ac.connected(null, this, msg.replyTo);
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
@@ -164,7 +164,7 @@
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
- if (DBG) log("CMD_CHANNEL_DISCONNECT");
+ if (VDBG) log("CMD_CHANNEL_DISCONNECT");
if (mAsyncChannel != null) mAsyncChannel.disconnect();
break;
}
diff --git a/core/java/android/net/NetworkBoundURLFactory.java b/core/java/android/net/NetworkBoundURLFactory.java
deleted file mode 100644
index 356100e..0000000
--- a/core/java/android/net/NetworkBoundURLFactory.java
+++ /dev/null
@@ -1,35 +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.net;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * An interface that describes a factory for network-specific {@link URL} objects.
- */
-public interface NetworkBoundURLFactory {
- /**
- * Returns a {@link URL} based on the given URL but bound to the specified {@code Network},
- * such that opening the URL will send all network traffic on the specified Network.
- *
- * @return a {@link URL} bound to this {@code Network}.
- * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the
- * specified URL (e.g., if it does not support the protocol of the URL).
- */
- public URL getBoundURL(Network network, URL url) throws MalformedURLException;
-}
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index a20e8e7..6ddd8b3 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -46,6 +46,7 @@
**/
public class NetworkFactory extends Handler {
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
private static final int BASE = Protocol.BASE_NETWORK_FACTORY;
/**
@@ -164,13 +165,14 @@
private void handleAddRequest(NetworkRequest request, int score) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
+ if (DBG) log("got request " + request + " with score " + score);
n = new NetworkRequestInfo(request, score);
mNetworkRequests.put(n.request.requestId, n);
} else {
+ if (VDBG) log("new score " + score + " for exisiting request " + request);
n.score = score;
}
- if (DBG) log("got request " + request + " with score " + score);
- if (DBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
+ if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
evalRequest(n);
}
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index bf8d15c..022a106 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -131,10 +131,17 @@
}
public static class FileBridgeOutputStream extends OutputStream {
+ private final ParcelFileDescriptor mClientPfd;
private final FileDescriptor mClient;
private final byte[] mTemp = new byte[MSG_LENGTH];
+ public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
+ mClientPfd = clientPfd;
+ mClient = clientPfd.getFileDescriptor();
+ }
+
public FileBridgeOutputStream(FileDescriptor client) {
+ mClientPfd = null;
mClient = client;
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index d9d4398..0202f91 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -350,6 +350,15 @@
public static final String PHONE_ACCOUNT_ID = "subscription_id";
/**
+ * The identifier of a account that is unique to a specified component. Equivalent value
+ * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only.
+ * <P>Type: INTEGER</P>
+ *
+ * @hide
+ */
+ public static final String SUB_ID = "sub_id";
+
+ /**
* If a successful call is made that is longer than this duration, update the phone number
* in the ContactsProvider with the normalized version of the number, based on the user's
* current country code.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 327fe4a..9a0858a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -511,8 +511,6 @@
public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
/** {@hide} */
- public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size";
- /** {@hide} */
public static final String EXTRA_URI = "uri";
private static final String PATH_ROOT = "root";
@@ -819,7 +817,7 @@
ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
throws RemoteException, IOException {
final Bundle openOpts = new Bundle();
- openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
+ openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
AssetFileDescriptor afd = null;
Bitmap bitmap = null;
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 021fff4..270d786 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -16,7 +16,6 @@
package android.provider;
-import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE;
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
@@ -763,8 +762,8 @@
public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
enforceTree(uri);
- if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
- final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
+ if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) {
+ final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE);
return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
@@ -781,8 +780,8 @@
Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
enforceTree(uri);
- if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
- final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
+ if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) {
+ final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE);
return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 95d1351..33e0468 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -904,6 +904,15 @@
public static final String ACTION_APP_NOTIFICATION_SETTINGS
= "android.settings.APP_NOTIFICATION_SETTINGS";
+ /**
+ * Activity Action: Show notification redaction settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_NOTIFICATION_REDACTION
+ = "android.settings.ACTION_APP_NOTIFICATION_REDACTION";
+
/** @hide */ public static final String EXTRA_APP_UID = "app_uid";
/** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package";
@@ -2113,6 +2122,13 @@
public static final String VOLUME_MASTER_MUTE = "volume_master_mute";
/**
+ * Microphone mute (int 1 = mute, 0 = not muted).
+ *
+ * @hide
+ */
+ public static final String MICROPHONE_MUTE = "microphone_mute";
+
+ /**
* Whether the notifications should use the ring volume (value of 1) or
* a separate notification volume (value of 0). In most cases, users
* will have this enabled so the notification and ringer volumes will be
@@ -3707,6 +3723,14 @@
"lock_screen_allow_private_notifications";
/**
+ * Set by the system to track if the user needs to see the call to action for
+ * the lockscreen notification policy.
+ * @hide
+ */
+ public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
+ "show_note_about_notification_hiding";
+
+ /**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
* @deprecated This identifier is poorly initialized and has
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 93b2d3b..8ca9b6c 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -29,4 +29,5 @@
in NotificationRankingUpdate update);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
-}
\ No newline at end of file
+ void onInterruptionFilterChanged(int interruptionFilter);
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 450b9a7..cb0bcf2 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -58,26 +58,55 @@
private final String TAG = NotificationListenerService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
- /** {@link #getCurrentListenerHints() Listener hints} constant - default state. */
- public static final int HINTS_NONE = 0;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * Normal interruption filter.
+ */
+ public static final int INTERRUPTION_FILTER_ALL = 1;
- /** Bitmask range for {@link #getCurrentListenerHints() Listener hints} host interruption level
- * constants. */
- public static final int HOST_INTERRUPTION_LEVEL_MASK = 0x3;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * Priority interruption filter.
+ */
+ public static final int INTERRUPTION_FILTER_PRIORITY = 2;
- /** {@link #getCurrentListenerHints() Listener hints} constant - Normal interruption level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1;
-
- /** {@link #getCurrentListenerHints() Listener hints} constant - Priority interruption level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2;
-
- /** {@link #getCurrentListenerHints() Listener hints} constant - No interruptions level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * No interruptions filter.
+ */
+ public static final int INTERRUPTION_FILTER_NONE = 3;
/** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
* should disable notification sound, vibrating and other visual or aural effects.
- * This does not change the interruption level, only the effects. **/
- public static final int HINT_HOST_DISABLE_EFFECTS = 1 << 2;
+ * This does not change the interruption filter, only the effects. **/
+ public static final int HINT_HOST_DISABLE_EFFECTS = 1;
+
+ /**
+ * The full trim of the StatusBarNotification including all its features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int TRIM_FULL = 0;
+
+ /**
+ * A light trim of the StatusBarNotification excluding the following features:
+ *
+ * <ol>
+ * <li>{@link Notification#tickerView tickerView}</li>
+ * <li>{@link Notification#contentView contentView}</li>
+ * <li>{@link Notification#largeIcon largeIcon}</li>
+ * <li>{@link Notification#bigContentView bigContentView}</li>
+ * <li>{@link Notification#headsUpContentView headsUpContentView}</li>
+ * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
+ * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
+ * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
+ * </ol>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int TRIM_LIGHT = 1;
private INotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
@@ -197,6 +226,17 @@
// optional
}
+ /**
+ * Implement this method to be notified when the
+ * {@link #getCurrentInterruptionFilter() interruption filter} changed.
+ *
+ * @param interruptionFilter The current
+ * {@link #getCurrentInterruptionFilter() interruption filter}.
+ */
+ public void onInterruptionFilterChanged(int interruptionFilter) {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -301,13 +341,53 @@
}
/**
+ * Sets the notification trim that will be received via {@link #onNotificationPosted}.
+ *
+ * <p>
+ * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
+ * full notification features right away to reduce their memory footprint. Full notifications
+ * can be requested on-demand via {@link #getActiveNotifications(int)}.
+ *
+ * <p>
+ * Set to {@link #TRIM_FULL} initially.
+ *
+ * @hide
+ *
+ * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
+ * See <code>TRIM_*</code> constants.
+ */
+ @SystemApi
+ public final void setOnNotificationPostedTrim(int trim) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
+ } catch (RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* 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.
*
* @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
- return getActiveNotifications(null);
+ return getActiveNotifications(null, TRIM_FULL);
+ }
+
+ /**
+ * 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.
+ *
+ * @hide
+ *
+ * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+ * @return An array of active notifications, sorted in natural order.
+ */
+ @SystemApi
+ public StatusBarNotification[] getActiveNotifications(int trim) {
+ return getActiveNotifications(null, trim);
}
/**
@@ -315,14 +395,33 @@
* notifications but didn't want to retain the bits, and now need to go back and extract
* more data out of those notifications.
*
+ * @param keys the keys of the notifications to request
* @return An array of notifications corresponding to the requested keys, in the
* same order as the key list.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
- if (!isBound()) return null;
+ return getActiveNotifications(keys, TRIM_FULL);
+ }
+
+ /**
+ * Request one or more notifications by key. Useful if you have been keeping track of
+ * notifications but didn't want to retain the bits, and now need to go back and extract
+ * more data out of those notifications.
+ *
+ * @hide
+ *
+ * @param keys the keys of the notifications to request
+ * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+ * @return An array of notifications corresponding to the requested keys, in the
+ * same order as the key list.
+ */
+ @SystemApi
+ public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
+ if (!isBound())
+ return null;
try {
- ParceledListSlice<StatusBarNotification> parceledList =
- getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys);
+ ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
+ .getActiveNotificationsFromListener(mWrapper, keys, trim);
List<StatusBarNotification> list = parceledList.getList();
int N = list.size();
@@ -345,15 +444,42 @@
* shared across all listeners or a feature the notification host does not support or refuses
* to grant.
*
- * @return One or more of the HINT_ constants.
+ * @return Zero or more of the HINT_ constants.
*/
public final int getCurrentListenerHints() {
- if (!isBound()) return HINTS_NONE;
+ if (!isBound()) return 0;
try {
return getNotificationInterface().getHintsFromListener(mWrapper);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
- return HINTS_NONE;
+ return 0;
+ }
+ }
+
+ /**
+ * Gets the current notification interruption filter active on the host.
+ *
+ * <p>
+ * The interruption filter defines which notifications are allowed to interrupt the user
+ * (e.g. via sound & vibration) and is applied globally. Listeners can find out whether
+ * a specific notification matched the interruption filter via
+ * {@link Ranking#matchesInterruptionFilter()}.
+ * <p>
+ * The current filter may differ from the previously requested filter if the notification host
+ * does not support or refuses to apply the requested filter, or if another component changed
+ * the filter in the meantime.
+ * <p>
+ * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+ *
+ * @return One of the INTERRUPTION_FILTER_ constants, or 0 on errors.
+ */
+ public final int getCurrentInterruptionFilter() {
+ if (!isBound()) return 0;
+ try {
+ return getNotificationInterface().getHintsFromListener(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ return 0;
}
}
@@ -361,7 +487,7 @@
* Sets the desired {@link #getCurrentListenerHints() listener hints}.
*
* <p>
- * This is merely a request, the host may or not choose to take action depending
+ * This is merely a request, the host may or may not choose to take action depending
* on other listener requests or other global state.
* <p>
* Listen for updates using {@link #onListenerHintsChanged(int)}.
@@ -378,6 +504,27 @@
}
/**
+ * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
+ *
+ * <p>
+ * This is merely a request, the host may or may not choose to apply the requested
+ * interruption filter depending on other listener requests or other global state.
+ * <p>
+ * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+ *
+ * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
+ */
+ public final void requestInterruptionFilter(int interruptionFilter) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface()
+ .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Returns current ranking information.
*
* <p>
@@ -514,6 +661,15 @@
Log.w(TAG, "Error running onListenerHintsChanged", t);
}
}
+
+ @Override
+ public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
+ try {
+ NotificationListenerService.this.onInterruptionFilterChanged(interruptionFilter);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onInterruptionFilterChanged", t);
+ }
+ }
}
private void applyUpdate(NotificationRankingUpdate update) {
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2095773..519bc28 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -84,20 +85,31 @@
private static final int STATE_NOT_READY = 0;
// Keyphrase management actions. Used in getManageIntent() ----//
- /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
MANAGE_ACTION_ENROLL,
MANAGE_ACTION_RE_ENROLL,
MANAGE_ACTION_UN_ENROLL
})
- public @interface ManageActions {}
+ private @interface ManageActions {}
- /** Indicates that we need to enroll. */
+ /**
+ * Indicates that we need to enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_ENROLL = 0;
- /** Indicates that we need to re-enroll. */
+ /**
+ * Indicates that we need to re-enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_RE_ENROLL = 1;
- /** Indicates that we need to un-enroll. */
+ /**
+ * Indicates that we need to un-enroll.
+ *
+ * @hide
+ */
public static final int MANAGE_ACTION_UN_ENROLL = 2;
//-- Flags for startRecognition ----//
@@ -111,7 +123,11 @@
})
public @interface RecognitionFlags {}
- /** Empty flag for {@link #startRecognition(int)}. */
+ /**
+ * Empty flag for {@link #startRecognition(int)}.
+ *
+ * @hide
+ */
public static final int RECOGNITION_FLAG_NONE = 0;
/**
* Recognition flag for {@link #startRecognition(int)} that indicates
@@ -264,7 +280,7 @@
/**
* Callbacks for always-on hotword detection.
*/
- public interface Callback {
+ public static abstract class Callback {
/**
* Called when the hotword availability changes.
* This indicates a change in the availability of recognition for the given keyphrase.
@@ -278,7 +294,7 @@
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
*/
- void onAvailabilityChanged(int status);
+ public abstract void onAvailabilityChanged(int status);
/**
* Called when the keyphrase is spoken.
* This implicitly stops listening for the keyphrase once it's detected.
@@ -289,23 +305,23 @@
* This may contain the trigger audio, if requested when calling
* {@link AlwaysOnHotwordDetector#startRecognition(int)}.
*/
- void onDetected(@NonNull EventPayload eventPayload);
+ public abstract void onDetected(@NonNull EventPayload eventPayload);
/**
* Called when the detection fails due to an error.
*/
- void onError();
+ public abstract void onError();
/**
* Called when the recognition is paused temporarily for some reason.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
- void onRecognitionPaused();
+ public abstract void onRecognitionPaused();
/**
* Called when the recognition is resumed after it was temporarily paused.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
- void onRecognitionResumed();
+ public abstract void onRecognitionResumed();
}
/**
@@ -372,10 +388,10 @@
/**
* Starts recognition for the associated keyphrase.
*
+ * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+ *
* @param recognitionFlags The flags to control the recognition properties.
- * The allowed flags are {@link #RECOGNITION_FLAG_NONE},
- * {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO} and
- * {@link #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS}.
* @return Indicates whether the call succeeded or not.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should only call this method after a supported state callback on
@@ -430,12 +446,13 @@
}
/**
- * Gets an intent to manage the associated keyphrase.
+ * Creates an intent to start the enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is un-enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
+ * otherwise {@link #createIntentToReEnroll()} should be preferred.
*
- * @param action The manage action that needs to be performed.
- * One of {@link #MANAGE_ACTION_ENROLL}, {@link #MANAGE_ACTION_RE_ENROLL} or
- * {@link #MANAGE_ACTION_UN_ENROLL}.
- * @return An {@link Intent} to manage the given keyphrase.
+ * @return An {@link Intent} to start enrollment for the given keyphrase.
* @throws UnsupportedOperationException if managing they keyphrase isn't supported.
* Callers should only call this method after a supported state callback on
* {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
@@ -443,10 +460,52 @@
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
- public Intent getManageIntent(@ManageActions int action) {
- if (DBG) Slog.d(TAG, "getManageIntent(" + action + ")");
+ public Intent createIntentToEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToEnroll");
synchronized (mLock) {
- return getManageIntentLocked(action);
+ return getManageIntentLocked(MANAGE_ACTION_ENROLL);
+ }
+ }
+
+ /**
+ * Creates an intent to start the un-enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+ *
+ * @return An {@link Intent} to start un-enrollment for the given keyphrase.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
+ */
+ public Intent createIntentToUnEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToUnEnroll");
+ synchronized (mLock) {
+ return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL);
+ }
+ }
+
+ /**
+ * Creates an intent to start the re-enrollment for the associated keyphrase.
+ * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+ * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+ *
+ * @return An {@link Intent} to start re-enrollment for the given keyphrase.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
+ */
+ public Intent createIntentToReEnroll() {
+ if (DBG) Slog.d(TAG, "createIntentToReEnroll");
+ synchronized (mLock) {
+ return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL);
}
}
@@ -462,12 +521,6 @@
"Managing the given keyphrase is not supported");
}
- if (action != MANAGE_ACTION_ENROLL
- && action != MANAGE_ACTION_RE_ENROLL
- && action != MANAGE_ACTION_UN_ENROLL) {
- throw new IllegalArgumentException("Invalid action specified " + action);
- }
-
return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index aecf488..e82057c 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -262,6 +262,8 @@
int fit = paraStart;
float fitWidth = w;
int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0;
+ // same as fitWidth but not including any trailing whitespace
+ float fitWidthGraphing = w;
boolean hasTabOrEmoji = false;
boolean hasTab = false;
@@ -346,6 +348,9 @@
if (w <= width || isSpaceOrTab) {
fitWidth = w;
+ if (!isSpaceOrTab) {
+ fitWidthGraphing = w;
+ }
fit = j + 1;
if (fmTop < fitTop)
@@ -365,7 +370,7 @@
breakOpp[breakOppIndex] == j - paraStart + 1;
if (isLineBreak) {
- okWidth = w;
+ okWidth = fitWidthGraphing;
ok = j + 1;
if (fitTop < okTop)
@@ -426,6 +431,7 @@
j = here - 1; // restart j-span loop from here, compensating for the j++
ok = fit = here;
w = 0;
+ fitWidthGraphing = w;
fitAscent = fitDescent = fitTop = fitBottom = 0;
okAscent = okDescent = okTop = okBottom = 0;
@@ -842,7 +848,7 @@
void prepare() {
mMeasured = MeasuredText.obtain();
}
-
+
void finish() {
mMeasured = MeasuredText.recycle(mMeasured);
}
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index ec79b36..3a63805 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,8 +22,7 @@
import android.content.res.Resources;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
+import java.nio.CharBuffer;
import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
@@ -31,15 +30,13 @@
import libcore.util.ZoneInfo;
/**
- * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
- * main issue with this implementation is the treatment of characters as ASCII, despite returning
- * localized (UTF-16) strings from the LocaleData.
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java.
*
* <p>This class is not thread safe.
*/
class TimeFormatter {
- // An arbitrary value outside the range representable by a byte / ASCII character code.
- private static final int FORCE_LOWER_CASE = 0x100;
+ // An arbitrary value outside the range representable by a char.
+ private static final int FORCE_LOWER_CASE = -1;
private static final int SECSPERMIN = 60;
private static final int MINSPERHOUR = 60;
@@ -62,10 +59,9 @@
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
- private final Locale locale;
private StringBuilder outputBuilder;
- private Formatter outputFormatter;
+ private Formatter numberFormatter;
public TimeFormatter() {
synchronized (TimeFormatter.class) {
@@ -84,7 +80,6 @@
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- this.locale = locale;
localeData = sLocaleData;
}
}
@@ -97,19 +92,21 @@
StringBuilder stringBuilder = new StringBuilder();
outputBuilder = stringBuilder;
- outputFormatter = new Formatter(stringBuilder, locale);
+ // This uses the US locale because number localization is handled separately (see below)
+ // and locale sensitive strings are output directly using outputBuilder.
+ numberFormatter = new Formatter(stringBuilder, Locale.US);
formatInternal(pattern, wallTime, zoneInfo);
String result = stringBuilder.toString();
// This behavior is the source of a bug since some formats are defined as being
- // in ASCII. Generally localization is very broken.
+ // in ASCII and not localized.
if (localeData.zeroDigit != '0') {
result = localizeDigits(result);
}
return result;
} finally {
outputBuilder = null;
- outputFormatter = null;
+ numberFormatter = null;
}
}
@@ -132,38 +129,30 @@
* {@link #outputBuilder}.
*/
private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
- // Convert to ASCII bytes to be compatible with old implementation behavior.
- byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
- if (bytes.length == 0) {
- return;
- }
-
- ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+ CharBuffer formatBuffer = CharBuffer.wrap(pattern);
while (formatBuffer.remaining() > 0) {
- boolean outputCurrentByte = true;
- char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- if (currentByteAsChar == '%') {
- outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+ boolean outputCurrentChar = true;
+ char currentChar = formatBuffer.get(formatBuffer.position());
+ if (currentChar == '%') {
+ outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
}
- if (outputCurrentByte) {
- currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- outputBuilder.append(currentByteAsChar);
+ if (outputCurrentChar) {
+ outputBuilder.append(formatBuffer.get(formatBuffer.position()));
}
-
formatBuffer.position(formatBuffer.position() + 1);
}
}
- private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+ private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime,
ZoneInfo zoneInfo) {
- // The byte at formatBuffer.position() is expected to be '%' at this point.
+ // The char at formatBuffer.position() is expected to be '%' at this point.
int modifier = 0;
while (formatBuffer.remaining() > 1) {
- // Increment the position then get the new current byte.
+ // Increment the position then get the new current char.
formatBuffer.position(formatBuffer.position() + 1);
- char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
- switch (currentByteAsChar) {
+ char currentChar = formatBuffer.get(formatBuffer.position());
+ switch (currentChar) {
case 'A':
modifyAndAppend((wallTime.getWeekDay() < 0
|| wallTime.getWeekDay() >= DAYSPERWEEK)
@@ -206,7 +195,7 @@
formatInternal("%m/%d/%y", wallTime, zoneInfo);
return false;
case 'd':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMonthDay());
return false;
case 'E':
@@ -218,46 +207,46 @@
case '0':
case '^':
case '#':
- modifier = currentByteAsChar;
+ modifier = currentChar;
continue;
case 'e':
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
wallTime.getMonthDay());
return false;
case 'F':
formatInternal("%Y-%m-%d", wallTime, zoneInfo);
return false;
case 'H':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getHour());
return false;
case 'I':
int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
return false;
case 'j':
int yearDay = wallTime.getYearDay() + 1;
- outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+ numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
yearDay);
return false;
case 'k':
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
wallTime.getHour());
return false;
case 'l':
int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
- outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+ numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
return false;
case 'M':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMinute());
return false;
case 'm':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getMonth() + 1);
return false;
case 'n':
- modifyAndAppend("\n", modifier);
+ outputBuilder.append('\n');
return false;
case 'p':
modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
@@ -274,27 +263,27 @@
formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
return false;
case 'S':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
wallTime.getSecond());
return false;
case 's':
int timeInSeconds = wallTime.mktime(zoneInfo);
- modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+ outputBuilder.append(Integer.toString(timeInSeconds));
return false;
case 'T':
formatInternal("%H:%M:%S", wallTime, zoneInfo);
return false;
case 't':
- modifyAndAppend("\t", modifier);
+ outputBuilder.append('\t');
return false;
case 'U':
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
(wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
/ DAYSPERWEEK);
return false;
case 'u':
int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
- outputFormatter.format("%d", day);
+ numberFormatter.format("%d", day);
return false;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
@@ -326,9 +315,9 @@
--year;
yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
}
- if (currentByteAsChar == 'V') {
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
- } else if (currentByteAsChar == 'g') {
+ if (currentChar == 'V') {
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+ } else if (currentChar == 'g') {
outputYear(year, false, true, modifier);
} else {
outputYear(year, true, true, modifier);
@@ -342,10 +331,10 @@
int n = (wallTime.getYearDay() + DAYSPERWEEK - (
wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
: (DAYSPERWEEK - 1))) / DAYSPERWEEK;
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
return false;
case 'w':
- outputFormatter.format("%d", wallTime.getWeekDay());
+ numberFormatter.format("%d", wallTime.getWeekDay());
return false;
case 'X':
formatInternal(timeOnlyFormat, wallTime, zoneInfo);
@@ -371,17 +360,17 @@
return false;
}
int diff = wallTime.getGmtOffset();
- String sign;
+ char sign;
if (diff < 0) {
- sign = "-";
+ sign = '-';
diff = -diff;
} else {
- sign = "+";
+ sign = '+';
}
- modifyAndAppend(sign, modifier);
+ outputBuilder.append(sign);
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
- outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+ numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
return false;
}
case '+':
@@ -422,7 +411,6 @@
break;
default:
outputBuilder.append(str);
-
}
}
@@ -443,14 +431,14 @@
}
if (outputTop) {
if (lead == 0 && trail < 0) {
- modifyAndAppend("-0", modifier);
+ outputBuilder.append("-0");
} else {
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
}
}
if (outputBottom) {
int n = ((trail < 0) ? -trail : trail);
- outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+ numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
}
}
@@ -472,24 +460,24 @@
}
/**
- * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static boolean brokenIsUpper(char toCheck) {
return toCheck >= 'A' && toCheck <= 'Z';
}
/**
- * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static boolean brokenIsLower(char toCheck) {
return toCheck >= 'a' && toCheck <= 'z';
}
/**
- * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static char brokenToLower(char input) {
if (input >= 'A' && input <= 'Z') {
@@ -499,8 +487,8 @@
}
/**
- * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
- * be compatible with the old native implementation.
+ * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in
+ * order to be compatible with the old native implementation.
*/
private static char brokenToUpper(char input) {
if (input >= 'a' && input <= 'z') {
@@ -509,11 +497,4 @@
return input;
}
- /**
- * Safely convert a byte containing an ASCII character to a char, even for character codes
- * > 127.
- */
- private static char convertToChar(byte b) {
- return (char) (b & 0xFF);
- }
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 59ba71f..bd52e71 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1656,7 +1656,7 @@
WindowId windowId = sceneRoot.getWindowId();
for (int i = numOldAnims - 1; i >= 0; i--) {
AnimationInfo info = runningAnimators.valueAt(i);
- if (info.view != null && windowId.equals(info.windowId)) {
+ if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
Animator anim = runningAnimators.keyAt(i);
anim.pause();
}
@@ -1689,7 +1689,7 @@
WindowId windowId = sceneRoot.getWindowId();
for (int i = numOldAnims - 1; i >= 0; i--) {
AnimationInfo info = runningAnimators.valueAt(i);
- if (info.view != null && windowId.equals(info.windowId)) {
+ if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
Animator anim = runningAnimators.keyAt(i);
anim.resume();
}
diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java
index d58f778..6424344 100644
--- a/core/java/android/util/Size.java
+++ b/core/java/android/util/Size.java
@@ -16,12 +16,15 @@
package android.util;
-import static com.android.internal.util.Preconditions.*;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* Immutable class for describing width and height dimensions in pixels.
*/
-public final class Size {
+public final class Size implements Parcelable {
/**
* Create a new immutable Size instance.
*
@@ -33,6 +36,11 @@
mHeight = height;
}
+ private Size(Parcel in) {
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ }
+
/**
* Get the width of the size (in pixels).
* @return width
@@ -147,6 +155,29 @@
return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mWidth);
+ out.writeInt(mHeight);
+ }
+
+ public static final Parcelable.Creator<Size> CREATOR = new Parcelable.Creator<Size>() {
+ @Override
+ public Size createFromParcel(Parcel in) {
+ return new Size(in);
+ }
+
+ @Override
+ public Size[] newArray(int size) {
+ return new Size[size];
+ }
+ };
+
private final int mWidth;
private final int mHeight;
-};
+}
diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java
index 0a8b4ed..88bb4393 100644
--- a/core/java/android/util/SizeF.java
+++ b/core/java/android/util/SizeF.java
@@ -16,7 +16,10 @@
package android.util;
-import static com.android.internal.util.Preconditions.*;
+import static com.android.internal.util.Preconditions.checkArgumentFinite;
+
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* Immutable class for describing width and height dimensions in some arbitrary
@@ -25,7 +28,7 @@
* Width and height are finite values stored as a floating point representation.
* </p>
*/
-public final class SizeF {
+public final class SizeF implements Parcelable {
/**
* Create a new immutable SizeF instance.
*
@@ -43,6 +46,11 @@
mHeight = checkArgumentFinite(height, "height");
}
+ private SizeF(Parcel in) {
+ mWidth = in.readFloat();
+ mHeight = in.readFloat();
+ }
+
/**
* Get the width of the size (as an arbitrary unit).
* @return width
@@ -103,6 +111,29 @@
return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeFloat(mWidth);
+ out.writeFloat(mHeight);
+ }
+
+ public static final Parcelable.Creator<SizeF> CREATOR = new Parcelable.Creator<SizeF>() {
+ @Override
+ public SizeF createFromParcel(Parcel in) {
+ return new SizeF(in);
+ }
+
+ @Override
+ public SizeF[] newArray(int size) {
+ return new SizeF[size];
+ }
+ };
+
private final float mWidth;
private final float mHeight;
-};
+}
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
index ed027eb..41a2e5d 100644
--- a/core/java/android/util/Spline.java
+++ b/core/java/android/util/Spline.java
@@ -20,15 +20,34 @@
* Performs spline interpolation given a set of control points.
* @hide
*/
-public final class Spline {
- private final float[] mX;
- private final float[] mY;
- private final float[] mM;
+public abstract class Spline {
- private Spline(float[] x, float[] y, float[] m) {
- mX = x;
- mY = y;
- mM = m;
+ /**
+ * Interpolates the value of Y = f(X) for given X.
+ * Clamps X to the domain of the spline.
+ *
+ * @param x The X value.
+ * @return The interpolated Y = f(X) value.
+ */
+ public abstract float interpolate(float x);
+
+ /**
+ * Creates an appropriate spline based on the properties of the control points.
+ *
+ * If the control points are monotonic then the resulting spline will preserve that and
+ * otherwise optimize for error bounds.
+ */
+ public static Spline createSpline(float[] x, float[] y) {
+ if (!isStrictlyIncreasing(x)) {
+ throw new IllegalArgumentException("The control points must all have strictly "
+ + "increasing X values.");
+ }
+
+ if (isMonotonic(y)) {
+ return createMonotoneCubicSpline(x, y);
+ } else {
+ return createLinearSpline(x, y);
+ }
}
/**
@@ -50,107 +69,229 @@
* @throws IllegalArgumentException if the control points are not monotonic.
*/
public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
- if (x == null || y == null || x.length != y.length || x.length < 2) {
- throw new IllegalArgumentException("There must be at least two control "
- + "points and the arrays must be of equal length.");
- }
-
- final int n = x.length;
- float[] d = new float[n - 1]; // could optimize this out
- float[] m = new float[n];
-
- // Compute slopes of secant lines between successive points.
- for (int i = 0; i < n - 1; i++) {
- float h = x[i + 1] - x[i];
- if (h <= 0f) {
- throw new IllegalArgumentException("The control points must all "
- + "have strictly increasing X values.");
- }
- d[i] = (y[i + 1] - y[i]) / h;
- }
-
- // Initialize the tangents as the average of the secants.
- m[0] = d[0];
- for (int i = 1; i < n - 1; i++) {
- m[i] = (d[i - 1] + d[i]) * 0.5f;
- }
- m[n - 1] = d[n - 2];
-
- // Update the tangents to preserve monotonicity.
- for (int i = 0; i < n - 1; i++) {
- if (d[i] == 0f) { // successive Y values are equal
- m[i] = 0f;
- m[i + 1] = 0f;
- } else {
- float a = m[i] / d[i];
- float b = m[i + 1] / d[i];
- if (a < 0f || b < 0f) {
- throw new IllegalArgumentException("The control points must have "
- + "monotonic Y values.");
- }
- float h = FloatMath.hypot(a, b);
- if (h > 9f) {
- float t = 3f / h;
- m[i] = t * a * d[i];
- m[i + 1] = t * b * d[i];
- }
- }
- }
- return new Spline(x, y, m);
+ return new MonotoneCubicSpline(x, y);
}
/**
- * Interpolates the value of Y = f(X) for given X.
- * Clamps X to the domain of the spline.
+ * Creates a linear spline from a given set of control points.
*
- * @param x The X value.
- * @return The interpolated Y = f(X) value.
+ * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points
+ * are monotonic.
+ *
+ * @param x The X component of the control points, strictly increasing.
+ * @param y The Y component of the control points.
+ * @return
+ *
+ * @throws IllegalArgumentException if the X or Y arrays are null, have
+ * different lengths or have fewer than 2 values.
+ * @throws IllegalArgumentException if the X components of the control points are not strictly
+ * increasing.
*/
- public float interpolate(float x) {
- // Handle the boundary cases.
- final int n = mX.length;
- if (Float.isNaN(x)) {
- return x;
- }
- if (x <= mX[0]) {
- return mY[0];
- }
- if (x >= mX[n - 1]) {
- return mY[n - 1];
- }
-
- // Find the index 'i' of the last point with smaller X.
- // We know this will be within the spline due to the boundary tests.
- int i = 0;
- while (x >= mX[i + 1]) {
- i += 1;
- if (x == mX[i]) {
- return mY[i];
- }
- }
-
- // Perform cubic Hermite spline interpolation.
- float h = mX[i + 1] - mX[i];
- float t = (x - mX[i]) / h;
- return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
- + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ public static Spline createLinearSpline(float[] x, float[] y) {
+ return new LinearSpline(x, y);
}
- // For debugging.
- @Override
- public String toString() {
- StringBuilder str = new StringBuilder();
- final int n = mX.length;
- str.append("[");
- for (int i = 0; i < n; i++) {
- if (i != 0) {
- str.append(", ");
- }
- str.append("(").append(mX[i]);
- str.append(", ").append(mY[i]);
- str.append(": ").append(mM[i]).append(")");
+ private static boolean isStrictlyIncreasing(float[] x) {
+ if (x == null || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control points.");
}
- str.append("]");
- return str.toString();
+ float prev = x[0];
+ for (int i = 1; i < x.length; i++) {
+ float curr = x[i];
+ if (curr <= prev) {
+ return false;
+ }
+ prev = curr;
+ }
+ return true;
+ }
+
+ private static boolean isMonotonic(float[] x) {
+ if (x == null || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control points.");
+ }
+ float prev = x[0];
+ for (int i = 1; i < x.length; i++) {
+ float curr = x[i];
+ if (curr < prev) {
+ return false;
+ }
+ prev = curr;
+ }
+ return true;
+ }
+
+ public static class MonotoneCubicSpline extends Spline {
+ private float[] mX;
+ private float[] mY;
+ private float[] mM;
+
+ public MonotoneCubicSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+
+ final int n = x.length;
+ float[] d = new float[n - 1]; // could optimize this out
+ float[] m = new float[n];
+
+ // Compute slopes of secant lines between successive points.
+ for (int i = 0; i < n - 1; i++) {
+ float h = x[i + 1] - x[i];
+ if (h <= 0f) {
+ throw new IllegalArgumentException("The control points must all "
+ + "have strictly increasing X values.");
+ }
+ d[i] = (y[i + 1] - y[i]) / h;
+ }
+
+ // Initialize the tangents as the average of the secants.
+ m[0] = d[0];
+ for (int i = 1; i < n - 1; i++) {
+ m[i] = (d[i - 1] + d[i]) * 0.5f;
+ }
+ m[n - 1] = d[n - 2];
+
+ // Update the tangents to preserve monotonicity.
+ for (int i = 0; i < n - 1; i++) {
+ if (d[i] == 0f) { // successive Y values are equal
+ m[i] = 0f;
+ m[i + 1] = 0f;
+ } else {
+ float a = m[i] / d[i];
+ float b = m[i + 1] / d[i];
+ if (a < 0f || b < 0f) {
+ throw new IllegalArgumentException("The control points must have "
+ + "monotonic Y values.");
+ }
+ float h = FloatMath.hypot(a, b);
+ if (h > 9f) {
+ float t = 3f / h;
+ m[i] = t * a * d[i];
+ m[i + 1] = t * b * d[i];
+ }
+ }
+ }
+
+ mX = x;
+ mY = y;
+ mM = m;
+ }
+
+ @Override
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+
+ // Perform cubic Hermite spline interpolation.
+ float h = mX[i + 1] - mX[i];
+ float t = (x - mX[i]) / h;
+ return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+ + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ }
+
+ // For debugging.
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("MonotoneCubicSpline{[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ str.append(": ").append(mM[i]).append(")");
+ }
+ str.append("]}");
+ return str.toString();
+ }
+ }
+
+ public static class LinearSpline extends Spline {
+ private final float[] mX;
+ private final float[] mY;
+ private final float[] mM;
+
+ public LinearSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+ final int N = x.length;
+ mM = new float[N-1];
+ for (int i = 0; i < N-1; i++) {
+ mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]);
+ }
+ mX = x;
+ mY = y;
+ }
+
+ @Override
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+ return mY[i] + mM[i] * (x - mX[i]);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("LinearSpline{[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ if (i < n-1) {
+ str.append(": ").append(mM[i]);
+ }
+ str.append(")");
+ }
+ str.append("]}");
+ return str.toString();
+ }
}
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 413ea04..fa4a13a 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -87,8 +87,11 @@
private float mFinalValue;
private TimeInterpolator mInterpolator;
- private boolean mStarted = false;
- private boolean mFinished = false;
+ private static final int STATE_PREPARE = 0;
+ private static final int STATE_DELAYED = 1;
+ private static final int STATE_RUNNING = 2;
+ private static final int STATE_FINISHED = 3;
+ private int mState = STATE_PREPARE;
private long mUnscaledDuration = 300;
private long mUnscaledStartDelay = 0;
@@ -142,7 +145,7 @@
}
private void checkMutable() {
- if (mStarted) {
+ if (mState != STATE_PREPARE) {
throw new IllegalStateException("Animator has already started, cannot change it now!");
}
}
@@ -170,11 +173,11 @@
throw new IllegalStateException("Missing target!");
}
- if (mStarted) {
+ if (mState != STATE_PREPARE) {
throw new IllegalStateException("Already started!");
}
- mStarted = true;
+ mState = STATE_DELAYED;
applyInterpolator();
if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
@@ -186,6 +189,7 @@
}
private void doStart() {
+ mState = STATE_RUNNING;
nStart(mNativePtr.get(), this);
// Alpha is a special snowflake that has the canonical value stored
@@ -197,11 +201,7 @@
mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
}
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationStart(this);
- }
+ notifyStartListeners();
if (mViewTarget != null) {
// Kick off a frame to start the process
@@ -209,10 +209,21 @@
}
}
+ private void notifyStartListeners() {
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationStart(this);
+ }
+ }
+
@Override
public void cancel() {
- if (!mFinished) {
- getHelper().removeDelayedAnimation(this);
+ if (mState != STATE_FINISHED) {
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
nEnd(mNativePtr.get());
final ArrayList<AnimatorListener> listeners = cloneListeners();
@@ -220,12 +231,17 @@
for (int i = 0; i < numListeners; i++) {
listeners.get(i).onAnimationCancel(this);
}
+
+ if (mViewTarget != null) {
+ // Kick off a frame to flush the state change
+ mViewTarget.invalidateViewProperty(true, false);
+ }
}
}
@Override
public void end() {
- if (!mFinished) {
+ if (mState != STATE_FINISHED) {
nEnd(mNativePtr.get());
}
}
@@ -299,12 +315,12 @@
@Override
public boolean isRunning() {
- return mStarted && !mFinished;
+ return mState == STATE_DELAYED || mState == STATE_RUNNING;
}
@Override
public boolean isStarted() {
- return mStarted;
+ return mState != STATE_PREPARE;
}
@Override
@@ -319,7 +335,11 @@
}
protected void onFinished() {
- mFinished = true;
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
+ mState = STATE_FINISHED;
final ArrayList<AnimatorListener> listeners = cloneListeners();
final int numListeners = listeners == null ? 0 : listeners.size();
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 764eea7..ca08ecc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -277,7 +277,11 @@
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
callbacks.onHardwarePreDraw(canvas);
+
+ canvas.insertReorderBarrier();
canvas.drawRenderNode(view.getDisplayList());
+ canvas.insertInorderBarrier();
+
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
mRootNodeNeedsUpdate = false;
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index bae0cfb..b73b9fa 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -430,6 +430,10 @@
}
}
mPendingAnimations.clear();
+ mPendingSetupAction = null;
+ mPendingCleanupAction = null;
+ mPendingOnStartAction = null;
+ mPendingOnEndAction = null;
mView.removeCallbacks(mAnimationStarter);
if (mRTBackend != null) {
mRTBackend.cancelAll();
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2c7ea3e..9b6f200 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1304,12 +1304,22 @@
public abstract int getVolumeControlStream();
/**
+ * Sets a {@link MediaController} to send media keys and volume changes to.
+ * If set, this should be preferred for all media keys and volume requests
+ * sent to this window.
+ *
+ * @param controller The controller for the session which should receive
+ * media keys and volume changes.
* @see android.app.Activity#setMediaController(android.media.session.MediaController)
*/
public void setMediaController(MediaController controller) {
}
/**
+ * Gets the {@link MediaController} that was previously set.
+ *
+ * @return The controller which should receive events.
+ * @see #setMediaController(android.media.session.MediaController)
* @see android.app.Activity#getMediaController()
*/
public MediaController getMediaController() {
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 7123b9a..ef8c006 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseBooleanArray;
@@ -645,6 +647,23 @@
super.onInitializeAccessibilityNodeInfo(info);
info.setCanOpenPopup(true);
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Set up the hotspot bounds to be centered on the image.
+ final Drawable d = getDrawable();
+ final Drawable bg = getBackground();
+ if (d != null && bg != null) {
+ final Rect bounds = d.getBounds();
+ final int height = bottom - top;
+ final int offset = (height - bounds.width()) / 2;
+ final int hotspotLeft = bounds.left - offset;
+ final int hotspotRight = bounds.right + offset;
+ bg.setHotspotBounds(hotspotLeft, 0, hotspotRight, height);
+ }
+ }
}
private class OverflowPopup extends MenuPopupHelper {
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index eb232fd..3b16aba 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -35,9 +35,8 @@
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-
import com.android.internal.R;
-
+import java.lang.ref.WeakReference;
/**
* <p>An editable text view that shows completion suggestions automatically
@@ -85,8 +84,8 @@
* @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor
* @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
* @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
- * @attr ref android.R.styleable#AutoCompleteTextView_dropDownVerticalOffset
- * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHorizontalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
public class AutoCompleteTextView extends EditText implements Filter.FilterListener {
static final boolean DEBUG = false;
@@ -130,7 +129,7 @@
}
public AutoCompleteTextView(Context context, AttributeSet attrs) {
- this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle);
+ this(context, attrs, R.attr.autoCompleteTextViewStyle);
}
public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -141,23 +140,17 @@
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mPopup = new ListPopupWindow(context, attrs,
- com.android.internal.R.attr.autoCompleteTextViewStyle);
+ mPopup = new ListPopupWindow(context, attrs, defStyleAttr, defStyleRes);
mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
- final TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
- mThreshold = a.getInt(
- R.styleable.AutoCompleteTextView_completionThreshold, 2);
+ mThreshold = a.getInt(R.styleable.AutoCompleteTextView_completionThreshold, 2);
mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector));
- mPopup.setVerticalOffset((int)
- a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f));
- mPopup.setHorizontalOffset((int)
- a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f));
-
+
// Get the anchor's id now, but the view won't be ready, so wait to actually get the
// view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
// Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return
@@ -167,11 +160,9 @@
// For dropdown width, the developer can specify a specific width, or MATCH_PARENT
// (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
- mPopup.setWidth(a.getLayoutDimension(
- R.styleable.AutoCompleteTextView_dropDownWidth,
+ mPopup.setWidth(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
ViewGroup.LayoutParams.WRAP_CONTENT));
- mPopup.setHeight(a.getLayoutDimension(
- R.styleable.AutoCompleteTextView_dropDownHeight,
+ mPopup.setHeight(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
ViewGroup.LayoutParams.WRAP_CONTENT));
mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
@@ -373,6 +364,8 @@
* <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
*
* @param offset the vertical offset
+ *
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
public void setDropDownVerticalOffset(int offset) {
mPopup.setVerticalOffset(offset);
@@ -382,6 +375,8 @@
* <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
*
* @return the vertical offset
+ *
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
public int getDropDownVerticalOffset() {
return mPopup.getVerticalOffset();
@@ -391,6 +386,8 @@
* <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
*
* @param offset the horizontal offset
+ *
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
public void setDropDownHorizontalOffset(int offset) {
mPopup.setHorizontalOffset(offset);
@@ -400,6 +397,8 @@
* <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
*
* @return the horizontal offset
+ *
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
public int getDropDownHorizontalOffset() {
return mPopup.getHorizontalOffset();
@@ -629,7 +628,7 @@
*/
public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
if (mObserver == null) {
- mObserver = new PopupDataSetObserver();
+ mObserver = new PopupDataSetObserver(this);
} else if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
@@ -1255,25 +1254,44 @@
}
}
- private class PopupDataSetObserver extends DataSetObserver {
+ /**
+ * Static inner listener that keeps a WeakReference to the actual AutoCompleteTextView.
+ * <p>
+ * This way, if adapter has a longer life span than the View, we won't leak the View, instead
+ * we will just leak a small Observer with 1 field.
+ */
+ private static class PopupDataSetObserver extends DataSetObserver {
+ private final WeakReference<AutoCompleteTextView> mViewReference;
+
+ private PopupDataSetObserver(AutoCompleteTextView view) {
+ mViewReference = new WeakReference<AutoCompleteTextView>(view);
+ }
+
@Override
public void onChanged() {
- if (mAdapter != null) {
+ final AutoCompleteTextView textView = mViewReference.get();
+ if (textView != null && textView.mAdapter != null) {
// If the popup is not showing already, showing it will cause
// the list of data set observers attached to the adapter to
// change. We can't do it from here, because we are in the middle
// of iterating through the list of observers.
- post(new Runnable() {
- public void run() {
- final ListAdapter adapter = mAdapter;
- if (adapter != null) {
- // This will re-layout, thus resetting mDataChanged, so that the
- // listView click listener stays responsive
- updateDropDownForFilter(adapter.getCount());
- }
- }
- });
+ textView.post(updateRunnable);
}
}
+
+ private final Runnable updateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final AutoCompleteTextView textView = mViewReference.get();
+ if (textView == null) {
+ return;
+ }
+ final ListAdapter adapter = textView.mAdapter;
+ if (adapter == null) {
+ return;
+ }
+ textView.updateDropDownForFilter(adapter.getCount());
+ }
+ };
}
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 6a514ba..3c186e3 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -40,6 +41,7 @@
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;
+import com.android.internal.R;
import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
import java.util.Locale;
@@ -208,6 +210,18 @@
*/
public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
mContext = context;
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPopupWindow,
+ defStyleAttr, defStyleRes);
+ mDropDownHorizontalOffset = a.getDimensionPixelOffset(
+ R.styleable.ListPopupWindow_dropDownHorizontalOffset, 0);
+ mDropDownVerticalOffset = a.getDimensionPixelOffset(
+ R.styleable.ListPopupWindow_dropDownVerticalOffset, 0);
+ if (mDropDownVerticalOffset != 0) {
+ mDropDownVerticalOffsetSet = true;
+ }
+ a.recycle();
+
mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
// Set the default layout direction to match the default locale one
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 602f955..56bdb9b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -176,7 +176,7 @@
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
if ((adapter = mAdapter.get()) != null) {
- mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
+ mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
} else {
Slog.w(TAG, "unbind: adapter was null");
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 9914800..98d52ff 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -49,14 +49,14 @@
*
* <p>See the <a href="{@docRoot}guide/topics/ui/controls/spinner.html">Spinners</a> guide.</p>
*
- * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
* @attr ref android.R.styleable#Spinner_dropDownSelector
- * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
* @attr ref android.R.styleable#Spinner_dropDownWidth
* @attr ref android.R.styleable#Spinner_gravity
* @attr ref android.R.styleable#Spinner_popupBackground
* @attr ref android.R.styleable#Spinner_prompt
* @attr ref android.R.styleable#Spinner_spinnerMode
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
@Widget
public class Spinner extends AbsSpinner implements OnClickListener {
@@ -209,17 +209,6 @@
ViewGroup.LayoutParams.WRAP_CONTENT);
popup.setBackgroundDrawable(a.getDrawable(
com.android.internal.R.styleable.Spinner_popupBackground));
- final int verticalOffset = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0);
- if (verticalOffset != 0) {
- popup.setVerticalOffset(verticalOffset);
- }
-
- final int horizontalOffset = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0);
- if (horizontalOffset != 0) {
- popup.setHorizontalOffset(horizontalOffset);
- }
mPopup = popup;
mForwardingListener = new ForwardingListener(this) {
@@ -303,7 +292,7 @@
*
* @param pixels Vertical offset in pixels
*
- * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
public void setDropDownVerticalOffset(int pixels) {
mPopup.setVerticalOffset(pixels);
@@ -315,7 +304,7 @@
*
* @return Vertical offset in pixels
*
- * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
*/
public int getDropDownVerticalOffset() {
return mPopup.getVerticalOffset();
@@ -327,7 +316,7 @@
*
* @param pixels Horizontal offset in pixels
*
- * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
public void setDropDownHorizontalOffset(int pixels) {
mPopup.setHorizontalOffset(pixels);
@@ -339,7 +328,7 @@
*
* @return Horizontal offset in pixels
*
- * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
*/
public int getDropDownHorizontalOffset() {
return mPopup.getHorizontalOffset();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 818efaa..ece8aa4 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -244,6 +244,16 @@
// Set the default context, since setPopupTheme() may be a no-op.
mPopupContext = mContext;
setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
+
+ final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
+ if (navIcon != null) {
+ setNavigationIcon(navIcon);
+ final CharSequence navDesc = a.getText(
+ R.styleable.Toolbar_navigationContentDescription);
+ if (!TextUtils.isEmpty(navDesc)) {
+ setNavigationContentDescription(navDesc);
+ }
+ }
a.recycle();
}
@@ -669,6 +679,8 @@
* as screen readers or tooltips.
*
* @return The navigation button's content description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
@Nullable
public CharSequence getNavigationContentDescription() {
@@ -682,6 +694,8 @@
*
* @param resId Resource ID of a content description string to set, or 0 to
* clear the description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(int resId) {
setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
@@ -694,6 +708,8 @@
*
* @param description Content description to set, or <code>null</code> to
* clear the content description
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(@Nullable CharSequence description) {
if (!TextUtils.isEmpty(description)) {
@@ -715,6 +731,8 @@
* tooltips.</p>
*
* @param resId Resource ID of a drawable to set
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(int resId) {
setNavigationIcon(getContext().getDrawable(resId));
@@ -731,6 +749,8 @@
* tooltips.</p>
*
* @param icon Drawable to set, may be null to clear the icon
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(@Nullable Drawable icon) {
if (icon != null) {
@@ -751,6 +771,8 @@
* Return the current drawable used as the navigation icon.
*
* @return The navigation icon drawable
+ *
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
*/
@Nullable
public Drawable getNavigationIcon() {
@@ -1316,6 +1338,8 @@
final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
+ final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
+ || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
@@ -1343,7 +1367,7 @@
break;
}
if (isRtl) {
- final int rd = mTitleMarginStart - collapsingMargins[1];
+ final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
right -= Math.max(0, rd);
collapsingMargins[1] = Math.max(0, -rd);
int titleRight = right;
@@ -1366,9 +1390,11 @@
subtitleRight = subtitleRight - mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
- right = Math.min(titleRight, subtitleRight);
+ if (titleHasWidth) {
+ right = Math.min(titleRight, subtitleRight);
+ }
} else {
- final int ld = mTitleMarginStart - collapsingMargins[0];
+ final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
left += Math.max(0, ld);
collapsingMargins[0] = Math.max(0, -ld);
int titleLeft = left;
@@ -1391,7 +1417,9 @@
subtitleLeft = subtitleRight + mTitleMarginEnd;
titleTop = subtitleBottom + lp.bottomMargin;
}
- left = Math.max(titleLeft, subtitleLeft);
+ if (titleHasWidth) {
+ left = Math.max(titleLeft, subtitleLeft);
+ }
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 61b4567..b6e7353 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -23,7 +23,6 @@
import android.os.AsyncTask;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Slog;
import android.widget.AbsListView;
import android.widget.GridView;
@@ -73,6 +72,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -100,7 +100,7 @@
private boolean mResolvingHome = false;
private UsageStatsManager mUsm;
- private ArrayMap<String, UsageStats> mStats;
+ private Map<String, UsageStats> mStats;
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
private boolean mRegistered;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index ee406bd..eae4427 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -993,6 +993,6 @@
} catch (RemoteException e) {
Log.w(TAG, "RemoteException:", e);
}
- return null;
+ return new BatteryStatsImpl();
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 299b0e6..69cdbff 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2487,6 +2487,16 @@
addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
}
+ public void noteCurrentTimeChangedLocked() {
+ final long currentTime = System.currentTimeMillis();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime);
+ if (isStartClockTimeValid()) {
+ mStartClockTime = currentTime;
+ }
+ }
+
public void noteProcessStartLocked(String name, int uid) {
uid = mapUid(uid);
if (isOnBattery()) {
@@ -4060,7 +4070,20 @@
}
}
+ boolean isStartClockTimeValid() {
+ return mStartClockTime > 365*24*60*60*1000L;
+ }
+
@Override public long getStartClockTime() {
+ if (!isStartClockTimeValid()) {
+ // If the last clock time we got was very small, then we hadn't had a real
+ // time yet, so try to get it again.
+ mStartClockTime = System.currentTimeMillis();
+ if (isStartClockTimeValid()) {
+ recordCurrentTimeChangeLocked(mStartClockTime, SystemClock.elapsedRealtime(),
+ SystemClock.uptimeMillis());
+ }
+ }
return mStartClockTime;
}
@@ -6799,6 +6822,16 @@
}
}
+ private void recordCurrentTimeChangeLocked(final long currentTime, final long elapsedRealtimeMs,
+ final long uptimeMs) {
+ if (mRecordingHistory) {
+ mHistoryCur.currentTime = currentTime;
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+ mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ }
+ }
+
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
@@ -8004,6 +8037,10 @@
public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
pullPendingStateUpdatesLocked();
+ // Pull the clock time. This may update the time and make a new history entry
+ // if we had originally pulled a time before the RTC was set.
+ long startClockTime = getStartClockTime();
+
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
@@ -8014,7 +8051,7 @@
out.writeInt(mStartCount);
out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
- out.writeLong(mStartClockTime);
+ out.writeLong(startClockTime);
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
@@ -8453,6 +8490,10 @@
// Need to update with current kernel wake lock counts.
pullPendingStateUpdatesLocked();
+ // Pull the clock time. This may update the time and make a new history entry
+ // if we had originally pulled a time before the RTC was set.
+ long startClockTime = getStartClockTime();
+
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime);
@@ -8463,7 +8504,7 @@
writeHistory(out, true, false);
out.writeInt(mStartCount);
- out.writeLong(mStartClockTime);
+ out.writeLong(startClockTime);
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
out.writeLong(mUptime);
@@ -8588,6 +8629,10 @@
public void prepareForDumpLocked() {
// Need to retrieve current kernel wake lock stats before printing.
pullPendingStateUpdatesLocked();
+
+ // Pull the clock time. This may update the time and make a new history entry
+ // if we had originally pulled a time before the RTC was set.
+ getStartClockTime();
}
public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 2967938..ba236f3 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -191,6 +191,20 @@
public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) {
if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
+ // It is possible that any other bit is used as a valid flag in a future release.
+ // We should reject the entire request in such a case.
+ final int KNOWN_FLAGS_MASK = InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE |
+ InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR;
+ final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
+ if (unknownFlags != 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
+ " cursorUpdateMode=" + cursorUpdateMode +
+ " unknownFlags=" + unknownFlags);
+ }
+ return false;
+ }
+
if (mIMM == null) {
// In this case, TYPE_CURSOR_ANCHOR_INFO is not handled.
// TODO: Return some notification code rather than false to indicate method that
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index 016f0ff..08e1060 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -14,10 +14,8 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="@dimen/control_inset_material">
- <ripple android:color="?attr/colorControlHighlight">
- <item android:id="@id/mask"
- android:drawable="@drawable/btn_default_mtrl_shape" />
- </ripple>
-</inset>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item android:id="@id/mask"
+ android:drawable="@drawable/btn_default_mtrl_shape" />
+</ripple>
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index d00a348..ed2b5aa 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -14,9 +14,7 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="@dimen/control_inset_material">
- <ripple android:color="?attr/colorControlHighlight">
- <item android:drawable="@drawable/btn_default_mtrl_shape" />
- </ripple>
-</inset>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item android:drawable="@drawable/btn_default_mtrl_shape" />
+</ripple>
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
index 9235c76..6d0f7f8 100644
--- a/core/res/res/drawable/btn_default_mtrl_shape.xml
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -15,12 +15,18 @@
-->
<!-- Used as the canonical button shape. -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="@dimen/control_corner_material" />
- <solid android:color="?attr/colorButtonNormal" />
- <padding android:top="@dimen/control_padding_material"
- android:bottom="@dimen/control_padding_material"
- android:left="@dimen/control_padding_material"
- android:right="@dimen/control_padding_material" />
-</shape>
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="@dimen/button_inset_horizontal_material"
+ android:insetTop="@dimen/button_inset_vertical_material"
+ android:insetRight="@dimen/button_inset_horizontal_material"
+ android:insetBottom="@dimen/button_inset_vertical_material">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/control_corner_material" />
+ <solid android:color="?attr/colorButtonNormal" />
+ <padding android:left="@dimen/button_padding_horizontal_material"
+ android:top="@dimen/button_padding_vertical_material"
+ android:right="@dimen/button_padding_horizontal_material"
+ android:bottom="@dimen/button_padding_vertical_material" />
+ </shape>
+</inset>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 9726782..f91d4cc 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -15,7 +15,10 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="@dimen/control_inset_material">
+ android:insetLeft="@dimen/button_inset_horizontal_material"
+ android:insetTop="@dimen/button_inset_vertical_material"
+ android:insetRight="@dimen/button_inset_horizontal_material"
+ android:insetBottom="@dimen/button_inset_vertical_material">
<layer-list android:paddingMode="stack">
<item>
<ripple android:color="?attr/colorControlHighlight">
@@ -25,10 +28,10 @@
<corners android:topLeftRadius="@dimen/control_corner_material"
android:topRightRadius="@dimen/control_corner_material"/>
<solid android:color="?attr/colorButtonNormal" />
- <padding android:top="@dimen/control_padding_material"
- android:bottom="@dimen/control_padding_material"
- android:left="@dimen/control_padding_material"
- android:right="@dimen/control_padding_material" />
+ <padding android:left="@dimen/button_padding_horizontal_material"
+ android:top="@dimen/button_padding_vertical_material"
+ android:right="@dimen/button_padding_horizontal_material"
+ android:bottom="@dimen/button_padding_vertical_material" />
</shape>
</item>
</ripple>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index f264b7b..ef916ed1 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -45,7 +45,7 @@
android:gravity="top"
>
<TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -53,8 +53,8 @@
android:visibility="gone"
/>
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index f8e1986..3c44141 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -49,7 +49,7 @@
android:orientation="horizontal"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -73,7 +73,7 @@
/>
</LinearLayout>
<TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Line2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-2dp"
@@ -86,7 +86,7 @@
android:visibility="gone"
/>
<TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
@@ -104,7 +104,7 @@
android:gravity="center_vertical"
>
<TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -114,7 +114,7 @@
android:fadingEdge="horizontal"
/>
<TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -146,7 +146,7 @@
android:layout_height="6dp"
android:layout_gravity="top"
android:visibility="gone"
- style="@style/Widget.StatusBar.Material.ProgressBar.Media"
+ style="@style/Widget.Material.Notification.ProgressBar.Media"
/>
</FrameLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index d9120f6..3415814 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,14 +39,15 @@
<include layout="@layout/notification_template_part_line2" />
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:gravity="top"
+ android:layout_weight="1"
>
<TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -54,8 +55,8 @@
android:visibility="gone"
/>
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 38b3ae2..2382d18 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -37,95 +37,27 @@
>
<include layout="@layout/notification_template_part_line1" />
<include layout="@layout/notification_template_part_line2" />
+
+ <!-- We can't have another vertical linear layout here with weight != 0 so this forces us to
+ put the badge on the first line. -->
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="0dp"
android:orientation="horizontal"
- android:gravity="top"
>
- <LinearLayout
+ <TextView android:id="@+id/inbox_text0"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
- android:layout_weight="1"
android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <TextView android:id="@+id/inbox_text0"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text1"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text3"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text4"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text5"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text6"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_more"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- android:text="@android:string/ellipsis"
- />
- </LinearLayout>
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
<ImageView android:id="@+id/profile_badge_large_template"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
@@ -133,6 +65,77 @@
android:visibility="gone"
/>
</LinearLayout>
+ <TextView android:id="@+id/inbox_text1"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text2"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text3"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text4"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text5"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_more"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ android:text="@android:string/ellipsis"
+ />
<FrameLayout
android:id="@+id/inbox_end_pad"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index c2fc006..db24c1f 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -50,7 +50,7 @@
android:orientation="horizontal"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -74,7 +74,7 @@
/>
</LinearLayout>
<TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Line2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-2dp"
@@ -91,7 +91,7 @@
android:layout_height="12dp"
android:layout_marginStart="8dp"
android:visibility="gone"
- style="@style/Widget.StatusBar.Material.ProgressBar"
+ style="@style/Widget.Material.Notification.ProgressBar"
/>
<LinearLayout
android:id="@+id/line3"
@@ -102,7 +102,7 @@
android:layout_marginStart="8dp"
>
<TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -112,7 +112,7 @@
android:fadingEdge="horizontal"
/>
<TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index 87dfe1f..1f0430e 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
-->
<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Time"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index c6ea6bf..7de4089 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -23,7 +23,7 @@
android:orientation="horizontal"
>
<TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_part_line2.xml b/core/res/res/layout/notification_template_part_line2.xml
index 1f95150..7e99c5e 100644
--- a/core/res/res/layout/notification_template_part_line2.xml
+++ b/core/res/res/layout/notification_template_part_line2.xml
@@ -20,14 +20,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
- android:visibility="gone"
- android:layout_weight="0"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<TextView
android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Line2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="-1dp"
@@ -39,8 +37,8 @@
android:layout_weight="1"
/>
<ImageView android:id="@+id/profile_badge_line2"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_weight="0"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index 06de2a5..6c043a0 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -24,7 +24,7 @@
android:gravity="center_vertical"
>
<TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -34,7 +34,7 @@
android:fadingEdge="horizontal"
/>
<TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -44,8 +44,8 @@
android:paddingStart="8dp"
/>
<ImageView android:id="@+id/profile_badge_line3"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
android:layout_gravity="center"
android:layout_weight="0"
android:layout_marginStart="4dp"
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
index 5982c48..37c7ebe 100644
--- a/core/res/res/layout/notification_template_part_time.xml
+++ b/core/res/res/layout/notification_template_part_time.xml
@@ -15,7 +15,7 @@
-->
<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Time"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f825c29..a798d2e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4172,10 +4172,6 @@
<attr name="completionThreshold" format="integer" min="1" />
<!-- Selector in a drop down list. -->
<attr name="dropDownSelector" format="reference|color" />
- <!-- Amount of pixels by which the drop down should be offset vertically. -->
- <attr name="dropDownVerticalOffset" format="dimension" />
- <!-- Amount of pixels by which the drop down should be offset horizontally. -->
- <attr name="dropDownHorizontalOffset" format="dimension" />
<!-- View to anchor the auto-complete dropdown to. If not specified, the text view itself
is used. -->
<attr name="dropDownAnchor" format="reference" />
@@ -4223,6 +4219,12 @@
<!-- Whether the popup window should overlap its anchor view. -->
<attr name="overlapAnchor" format="boolean" />
</declare-styleable>
+ <declare-styleable name="ListPopupWindow">
+ <!-- Amount of pixels by which the drop down should be offset vertically. -->
+ <attr name="dropDownVerticalOffset" format="dimension" />
+ <!-- Amount of pixels by which the drop down should be offset horizontally. -->
+ <attr name="dropDownHorizontalOffset" format="dimension" />
+ </declare-styleable>
<declare-styleable name="ViewAnimator">
<!-- Identifier for the animation to use when a view is shown. -->
<attr name="inAnimation" format="reference" />
@@ -4281,12 +4283,6 @@
<attr name="popupBackground" />
<!-- Window elevation to use for the dropdown in spinnerMode="dropdown". -->
<attr name="popupElevation" />
- <!-- Vertical offset from the spinner widget for positioning the dropdown in
- spinnerMode="dropdown". -->
- <attr name="dropDownVerticalOffset" />
- <!-- Horizontal offset from the spinner widget for positioning the dropdown
- in spinnerMode="dropdown". -->
- <attr name="dropDownHorizontalOffset" />
<!-- Width of the dropdown in spinnerMode="dropdown". -->
<attr name="dropDownWidth" />
<!-- Reference to a layout to use for displaying a prompt in the dropdown for
@@ -7322,6 +7318,12 @@
<!-- Reference to a theme that should be used to inflate popups
shown by widgets in the toolbar. -->
<attr name="popupTheme" format="reference" />
+ <!-- Icon drawable to use for the navigation button located at
+ the start of the toolbar. -->
+ <attr name="navigationIcon" format="reference" />
+ <!-- Text to set as the content description for the navigation button
+ located at the start of the toolbar. -->
+ <attr name="navigationContentDescription" format="string" />
</declare-styleable>
<declare-styleable name="Toolbar_LayoutParams">
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4e3abb9..77b451f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,6 +236,9 @@
<!-- Padding for notification icon when drawn with circle around it -->
<dimen name="notification_large_icon_circle_padding">11dp</dimen>
+ <!-- Size of the profile badge for notifications -->
+ <dimen name="notification_badge_size">16dp</dimen>
+
<!-- Keyguard dimensions -->
<!-- TEMP -->
<dimen name="kg_security_panel_height">600dp</dimen>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 972ae5e..f5c9299 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -33,7 +33,6 @@
<dimen name="action_button_min_width_material">48dp</dimen>
<dimen name="action_button_min_height_material">48dp</dimen>
- <dimen name="action_overflow_min_width_material">36dp</dimen>
<dimen name="text_size_display_4_material">112sp</dimen>
<dimen name="text_size_display_3_material">56sp</dimen>
@@ -64,6 +63,13 @@
<dimen name="button_elevation_material">1dp</dimen>
<!-- Z translation to apply when button is pressed -->
<dimen name="button_pressed_z_material">2dp</dimen>
+ <!-- Default insets (outer padding) around buttons -->
+ <dimen name="button_inset_vertical_material">6dp</dimen>
+ <dimen name="button_inset_horizontal_material">@dimen/control_inset_material</dimen>
+ <!-- Default inner padding within buttons -->
+ <dimen name="button_padding_vertical_material">@dimen/control_padding_material</dimen>
+ <dimen name="button_padding_horizontal_material">8dp</dimen>
+
<!-- Default insets (outer padding) around controls -->
<dimen name="control_inset_material">4dp</dimen>
<!-- Default inner padding within controls -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 4c59f73..bd24f3e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -87,4 +87,6 @@
<item type="id" name="transitionPosition" />
<item type="id" name="transitionTransform" />
<item type="id" name="parentMatrix" />
+ <item type="id" name="statusBarBackground" />
+ <item type="id" name="navigationBarBackground" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index eca6c72..5e76a87 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2280,12 +2280,16 @@
<public type="attr" name="reparentWithOverlay" />
<public type="attr" name="ambientShadowAlpha" />
<public type="attr" name="spotShadowAlpha" />
+ <public type="attr" name="navigationIcon" />
+ <public type="attr" name="navigationContentDescription" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
<public-padding type="id" name="l_resource_pad" end="0x01020040" />
<public type="id" name="mask" />
+ <public type="id" name="statusBarBackground" />
+ <public type="id" name="navigationBarBackground" />
<public-padding type="style" name="l_resource_pad" end="0x01030200" />
@@ -2527,13 +2531,13 @@
<public type="style" name="Theme.Leanback.FormWizard"/>
- <public type="style" name="TextAppearance.StatusBar.Material" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent.Title" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent.Line2" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent.Info" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent.Time" />
- <public type="style" name="TextAppearance.StatusBar.Material.EventContent.Emphasis" />
+ <public type="style" name="__removed" />
+ <public type="style" name="TextAppearance.Material.Notification" />
+ <public type="style" name="TextAppearance.Material.Notification.Title" />
+ <public type="style" name="TextAppearance.Material.Notification.Line2" />
+ <public type="style" name="TextAppearance.Material.Notification.Info" />
+ <public type="style" name="TextAppearance.Material.Notification.Time" />
+ <public type="style" name="TextAppearance.Material.Notification.Emphasis" />
<public type="style" name="Widget.Material.Spinner.Underlined" />
<public type="style" name="Widget.Material.Light.Spinner.Underlined" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d6224da..f1ec5d2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3642,9 +3642,9 @@
<!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
<string name="sms_short_code_confirm_message"><b><xliff:g id="app_name">%1$s</xliff:g></b> would like to send a message to <b><xliff:g id="dest_address">%2$s</xliff:g></b>.</string>
<!-- Message details for the SMS short code confirmation dialog (possible premium short code). [CHAR LIMIT=NONE] -->
- <string name="sms_short_code_details">This <font fgcolor="#ffffb060">may cause charges</font> on your mobile account.</string>
+ <string name="sms_short_code_details">This <b>may cause charges</b> on your mobile account.</string>
<!-- Message details for the SMS short code confirmation dialog (premium short code). [CHAR LIMIT=NONE] -->
- <string name="sms_premium_short_code_details"><font fgcolor="#ffffb060">This will cause charges on your mobile account.</font></string>
+ <string name="sms_premium_short_code_details"><b>This will cause charges on your mobile account.</b></string>
<!-- Text of the approval button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
<string name="sms_short_code_confirm_allow">Send</string>
<!-- Text of the cancel button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 3ee5552..e783cd6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -306,7 +306,7 @@
</style>
<style name="TextAppearance.Material.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Menu" />
- <style name="TextAppearance.Material.Widget.TextView.SpinnerItem" />
+ <style name="TextAppearance.Material.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Menu" />
<style name="TextAppearance.Material.Widget.DropDownItem" parent="TextAppearance.Material.Menu">
<item name="textColor">?attr/textColorPrimaryDisableOnly</item>
@@ -410,37 +410,35 @@
<item name="textSize">@dimen/datepicker_year_label_text_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material" />
-
- <style name="TextAppearance.StatusBar.Material.EventContent">
+ <style name="TextAppearance.Material.Notification">
<item name="textColor">@color/secondary_text_material_light</item>
<item name="textSize">@dimen/notification_text_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Title">
+ <style name="TextAppearance.Material.Notification.Title">
<item name="textColor">@color/primary_text_default_material_light</item>
<item name="textSize">@dimen/notification_title_text_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Line2">
+ <style name="TextAppearance.Material.Notification.Line2">
<item name="textSize">@dimen/notification_subtext_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Info">
+ <style name="TextAppearance.Material.Notification.Info">
<item name="textSize">@dimen/notification_subtext_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Time">
+ <style name="TextAppearance.Material.Notification.Time">
<item name="textSize">@dimen/notification_subtext_size</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Emphasis">
+ <style name="TextAppearance.Material.Notification.Emphasis">
<item name="textColor">#66000000</item>
</style>
- <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
+ <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
- <style name="Widget.StatusBar.Material.ProgressBar.Media">
+ <style name="Widget.Material.Notification.ProgressBar.Media">
<item name="progressDrawable">@drawable/notification_material_media_progress</item>
</style>
@@ -746,6 +744,7 @@
<item name="popupElevation">@dimen/floating_window_z</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
+ <item name="overlapAnchor">true</item>
<item name="dropDownWidth">wrap_content</item>
<item name="popupPromptView">@layout/simple_dropdown_hint</item>
<item name="gravity">start|center_vertical</item>
@@ -829,6 +828,7 @@
<style name="Widget.Material.PopupMenu.Overflow">
<item name="overlapAnchor">true</item>
+ <item name="dropDownHorizontalOffset">-4dip</item>
</style>
<style name="Widget.Material.ActionButton" parent="Widget.ActionButton">
@@ -837,6 +837,8 @@
<item name="gravity">center</item>
<item name="scaleType">center</item>
<item name="maxLines">2</item>
+ <item name="paddingStart">0dp</item>
+ <item name="paddingEnd">0dp</item>
</style>
<style name="Widget.Material.ActionButton.CloseMode">
@@ -847,8 +849,9 @@
<item name="src">@drawable/ic_menu_moreoverflow_material</item>
<item name="background">?attr/actionBarItemBackground</item>
<item name="contentDescription">@string/action_menu_overflow_description</item>
- <item name="minWidth">@dimen/action_overflow_min_width_material</item>
+ <item name="minWidth">@dimen/action_button_min_width_material</item>
<item name="minHeight">@dimen/action_button_min_height_material</item>
+ <item name="paddingEnd">12dp</item>
<item name="scaleType">center</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d8c29b0..622a01a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -357,6 +357,7 @@
<java-symbol type="dimen" name="notification_top_pad_large_text" />
<java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
<java-symbol type="dimen" name="notification_large_icon_circle_padding" />
+ <java-symbol type="dimen" name="notification_badge_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
<java-symbol type="dimen" name="circular_display_mask_offset" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a79bd0a..89fac13 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1257,4 +1257,6 @@
<item name="colorAccent">@color/material_deep_teal_500</item>
</style>
+ <!-- TODO: Spacer to be removed from here and public.xml -->
+ <style name="__removed" />
</resources>
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index ee6b814..bed4b4d 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -28,12 +28,6 @@
<li>{@link android.app.admin.DevicePolicyManager}</li>
<li>{@link android.app.admin.DeviceAdminInfo}</li>
</ol>
- <h2>Related samples</h2>
- <ol>
- <li><a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-DeviceAdminSample</a></li>
-</ol>
</div>
</div>
@@ -232,18 +226,12 @@
<h2 id="sample">Sample Application</h2>
-<p>The examples used in this document are based on the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-Device Administration API
-sample</a>, which is included in the SDK samples. For information on downloading and
-installing the SDK samples, see <a
-href="{@docRoot}resources/samples/get.html">
-Getting the Samples</a>. Here is the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-complete code</a> for
-the sample. </p>
-<p>The
-sample application offers a demo of device admin features. It presents users
+<p>The examples used in this document are based on the Device Administration API
+sample, which is included in the SDK samples (available through the
+Android SDK Manager) and located on your system as
+<code><sdk_root>/ApiDemos/app/src/main/java/com/example/android/apis/app/DeviceAdminSample.java</code>.</p>
+
+<p>The sample application offers a demo of device admin features. It presents users
with a user interface that lets them enable the device admin application. Once
they've enabled the application, they can use the buttons in the user interface
to do the following:</p>
@@ -676,7 +664,8 @@
<p>You can also programmatically tell the device to lock immediately:</p>
<pre>
DevicePolicyManager mDPM;
-mDPM.lockNow();</pre>
+mDPM.lockNow();
+</pre>
@@ -692,12 +681,12 @@
<pre>
DevicePolicyManager mDPM;
mDPM.wipeData(0);</pre>
-<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
-additional options. Currently the value must be 0. </p>
+<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its
+ parameter a bit mask of additional options. Currently the value must be 0. </p>
<h4>Disable camera</h4>
<p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p>
-<p>You control whether the camera is disabled by using the
+<p>You control whether the camera is disabled by using the
{@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p>
<pre>private CheckBoxPreference mDisableCameraCheckbox;
@@ -708,8 +697,8 @@
</pre>
-<h4 id=storage">Storage encryption</h4>
-<p>Beginning with Android 3.0, you can use the
+<h4 id="storage">Storage encryption</h4>
+<p>Beginning with Android 3.0, you can use the
{@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()}
method to set a policy requiring encryption of the storage area, where supported.</p>
@@ -722,5 +711,5 @@
mDPM.setStorageEncryption(mDeviceAdminSample, true);
</pre>
<p>
-See the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> Device Administration API sample</a> for a complete
-example of how to enable storage encryption.</p>
+See the Device Administration API sample for a complete example of how to enable storage encryption.
+</p>
\ No newline at end of file
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 11568d2..c65efe4 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1557,7 +1557,7 @@
};
const char16_t* valueToString(const Res_value* value, size_t stringBlock,
char16_t tmpBuffer[TMP_BUFFER_SIZE],
- size_t* outLen);
+ size_t* outLen) const;
struct bag_entry {
ssize_t stringBlock;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3f014ef..690b1d6 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2939,7 +2939,7 @@
for (size_t i = 0; i < bags->size(); i++) {
TABLE_NOISY(printf("type=%d\n", i));
const TypeList& typeList = types[i];
- if (typeList.isEmpty()) {
+ if (!typeList.isEmpty()) {
bag_set** typeBags = bags->get(i);
TABLE_NOISY(printf("typeBags=%p\n", typeBags));
if (typeBags) {
@@ -3728,7 +3728,7 @@
const char16_t* ResTable::valueToString(
const Res_value* value, size_t stringBlock,
- char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
+ char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
{
if (!value) {
return NULL;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a10c387..5808d20 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -26,6 +26,7 @@
Idmap_test.cpp \
ResTable_test.cpp \
Split_test.cpp \
+ Theme_test.cpp \
TypeWrappers_test.cpp \
ZipUtils_test.cpp
diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp
index 17f91ca..92af7fe 100644
--- a/libs/androidfw/tests/BackupData_test.cpp
+++ b/libs/androidfw/tests/BackupData_test.cpp
@@ -45,7 +45,7 @@
class BackupDataTest : public testing::Test {
protected:
char* m_external_storage;
- char* m_filename;
+ String8 mFilename;
String8 mKey1;
String8 mKey2;
String8 mKey3;
@@ -53,15 +53,13 @@
virtual void SetUp() {
m_external_storage = getenv("EXTERNAL_STORAGE");
+ mFilename.append(m_external_storage);
+ mFilename.append(TEST_FILENAME);
- const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1;
- m_filename = new char[totalLen];
- snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME);
-
- ::unlink(m_filename);
- int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ ::unlink(mFilename.string());
+ int fd = ::open(mFilename.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << m_filename << " for writing";
+ FAIL() << "Couldn't create " << mFilename.string() << " for writing";
}
mKey1 = String8(KEY1);
mKey2 = String8(KEY2);
@@ -74,7 +72,7 @@
};
TEST_F(BackupDataTest, WriteAndReadSingle) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
@@ -83,7 +81,7 @@
<< "WriteEntityData returned an error";
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
EXPECT_EQ(NO_ERROR, reader->Status())
<< "Reader ctor failed";
@@ -116,7 +114,7 @@
}
TEST_F(BackupDataTest, WriteAndReadMultiple) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -124,7 +122,7 @@
writer->WriteEntityData(DATA2, sizeof(DATA2));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -164,7 +162,7 @@
}
TEST_F(BackupDataTest, SkipEntity) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -174,7 +172,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -219,14 +217,14 @@
}
TEST_F(BackupDataTest, DeleteEntity) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -258,7 +256,7 @@
}
TEST_F(BackupDataTest, EneityAfterDelete) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -267,7 +265,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -319,7 +317,7 @@
}
TEST_F(BackupDataTest, OnlyDeleteEntities) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
@@ -327,7 +325,7 @@
writer->WriteEntityHeader(mKey4, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -387,13 +385,13 @@
}
TEST_F(BackupDataTest, ReadDeletedEntityData) {
- int fd = ::open(m_filename, O_WRONLY);
+ int fd = ::open(mFilename.string(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(m_filename, O_RDONLY);
+ fd = ::open(mFilename.string(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -431,6 +429,7 @@
EXPECT_EQ(-1, (int) dataSize)
<< "not recognizing deletion on second entity";
+ delete[] dataBytes;
delete writer;
delete reader;
}
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 7a4dd13..1151121 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -34,20 +34,18 @@
class ObbFileTest : public testing::Test {
protected:
sp<ObbFile> mObbFile;
- char* mExternalStorage;
- char* mFileName;
+ String8 mFileName;
virtual void SetUp() {
mObbFile = new ObbFile();
- mExternalStorage = getenv("EXTERNAL_STORAGE");
+ char* externalStorage = getenv("EXTERNAL_STORAGE");
- const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
- mFileName = new char[totalLen];
- snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
+ mFileName.append(externalStorage);
+ mFileName.append(TEST_FILENAME);
- int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ int fd = ::open(mFileName.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << mFileName << " for tests";
+ FAIL() << "Couldn't create " << mFileName.string() << " for tests";
}
}
@@ -71,12 +69,12 @@
EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
<< "Salt should be successfully set";
- EXPECT_TRUE(mObbFile->writeTo(mFileName))
+ EXPECT_TRUE(mObbFile->writeTo(mFileName.string()))
<< "couldn't write to fake .obb file";
mObbFile = new ObbFile();
- EXPECT_TRUE(mObbFile->readFrom(mFileName))
+ EXPECT_TRUE(mObbFile->readFrom(mFileName.string()))
<< "couldn't read from fake .obb file";
EXPECT_EQ(versionNum, mObbFile->getVersion())
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
new file mode 100644
index 0000000..4d07130
--- /dev/null
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/system/R.h"
+#include "data/app/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/system/system_arsc.h"
+#include "data/app/app_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+/**
+ * TODO(adamlesinski): Enable when fixed.
+ */
+TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len));
+
+ ResTable::Theme theme1(table);
+ ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
+ Res_value val;
+ ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+ ASSERT_EQ(uint32_t(0xffff0000), val.data);
+ ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(1), val.data);
+
+ ResTable table2;
+ ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len));
+ ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len));
+
+ ResTable::Theme theme2(table2);
+ ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
+ ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+ ASSERT_EQ(uint32_t(0xffff0000), val.data);
+ ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(1), val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/data/app/AndroidManifest.xml b/libs/androidfw/tests/data/app/AndroidManifest.xml
new file mode 100644
index 0000000..bfa3a39
--- /dev/null
+++ b/libs/androidfw/tests/data/app/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+</manifest>
diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h
new file mode 100644
index 0000000..780a116
--- /dev/null
+++ b/libs/androidfw/tests/data/app/R.h
@@ -0,0 +1,22 @@
+#ifndef __APP_R_H
+#define __APP_R_H
+
+namespace app {
+namespace R {
+
+namespace attr {
+ enum {
+ number = 0x7f010000, // default
+ };
+}
+
+namespace style {
+ enum {
+ Theme_One = 0x7f020000, // default
+ };
+}
+
+} // namespace R
+} // namespace app
+
+#endif // __APP_R_H
diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h
new file mode 100644
index 0000000..d5d9a3b
--- /dev/null
+++ b/libs/androidfw/tests/data/app/app_arsc.h
@@ -0,0 +1,62 @@
+unsigned char app_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00,
+ 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+ 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00,
+ 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00
+};
+unsigned int app_arsc_len = 708;
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
new file mode 100755
index 0000000..89c4641
--- /dev/null
+++ b/libs/androidfw/tests/data/app/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc app.arsc && \
+xxd -i app.arsc > app_arsc.h
diff --git a/libs/androidfw/tests/data/app/res/values/values.xml b/libs/androidfw/tests/data/app/res/values/values.xml
new file mode 100644
index 0000000..b0ead38
--- /dev/null
+++ b/libs/androidfw/tests/data/app/res/values/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="number" format="integer"/>
+ <style name="Theme.One" parent="@android:style/Theme.One">
+ <item name="number">1</item>
+ </style>
+</resources>
diff --git a/libs/androidfw/tests/data/system/AndroidManifest.xml b/libs/androidfw/tests/data/system/AndroidManifest.xml
new file mode 100644
index 0000000..af105ee
--- /dev/null
+++ b/libs/androidfw/tests/data/system/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+</manifest>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
new file mode 100644
index 0000000..7a9d3db
--- /dev/null
+++ b/libs/androidfw/tests/data/system/R.h
@@ -0,0 +1,23 @@
+#ifndef __ANDROID_R_H
+#define __ANDROID_R_H
+
+namespace android {
+namespace R {
+
+namespace attr {
+ enum {
+ background = 0x01010000, // default
+ foreground = 0x01010001, // default
+ };
+}
+
+namespace style {
+ enum {
+ Theme_One = 0x01020000, // default
+ };
+}
+
+} // namespace R
+} // namespace android
+
+#endif // __ANDROID_R_H
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
new file mode 100755
index 0000000..2a3ac0b
--- /dev/null
+++ b/libs/androidfw/tests/data/system/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc system.arsc && \
+xxd -i system.arsc > system_arsc.h
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
new file mode 100644
index 0000000..b29848e
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <public name="background" type="attr" id="0x01010000"/>
+ <public name="foreground" type="attr" id="0x01010001"/>
+ <public name="Theme.One" type="style" id="0x01020000"/>
+
+ <attr name="background" format="color|reference"/>
+ <attr name="foreground" format="color|reference"/>
+ <style name="Theme.One" parent="">
+ <item name="android:background">#ff0000</item>
+ <item name="android:foreground">#000000</item>
+ </style>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
new file mode 100644
index 0000000..215ecae
--- /dev/null
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -0,0 +1,69 @@
+unsigned char system_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+ 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
+ 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
+ 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
+ 0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+};
+unsigned int system_arsc_len = 792;
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 9cc83ed..7834ef8 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -16,6 +16,43 @@
#define LOG_TAG "OpenGLRenderer"
+/**
+ * Extra vertices for the corner for smoother corner.
+ * Only for outer vertices.
+ * Note that we use such extra memory to avoid an extra loop.
+ */
+// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
+// Set to 1 if we don't want to have any.
+#define EXTRA_CORNER_VERTEX_PER_PI 12
+
+// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
+// therefore, the maximum number of extra vertices will be twice bigger.
+#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI)
+
+// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
+#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI)
+
+/**
+ * Extra vertices for the Edge for interpolation artifacts.
+ * Same value for both inner and outer vertices.
+ */
+#define EXTRA_EDGE_VERTEX_PER_PI 50
+
+#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI)
+
+#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI)
+
+/**
+ * Other constants:
+ */
+// For the edge of the penumbra, the opacity is 0.
+#define OUTER_OPACITY (0.0f)
+
+// Once the alpha difference is greater than this threshold, we will allocate extra
+// edge vertices.
+// If this is set to negative value, then all the edge will be tessellated.
+#define ALPHA_THRESHOLD (0.1f / 255.0f)
+
#include <math.h>
#include <utils/Log.h>
#include <utils/Vector.h>
@@ -23,11 +60,97 @@
#include "AmbientShadow.h"
#include "ShadowTessellator.h"
#include "Vertex.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
/**
+ * Local utility functions.
+ */
+inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) {
+ // Convert from Vector3 to Vector2 first.
+ Vector2 currentVertex = { vertices[current].x, vertices[current].y };
+ Vector2 nextVertex = { vertices[next].x, vertices[next].y };
+
+ return ShadowTessellator::calculateNormal(currentVertex, nextVertex);
+}
+
+// The input z value will be converted to be non-negative inside.
+// The output must be ranged from 0 to 1.
+inline float getAlphaFromFactoredZ(float factoredZ) {
+ return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
+}
+
+inline float getTransformedAlphaFromAlpha(float alpha) {
+ return acosf(1.0f - 2.0f * alpha);
+}
+
+// The output is ranged from 0 to M_PI.
+inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
+ return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
+}
+
+inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
+ float divisor) {
+ // The formula is :
+ // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
+ // The value ranges for each step are:
+ // dot( ) --- [-1, 1]
+ // acos( ) --- [0, M_PI]
+ // floor(...) --- [0, EXTRA_VERTEX_PER_PI]
+ float dotProduct = vector1.dot(vector2);
+ // TODO: Use look up table for the dotProduct to extraVerticesNumber
+ // computation, if needed.
+ float angle = acosf(dotProduct);
+ return (int) floor(angle / divisor);
+}
+
+inline void checkOverflow(int used, int total, const char* bufferName) {
+ LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d",
+ bufferName, used, total);
+}
+
+inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
+ const Vector3& secondVertex, const Vector3& centroid) {
+ Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
+ secondSpike.normalize();
+
+ int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR);
+ *currentSpike = secondSpike;
+ return result;
+}
+
+// Given the caster's vertex count, compute all the buffers size depending on
+// whether or not the caster is opaque.
+inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount,
+ int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) {
+ // Compute the size of the vertex buffer.
+ int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER +
+ MAX_EXTRA_EDGE_VERTEX_NUMBER;
+ int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER;
+ *totalVertexCount = outerVertexCount + innerVertexCount;
+
+ // Compute the size of the index buffer.
+ *totalIndexCount = 2 * outerVertexCount + 2;
+
+ // Compute the size of the umber buffer.
+ // For translucent object, keep track of the umbra(inner) vertex in order to draw
+ // inside. We only need to store the index information.
+ *totalUmbraCount = 0;
+ if (!isCasterOpaque) {
+ // Add the centroid if occluder is translucent.
+ *totalVertexCount++;
+ *totalIndexCount += 2 * innerVertexCount + 1;
+ *totalUmbraCount = innerVertexCount;
+ }
+}
+
+inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
+ return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
+}
+
+/**
* Calculate the shadows as a triangle strips while alpha value as the
* shadow values.
*
@@ -43,290 +166,198 @@
*
* @param shadowVertexBuffer Return an floating point array of (x, y, a)
* triangle strips mode.
+ *
+ * An simple illustration:
+ * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C.
+ *
+ * First project the occluder to the Z=0 surface.
+ * Then we got all the inner vertices. And we compute the normal for each edge.
+ * According to the normal, we generate outer vertices. E.g: We generate P1 / P4
+ * as extra corner vertices to make the corner looks round and smoother.
+ *
+ * Due to the fact that the alpha is not linear interpolated along the inner
+ * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2,
+ * V0.1, V0.2 to avoid the visual artifacts.
+ *
+ * (P3)
+ * (P2) (P2.1) (P2.2) | ' (P4)
+ * (P1)' | | | | '
+ * ' | | | | '
+ * (P0) ------------------------------------------------(P5)
+ * | (V0) (V0.1) (V0.2) |(V1)
+ * | |
+ * | |
+ * | (C) |
+ * | |
+ * | |
+ * | |
+ * | |
+ * (V3)-----------------------------------(V2)
*/
void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
- const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
+ const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
- const int rays = SHADOW_RAY_COUNT;
- // Validate the inputs.
- if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
- || geomFactor <= 0) {
-#if DEBUG_SHADOW
- ALOGW("Invalid input for createAmbientShadow(), early return!");
-#endif
- return;
- }
+ shadowVertexBuffer.setMode(VertexBuffer::kIndices);
- Vector<Vector2> dir; // TODO: use C++11 unique_ptr
- dir.setCapacity(rays);
- float rayDist[rays];
- float rayHeight[rays];
- calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray());
+ // In order to computer the outer vertices in one loop, we need pre-compute
+ // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
+ // for vertex 0.
+ Vector2 previousNormal = getNormalFromVertices(casterVertices,
+ casterVertexCount - 1 , 0);
+ Vector2 currentSpike = {casterVertices[0].x - centroid3d.x,
+ casterVertices[0].y - centroid3d.y};
+ currentSpike.normalize();
+ float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor);
- // Calculate the length and height of the points along the edge.
- //
- // The math here is:
- // Intersect each ray (starting from the centroid) with the polygon.
- for (int i = 0; i < rays; i++) {
- int edgeIndex;
- float edgeFraction;
- float rayDistance;
- calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex,
- edgeFraction, rayDistance);
- rayDist[i] = rayDistance;
- if (edgeIndex < 0 || edgeIndex >= vertexCount) {
-#if DEBUG_SHADOW
- ALOGW("Invalid edgeIndex!");
-#endif
- edgeIndex = 0;
- }
- float h1 = vertices[edgeIndex].z;
- float h2 = vertices[((edgeIndex + 1) % vertexCount)].z;
- rayHeight[i] = h1 + edgeFraction * (h2 - h1);
- }
-
- // The output buffer length basically is roughly rays * layers, but since we
- // need triangle strips, so we need to duplicate vertices to accomplish that.
+ // Preparing all the output data.
+ int totalVertexCount, totalIndexCount, totalUmbraCount;
+ computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount,
+ casterVertexCount, isCasterOpaque);
AlphaVertex* shadowVertices =
- shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+ shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount);
+ int vertexBufferIndex = 0;
+ uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount);
+ int indexBufferIndex = 0;
+ uint16_t umbraVertices[totalUmbraCount];
+ int umbraIndex = 0;
- // Calculate the vertex of the shadows.
- //
- // The math here is:
- // Along the edges of the polygon, for each intersection point P (generated above),
- // calculate the normal N, which should be perpendicular to the edge of the
- // polygon (represented by the neighbor intersection points) .
- // Shadow's vertices will be generated as : P + N * scale.
- const Vector2 centroid2d = {centroid3d.x, centroid3d.y};
- for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
- Vector2 normal = {1.0f, 0.0f};
- calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
+ for (int i = 0; i < casterVertexCount; i++) {
+ // Corner: first figure out the extra vertices we need for the corner.
+ const Vector3& innerVertex = casterVertices[i];
+ Vector2 currentNormal = getNormalFromVertices(casterVertices, i,
+ (i + 1) % casterVertexCount);
- // The vertex should be start from rayDist[i] then scale the
- // normalizeNormal!
- Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
- centroid2d;
+ int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal,
+ CORNER_RADIANS_DIVISOR);
- // outer ring of points, expanded based upon height of each ray intersection
- float expansionDist = rayHeight[rayIndex] * heightFactor *
- geomFactor;
- AlphaVertex::set(&shadowVertices[rayIndex],
- intersection.x + normal.x * expansionDist,
- intersection.y + normal.y * expansionDist,
- 0.0f);
-
- // inner ring of points
- float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
- // NOTE: Shadow alpha values are transformed when stored in alphavertices,
- // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
- float transformedOpacity = acos(1.0f - 2.0f * opacity);
- AlphaVertex::set(&shadowVertices[rays + rayIndex],
- intersection.x,
- intersection.y,
- transformedOpacity);
- }
-
- if (isCasterOpaque) {
- // skip inner ring, calc bounds over filled portion of buffer
- shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
- shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
- } else {
- // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
- // centroid in the innermost ring of vertices.
- float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
- AlphaVertex centroidXYA;
- AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
- for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
- shadowVertices[2 * rays + rayIndex] = centroidXYA;
- }
- // calc bounds over entire buffer
- shadowVertexBuffer.computeBounds<AlphaVertex>();
- shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
- }
-
+ float expansionDist = innerVertex.z * heightFactor * geomFactor;
+ const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
#if DEBUG_SHADOW
- for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
- ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
- shadowVertices[i].y, shadowVertices[i].alpha);
- }
+ ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
#endif
-}
-/**
- * Generate an array of rays' direction vectors.
- * To make sure the vertices generated are clockwise, the directions are from PI
- * to -PI.
- *
- * @param rays The number of rays shooting out from the centroid.
- * @param vertices Vertices of the polygon.
- * @param vertexCount The number of vertices.
- * @param centroid3d The centroid of the polygon.
- * @param dir Return the array of ray vectors.
- */
-void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices,
- const int vertexCount, const Vector3& centroid3d, Vector2* dir) {
- // If we don't have enough rays, then fall back to the uniform distribution.
- if (vertexCount * 2 > rays) {
- float deltaAngle = 2 * M_PI / rays;
- for (int i = 0; i < rays; i++) {
- dir[i].x = cosf(M_PI - deltaAngle * i);
- dir[i].y = sinf(M_PI - deltaAngle * i);
+ // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB).
+ // We fill the inner vertex first, such that we can fill the index buffer
+ // inside the loop.
+ int currentInnerVertexIndex = vertexBufferIndex;
+ if (!isCasterOpaque) {
+ umbraVertices[umbraIndex++] = vertexBufferIndex;
}
- return;
- }
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
+ casterVertices[i].y,
+ getTransformedAlphaFromAlpha(currentAlpha));
- // If we have enough rays, then we assign each vertices a ray, and distribute
- // the rest uniformly.
- float rayThetas[rays];
+ const Vector3& innerStart = casterVertices[i];
- const int uniformRayCount = rays - vertexCount;
- const float deltaAngle = 2 * M_PI / uniformRayCount;
+ // outerStart is the first outer vertex for this inner vertex.
+ // outerLast is the last outer vertex for this inner vertex.
+ Vector2 outerStart = {0, 0};
+ Vector2 outerLast = {0, 0};
+ // This will create vertices from [0, cornerSlicesNumber] inclusively,
+ // which means minimally 2 vertices even without the extra ones.
+ for (int j = 0; j <= cornerSlicesNumber; j++) {
+ Vector2 averageNormal =
+ previousNormal * (cornerSlicesNumber - j) + currentNormal * j;
+ averageNormal /= cornerSlicesNumber;
+ averageNormal.normalize();
+ Vector2 outerVertex;
+ outerVertex.x = innerVertex.x + averageNormal.x * expansionDist;
+ outerVertex.y = innerVertex.y + averageNormal.y * expansionDist;
- // We have to generate all the vertices' theta anyway and we also need to
- // find the minimal, so let's precompute it first.
- // Since the incoming polygon is clockwise, we can find the dip to identify
- // the minimal theta.
- float polyThetas[vertexCount];
- int maxPolyThetaIndex = 0;
- for (int i = 0; i < vertexCount; i++) {
- polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
- vertices[i].x - centroid3d.x);
- if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
- maxPolyThetaIndex = i;
- }
- }
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
+ outerVertex.y, OUTER_OPACITY);
- // Both poly's thetas and uniform thetas are in decrease order(clockwise)
- // from PI to -PI.
- int polyThetaIndex = maxPolyThetaIndex;
- float polyTheta = polyThetas[maxPolyThetaIndex];
- int uniformThetaIndex = 0;
- float uniformTheta = M_PI;
- for (int i = 0; i < rays; i++) {
- // Compare both thetas and pick the smaller one and move on.
- bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
- if (polyTheta > uniformTheta || hasThetaCollision) {
- if (hasThetaCollision) {
- // Shift the uniformTheta to middle way between current polyTheta
- // and next uniform theta. The next uniform theta can wrap around
- // to exactly PI safely here.
- // Note that neither polyTheta nor uniformTheta can be FLT_MAX
- // due to the hasThetaCollision is true.
- uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
-#if DEBUG_SHADOW
- ALOGD("Shifted uniformTheta to %f", uniformTheta);
-#endif
- }
- rayThetas[i] = polyTheta;
- polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
- if (polyThetaIndex != maxPolyThetaIndex) {
- polyTheta = polyThetas[polyThetaIndex];
- } else {
- // out of poly points.
- polyTheta = - FLT_MAX;
- }
- } else {
- rayThetas[i] = uniformTheta;
- uniformThetaIndex++;
- if (uniformThetaIndex < uniformRayCount) {
- uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
- } else {
- // out of uniform points.
- uniformTheta = - FLT_MAX;
+ if (j == 0) {
+ outerStart = outerVertex;
+ } else if (j == cornerSlicesNumber) {
+ outerLast = outerVertex;
}
}
- }
+ previousNormal = currentNormal;
- for (int i = 0; i < rays; i++) {
+ // Edge: first figure out the extra vertices needed for the edge.
+ const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount];
+ float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor);
+ if (needsExtraForEdge(currentAlpha, nextAlpha)) {
+ // TODO: See if we can / should cache this outer vertex across the loop.
+ Vector2 outerNext;
+ float expansionDist = innerNext.z * heightFactor * geomFactor;
+ outerNext.x = innerNext.x + currentNormal.x * expansionDist;
+ outerNext.y = innerNext.y + currentNormal.y * expansionDist;
+
+ // Compute the angle and see how many extra points we need.
+ int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike,
+ innerNext, centroid3d);
#if DEBUG_SHADOW
- ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI);
+ ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
#endif
- // TODO: Fix the intersection precision problem and remvoe the delta added
- // here.
- dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
- dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
- }
-}
+ // Edge: fill the edge's VB and IB.
+ // This will create vertices pair from [1, extraVerticesNumber - 1].
+ // If there is no extra vertices created here, the edge will be drawn
+ // as just 2 triangles.
+ for (int k = 1; k < extraVerticesNumber; k++) {
+ int startWeight = extraVerticesNumber - k;
+ Vector2 currentOuter =
+ (outerLast * startWeight + outerNext * k) / extraVerticesNumber;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
+ currentOuter.y, OUTER_OPACITY);
-/**
- * Calculate the intersection of a ray hitting the polygon.
- *
- * @param vertices The shadow caster's polygon, which is represented in a
- * Vector3 array.
- * @param vertexCount The length of caster's polygon in terms of number of vertices.
- * @param start The starting point of the ray.
- * @param dir The direction vector of the ray.
- *
- * @param outEdgeIndex Return the index of the segment (or index of the starting
- * vertex) that ray intersect with.
- * @param outEdgeFraction Return the fraction offset from the segment starting
- * index.
- * @param outRayDist Return the ray distance from centroid to the intersection.
- */
-void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount,
- const Vector3& start, const Vector2& dir, int& outEdgeIndex,
- float& outEdgeFraction, float& outRayDist) {
- float startX = start.x;
- float startY = start.y;
- float dirX = dir.x;
- float dirY = dir.y;
- // Start the search from the last edge from poly[len-1] to poly[0].
- int p1 = vertexCount - 1;
-
- for (int p2 = 0; p2 < vertexCount; p2++) {
- float p1x = vertices[p1].x;
- float p1y = vertices[p1].y;
- float p2x = vertices[p2].x;
- float p2y = vertices[p2].y;
-
- // The math here is derived from:
- // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0;
- // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0;
- float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x);
- if (div != 0) {
- float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div);
- if (t > 0 && t <= 1) {
- float t2 = (p1x * (startY - p2y)
- + p2x * (p1y - startY)
- + startX * (p2y - p1y)) / div;
- if (t2 > 0) {
- outEdgeIndex = p1;
- outRayDist = t2;
- outEdgeFraction = t;
- return;
+ if (!isCasterOpaque) {
+ umbraVertices[umbraIndex++] = vertexBufferIndex;
}
+ Vector3 currentInner =
+ (innerStart * startWeight + innerNext * k) / extraVerticesNumber;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
+ currentInner.y,
+ getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
}
}
- p1 = p2;
+ currentAlpha = nextAlpha;
}
- return;
-};
-/**
- * Calculate the normal at the intersection point between a ray and the polygon.
- *
- * @param rays The total number of rays.
- * @param currentRayIndex The index of the ray which the normal is based on.
- * @param dir The array of the all the rays directions.
- * @param rayDist The pre-computed ray distances array.
- *
- * @param normal Return the normal.
- */
-void AmbientShadow::calculateNormal(int rays, int currentRayIndex,
- const Vector2* dir, const float* rayDist, Vector2& normal) {
- int preIndex = (currentRayIndex - 1 + rays) % rays;
- int postIndex = (currentRayIndex + 1) % rays;
- Vector2 p1 = dir[preIndex] * rayDist[preIndex];
- Vector2 p2 = dir[postIndex] * rayDist[postIndex];
+ indexBuffer[indexBufferIndex++] = 1;
+ indexBuffer[indexBufferIndex++] = 0;
- // Now the rays are going CW around the poly.
- Vector2 delta = p2 - p1;
- if (delta.length() != 0) {
- delta.normalize();
- // Calculate the normal , which is CCW 90 rotate to the delta.
- normal.x = - delta.y;
- normal.y = delta.x;
+ if (!isCasterOpaque) {
+ // Add the centroid as the last one in the vertex buffer.
+ float centroidOpacity =
+ getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
+ int centroidIndex = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
+ centroid3d.y, centroidOpacity);
+
+ for (int i = 0; i < umbraIndex; i++) {
+ // Note that umbraVertices[0] is always 0.
+ // So the start and the end of the umbra are using the "0".
+ // And penumbra ended with 0, so a degenerated triangle is formed b/t
+ // the umbra and penumbra.
+ indexBuffer[indexBufferIndex++] = umbraVertices[i];
+ indexBuffer[indexBufferIndex++] = centroidIndex;
+ }
+ indexBuffer[indexBufferIndex++] = 0;
}
+
+ // At the end, update the real index and vertex buffer size.
+ shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
+ shadowVertexBuffer.updateIndexCount(indexBufferIndex);
+
+ checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
+ checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
+ checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
+
+#if DEBUG_SHADOW
+ for (int i = 0; i < vertexBufferIndex; i++) {
+ ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
+ shadowVertices[i].alpha);
+ }
+ for (int i = 0; i < indexBufferIndex; i++) {
+ ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
+ }
+#endif
}
}; // namespace uirenderer
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 68df246..9660dc0 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -28,27 +28,12 @@
/**
* AmbientShadow is used to calculate the ambient shadow value around a polygon.
- *
- * TODO: calculateIntersection() now is O(N*M), where N is the number of
- * polygon's vertics and M is the number of rays. In fact, by staring tracing
- * the vertex from the previous intersection, the algorithm can be O(N + M);
*/
class AmbientShadow {
public:
static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& centroid3d, float heightFactor,
float geomFactor, VertexBuffer& shadowVertexBuffer);
-
-private:
- static void calculateRayDirections(const int rays, const Vector3* vertices,
- const int vertexCount, const Vector3& centroid3d, Vector2* dir);
-
- static void calculateIntersection(const Vector3* poly, int nbVertices,
- const Vector3& start, const Vector2& dir, int& outEdgeIndex,
- float& outEdgeFraction, float& outRayDist);
-
- static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir,
- const float* rayDist, Vector2& normal);
}; // AmbientShadow
}; // namespace uirenderer
diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp
index ec44de3..d7d9743 100644
--- a/libs/hwui/AnimationContext.cpp
+++ b/libs/hwui/AnimationContext.cpp
@@ -31,6 +31,14 @@
}
AnimationContext::~AnimationContext() {
+ startFrame();
+ while (mCurrentFrameAnimations.mNextHandle) {
+ AnimationHandle* current = mCurrentFrameAnimations.mNextHandle;
+ AnimatorManager& animators = current->mRenderNode->animators();
+ animators.endAllAnimators();
+ LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle == current,
+ "endAllAnimators failed to remove from current frame list!");
+ }
}
void AnimationContext::addAnimatingRenderNode(RenderNode& node) {
@@ -96,11 +104,18 @@
if (mRenderNode->animators().hasAnimators()) {
mContext.addAnimationHandle(this);
} else {
- mRenderNode->animators().setAnimationHandle(NULL);
- delete this;
+ release();
}
}
+void AnimationHandle::release() {
+ LOG_ALWAYS_FATAL_IF(mRenderNode->animators().hasAnimators(),
+ "Releasing the handle for an RenderNode with outstanding animators!");
+ removeFromList();
+ mRenderNode->animators().setAnimationHandle(NULL);
+ delete this;
+}
+
void AnimationHandle::insertAfter(AnimationHandle* prev) {
removeFromList();
mNextHandle = prev->mNextHandle;
diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h
index e32c33d..900d953 100644
--- a/libs/hwui/AnimationContext.h
+++ b/libs/hwui/AnimationContext.h
@@ -46,8 +46,15 @@
public:
AnimationContext& context() { return mContext; }
+ // Called by the RenderNode when it has internally pulsed its own animations
+ // this frame and does not need to be run again this frame.
void notifyAnimationsRan();
+ // Stops tracking the RenderNode and destroys the handle. The node must be
+ // re-attached to the AnimationContext to receive managed animation
+ // pulses.
+ void release();
+
private:
friend class AnimationContext;
AnimationHandle(AnimationContext& context);
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 1c697d5..da65f38 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -95,6 +95,8 @@
// Oh boy, we're starting! Man the battle stations!
if (mPlayState == RUNNING) {
transitionToRunning(context);
+ } else if (mPlayState == FINISHED) {
+ callOnFinishedListener(context);
}
}
}
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 3832d42..678b1ee 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -160,13 +160,15 @@
if (mAnimationHandle) {
EndAnimatorsFunctor functor(mAnimationHandle->context());
for_each(mAnimators.begin(), mAnimators.end(), functor);
+ mAnimators.clear();
+ mAnimationHandle->release();
} else {
// We have no context, so bust out the sledgehammer
// This works because this state can only happen on the UI thread,
// which means we're already on the right thread to invoke listeners
for_each(mAnimators.begin(), mAnimators.end(), endAnimatorsHard);
+ mAnimators.clear();
}
- mAnimators.clear();
}
} /* namespace uirenderer */
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8818510..5ff7b7f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1528,8 +1528,7 @@
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
TessellationCache::vertexBuffer_pair_t buffers;
- Matrix4 drawTransform;
- renderer.getMatrix(&drawTransform);
+ Matrix4 drawTransform(*(renderer.currentTransform()));
renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
&mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bbf0551..0f36c06 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2417,6 +2417,10 @@
} else if (mode == VertexBuffer::kTwoPolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+ } else if (mode == VertexBuffer::kIndices) {
+ mCaches.unbindIndicesBuffer();
+ glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT,
+ vertexBuffer.getIndices());
}
if (isAA) {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a79875e..6a92a6e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -34,6 +34,7 @@
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
+#include "renderthread/CanvasContext.h"
namespace android {
namespace uirenderer {
@@ -208,6 +209,13 @@
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);
}
+
+ if (CC_UNLIKELY(info.canvasContext)) {
+ // If canvasContext is not null that means there are prefetched layers
+ // that need to be accounted for. That might be us, so tell CanvasContext
+ // that this layer is in the tree and should not be destroyed.
+ info.canvasContext->markLayerInUse(this);
+ }
}
void RenderNode::prepareTreeImpl(TreeInfo& info) {
@@ -653,41 +661,12 @@
handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
-template <class T>
-int RenderNode::issueOperationsOfNegZChildren(
- const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- OpenGLRenderer& renderer, T& handler) {
- if (zTranslatedNodes.isEmpty()) return -1;
-
- // create a save around the body of the ViewGroup's draw method, so that
- // matrix/clip methods don't affect composited children
- int shadowSaveCount = renderer.getSaveCount();
- handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
- issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
- return shadowSaveCount;
-}
-
-template <class T>
-void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo,
- const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- OpenGLRenderer& renderer, T& handler) {
- if (zTranslatedNodes.isEmpty()) return;
-
- LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to");
- handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
- renderer.setOverrideLayerAlpha(1.0f);
-
- issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
-}
-
#define SHADOW_DELTA 0.1f
template <class T>
-void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
+ const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler) {
const int size = zTranslatedNodes.size();
if (size == 0
|| (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
@@ -696,6 +675,11 @@
return;
}
+ // Apply the base transform of the parent of the 3d children. This isolates
+ // 3d children of the current chunk from transformations made in previous chunks.
+ int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ renderer.setMatrix(initialTransform);
+
/**
* Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
* with very similar Z heights to draw together.
@@ -750,6 +734,7 @@
renderer.restoreToCount(restoreTo);
drawIndex++;
}
+ renderer.restoreToCount(rootRestoreTo);
}
template <class T>
@@ -869,6 +854,8 @@
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
+ Matrix4 initialTransform(*(renderer.currentTransform()));
+
if (drawLayer) {
handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
renderer.getSaveCount() - 1, properties().getClipToBounds());
@@ -880,9 +867,9 @@
Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
buildZSortedChildList(chunk, zTranslatedNodes);
- // for 3d root, draw children with negative z values
- int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes,
- renderer, handler);
+ issueOperationsOf3dChildren(kNegativeZChildren,
+ initialTransform, zTranslatedNodes, renderer, handler);
+
const int saveCountOffset = renderer.getSaveCount() - 1;
const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
@@ -899,8 +886,8 @@
}
}
- // for 3d root, draw children with positive z values
- issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
+ issueOperationsOf3dChildren(kPositiveZChildren,
+ initialTransform, zTranslatedNodes, renderer, handler);
}
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 27b05e2..d897997 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -208,16 +208,9 @@
inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
template <class T>
- inline int issueOperationsOfNegZChildren(
- const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ inline void issueOperationsOf3dChildren(ChildrenSelectMode mode,
+ const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
OpenGLRenderer& renderer, T& handler);
- template <class T>
- inline void issueOperationsOfPosZChildren(int shadowRestoreTo,
- const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- OpenGLRenderer& renderer, T& handler);
- template <class T>
- inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
template <class T>
inline void issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler);
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index bdac47b..3e1aed3 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -102,10 +102,6 @@
// Matrix
///////////////////////////////////////////////////////////////////////////////
-void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const {
- matrix->load(*(mSnapshot->transform));
-}
-
void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
mSnapshot->transform->copyTo(*matrix);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 3957d36..c6974b4 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -69,7 +69,6 @@
// int alpha, SkXfermode::Mode mode, int flags);
// Matrix
- void getMatrix(Matrix4* outMatrix) const;
virtual void getMatrix(SkMatrix* outMatrix) const;
virtual void translate(float dx, float dy, float dz = 0.0f);
virtual void rotate(float degrees);
@@ -100,6 +99,10 @@
void setClippingRoundRect(LinearAllocator& allocator,
const Rect& rect, float radius);
+ inline const mat4* currentTransform() const {
+ return mSnapshot->transform;
+ }
+
protected:
const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
@@ -134,10 +137,6 @@
return mSnapshot->clipRect;
}
- inline const mat4* currentTransform() const {
- return mSnapshot->transform;
- }
-
inline const Snapshot* currentSnapshot() const {
return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get();
}
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index e78d8bd..ae6ea94 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -26,6 +26,10 @@
namespace android {
namespace uirenderer {
+namespace renderthread {
+class CanvasContext;
+}
+
class OpenGLRenderer;
class RenderState;
@@ -59,6 +63,7 @@
, renderState(renderState)
, renderer(NULL)
, errorHandler(NULL)
+ , canvasContext(NULL)
{}
explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
@@ -69,6 +74,7 @@
, renderState(clone.renderState)
, renderer(clone.renderer)
, errorHandler(clone.errorHandler)
+ , canvasContext(clone.canvasContext)
{}
const TraversalMode mode;
@@ -89,6 +95,8 @@
// layer updates or similar. May be NULL.
OpenGLRenderer* renderer;
ErrorHandler* errorHandler;
+ // TODO: Remove this? May be NULL
+ renderthread::CanvasContext* canvasContext;
struct Out {
Out()
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 2a9f01c..d033ed9 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -111,6 +111,23 @@
float y;
float z;
+ Vector3 operator+(const Vector3& v) const {
+ return (Vector3){x + v.x, y + v.y, z + v.z};
+ }
+
+ Vector3 operator-(const Vector3& v) const {
+ return (Vector3){x - v.x, y - v.y, z - v.z};
+ }
+
+ Vector3 operator/(float s) const {
+ return (Vector3){x / s, y / s, z / s};
+ }
+
+ Vector3 operator*(float s) const {
+ return (Vector3){x * s, y * s, z * s};
+ }
+
+
void dump() {
ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
}
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 3837f88..966fa4e 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
#define ANDROID_HWUI_VERTEX_BUFFER_H
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -26,19 +27,27 @@
enum Mode {
kStandard = 0,
kOnePolyRingShadow = 1,
- kTwoPolyRingShadow = 2
+ kTwoPolyRingShadow = 2,
+ kIndices = 3
};
VertexBuffer()
: mBuffer(0)
+ , mIndices(0)
, mVertexCount(0)
+ , mIndexCount(0)
+ , mAllocatedVertexCount(0)
+ , mAllocatedIndexCount(0)
, mByteCount(0)
, mMode(kStandard)
+ , mReallocBuffer(0)
, mCleanupMethod(NULL)
+ , mCleanupIndexMethod(NULL)
{}
~VertexBuffer() {
if (mCleanupMethod) mCleanupMethod(mBuffer);
+ if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices);
}
/**
@@ -59,6 +68,7 @@
mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
+ mAllocatedVertexCount = vertexCount;
mVertexCount = vertexCount;
mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
@@ -69,6 +79,17 @@
}
template <class TYPE>
+ TYPE* allocIndices(int indexCount) {
+ mAllocatedIndexCount = indexCount;
+ mIndexCount = indexCount;
+ mIndices = (void*)new TYPE[indexCount];
+
+ mCleanupIndexMethod = &(cleanup<TYPE>);
+
+ return (TYPE*)mIndices;
+ }
+
+ template <class TYPE>
void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
int verticesToCopy = srcBuffer.getVertexCount();
@@ -103,9 +124,17 @@
}
const void* getBuffer() const { return mBuffer; }
+ const void* getIndices() const { return mIndices; }
const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
unsigned int getSize() const { return mByteCount; }
+ unsigned int getIndexCount() const { return mIndexCount; }
+ void updateIndexCount(unsigned int newCount) {
+ mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
+ }
+ void updateVertexCount(unsigned int newCount) {
+ newCount = MathUtils::min(newCount, mAllocatedVertexCount);
+ }
Mode getMode() const { return mMode; }
void setBounds(Rect bounds) { mBounds = bounds; }
@@ -127,14 +156,22 @@
}
Rect mBounds;
+
void* mBuffer;
+ void* mIndices;
+
unsigned int mVertexCount;
+ unsigned int mIndexCount;
+ unsigned int mAllocatedVertexCount;
+ unsigned int mAllocatedIndexCount;
unsigned int mByteCount;
+
Mode mMode;
void* mReallocBuffer; // used for multi-allocation
void (*mCleanupMethod)(void*);
+ void (*mCleanupIndexMethod)(void*);
};
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d9fa0bc..491a295 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,6 +16,7 @@
#include "CanvasContext.h"
+#include <algorithm>
#include <private/hwui/DrawGlInfo.h>
#include <strings.h>
@@ -53,6 +54,7 @@
destroyCanvasAndSurface();
mRenderThread.removeFrameCallback(this);
delete mAnimationContext;
+ freePrefetechedLayers();
}
void CanvasContext::destroyCanvasAndSurface() {
@@ -142,10 +144,17 @@
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
+ if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
+ info.canvasContext = this;
+ }
mAnimationContext->startFrame();
mRootRenderNode->prepareTree(info);
mAnimationContext->runRemainingAnimations(info);
+ if (info.canvasContext) {
+ freePrefetechedLayers();
+ }
+
int runningBehind = 0;
// TODO: This query is moderately expensive, investigate adding some sort
// of fast-path based off when we last called eglSwapBuffers() as well as
@@ -249,6 +258,26 @@
thread.renderState().invokeFunctor(functor, mode, NULL);
}
+void CanvasContext::markLayerInUse(RenderNode* node) {
+ if (mPrefetechedLayers.erase(node)) {
+ node->decStrong(0);
+ }
+}
+
+static void destroyPrefetechedNode(RenderNode* node) {
+ ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
+ node->destroyHardwareResources();
+ node->decStrong(0);
+}
+
+void CanvasContext::freePrefetechedLayers() {
+ if (mPrefetechedLayers.size()) {
+ requireGlContext();
+ std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
+ mPrefetechedLayers.clear();
+ }
+}
+
void CanvasContext::buildLayer(RenderNode* node) {
ATRACE_CALL();
if (!mEglManager.hasEglContext() || !mCanvas) {
@@ -270,6 +299,9 @@
node->setPropertyFieldsDirty(RenderNode::GENERIC);
mCanvas->flushLayerUpdates();
+
+ node->incStrong(0);
+ mPrefetechedLayers.insert(node);
}
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
@@ -282,6 +314,7 @@
stopDrawing();
if (mEglManager.hasEglContext()) {
requireGlContext();
+ freePrefetechedLayers();
mRootRenderNode->destroyHardwareResources();
Caches::getInstance().flush(Caches::kFlushMode_Layers);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 749da1b..7c27190 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -17,6 +17,8 @@
#ifndef CANVASCONTEXT_H_
#define CANVASCONTEXT_H_
+#include <set>
+
#include <cutils/compiler.h>
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -71,6 +73,7 @@
void buildLayer(RenderNode* node);
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+ void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
static void trimMemory(RenderThread& thread, int level);
@@ -99,6 +102,8 @@
void requireGlContext();
+ void freePrefetechedLayers();
+
RenderThread& mRenderThread;
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
@@ -114,6 +119,8 @@
const sp<RenderNode> mRootRenderNode;
DrawProfiler mProfiler;
+
+ std::set<RenderNode*> mPrefetechedLayers;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 37f8e60..e030cdb 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EglContext"
-
#include "EglManager.h"
#include <cutils/log.h>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e69c456..e2770b4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1688,11 +1688,19 @@
/**
* Return a new audio session identifier not associated with any player or effect.
- * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
- * objects or specify a session for speech synthesis in
- * {@link android.speech.tts.TextToSpeech.Engine}.
+ * An audio session identifier is a system wide unique identifier for a set of audio streams
+ * (one or more mixed together).
+ * <p>The primary use of the audio session ID is to associate audio effects to audio players,
+ * such as {@link MediaPlayer} or {@link AudioTrack}: all audio effects sharing the same audio
+ * session ID will be applied to the mixed audio content of the players that share the same
+ * audio session.
+ * <p>This method can for instance be used when creating one of the
+ * {@link android.media.audiofx.AudioEffect} objects to define the audio session of the effect,
+ * or to specify a session for a speech synthesis utterance
+ * in {@link android.speech.tts.TextToSpeech.Engine}.
* @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the
- * system failed to generate a new session.
+ * system failed to generate a new session, a condition in which audio playback or recording
+ * will subsequently fail as well.
*/
public int generateAudioSessionId() {
int session = AudioSystem.newAudioSessionId();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index bce4074..d002924 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -200,6 +200,7 @@
private static final int MSG_UNLOAD_SOUND_EFFECTS = 20;
private static final int MSG_SYSTEM_READY = 21;
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 22;
+ private static final int MSG_PERSIST_MICROPHONE_MUTE = 23;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -873,6 +874,10 @@
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
+ boolean microphoneMute =
+ System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1;
+ AudioSystem.muteMicrophone(microphoneMute);
+
// Each stream will read its own persisted settings
// Broadcast the sticky intent
@@ -1447,17 +1452,15 @@
if (mUseFixedVolume) {
return;
}
-
if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
-
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
// Post a persist master volume msg
sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
- : 0, 0, null, PERSIST_DELAY);
+ : 0, UserHandle.getCallingUserId(), null, PERSIST_DELAY);
sendMasterMuteUpdate(state, flags);
}
}
@@ -1563,6 +1566,9 @@
}
AudioSystem.muteMicrophone(on);
+ // Post a persist microphone msg.
+ sendMsg(mAudioHandler, MSG_PERSIST_MICROPHONE_MUTE, SENDMSG_REPLACE, on ? 1
+ : 0, UserHandle.getCallingUserId(), null, PERSIST_DELAY);
}
/** @see AudioManager#getRingerMode() */
@@ -3486,13 +3492,28 @@
private void dump(PrintWriter pw) {
pw.print(" Mute count: ");
pw.println(muteCount());
+ pw.print(" Max: ");
+ pw.println((mIndexMax + 5) / 10);
pw.print(" Current: ");
Set set = mIndex.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry entry = (Map.Entry)i.next();
- pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
- + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
+ final int device = (Integer) entry.getKey();
+ pw.print(Integer.toHexString(device));
+ final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
+ : AudioSystem.getOutputDeviceName(device);
+ if (!deviceName.isEmpty()) {
+ pw.print(" (");
+ pw.print(deviceName);
+ pw.print(")");
+ }
+ pw.print(": ");
+ final int index = (((Integer) entry.getValue()) + 5) / 10;
+ pw.print(index);
+ if (i.hasNext()) {
+ pw.print(", ");
+ }
}
}
}
@@ -3819,7 +3840,6 @@
@Override
public void handleMessage(Message msg) {
-
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
@@ -3851,7 +3871,7 @@
Settings.System.putIntForUser(mContentResolver,
Settings.System.VOLUME_MASTER_MUTE,
msg.arg1,
- UserHandle.USER_CURRENT);
+ msg.arg2);
break;
case MSG_PERSIST_RINGER_MODE:
@@ -4046,6 +4066,12 @@
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
UserHandle.USER_CURRENT);
break;
+ case MSG_PERSIST_MICROPHONE_MUTE:
+ Settings.System.putIntForUser(mContentResolver,
+ Settings.System.MICROPHONE_MUTE,
+ msg.arg1,
+ msg.arg2);
+ break;
}
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e11aab1..9a76f94 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -371,6 +371,7 @@
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 final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
public static String getOutputDeviceName(int device)
{
@@ -417,6 +418,8 @@
return DEVICE_OUT_SPDIF_NAME;
case DEVICE_OUT_FM:
return DEVICE_OUT_FM_NAME;
+ case DEVICE_OUT_AUX_LINE:
+ return DEVICE_OUT_AUX_LINE_NAME;
case DEVICE_OUT_DEFAULT:
default:
return "";
diff --git a/media/java/android/media/MediaDescription.aidl b/media/java/android/media/MediaDescription.aidl
new file mode 100644
index 0000000..6f934f7
--- /dev/null
+++ b/media/java/android/media/MediaDescription.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable MediaDescription;
diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
new file mode 100644
index 0000000..4399c0d
--- /dev/null
+++ b/media/java/android/media/MediaDescription.java
@@ -0,0 +1,276 @@
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Size;
+
+/**
+ * A simple set of metadata for a media item suitable for display. This can be
+ * created using the Builder or retrieved from existing metadata using
+ * {@link MediaMetadata#getDescription()}.
+ */
+public class MediaDescription implements Parcelable {
+ /**
+ * A unique persistent id for the content or null.
+ */
+ private final String mMediaId;
+ /**
+ * A primary title suitable for display or null.
+ */
+ private final CharSequence mTitle;
+ /**
+ * A subtitle suitable for display or null.
+ */
+ private final CharSequence mSubtitle;
+ /**
+ * A description suitable for display or null.
+ */
+ private final CharSequence mDescription;
+ /**
+ * A bitmap icon suitable for display or null.
+ */
+ private final Bitmap mIcon;
+ /**
+ * A Uri for an icon suitable for display or null.
+ */
+ private final Uri mIconUri;
+ /**
+ * Extras for opaque use by apps/system.
+ */
+ private final Bundle mExtras;
+
+ private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
+ CharSequence description, Bitmap icon, Uri iconUri, Bundle extras) {
+ mMediaId = mediaId;
+ mTitle = title;
+ mSubtitle = subtitle;
+ mDescription = description;
+ mIcon = icon;
+ mIconUri = iconUri;
+ mExtras = extras;
+ }
+
+ private MediaDescription(Parcel in) {
+ mMediaId = in.readString();
+ mTitle = in.readCharSequence();
+ mSubtitle = in.readCharSequence();
+ mDescription = in.readCharSequence();
+ mIcon = in.readParcelable(null);
+ mIconUri = in.readParcelable(null);
+ mExtras = in.readBundle();
+ }
+
+ /**
+ * Returns the media id or null. See
+ * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}.
+ */
+ public @Nullable String getMediaId() {
+ return mMediaId;
+ }
+
+ /**
+ * Returns a title suitable for display or null.
+ *
+ * @return A title or null.
+ */
+ public @Nullable CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Returns a subtitle suitable for display or null.
+ *
+ * @return A subtitle or null.
+ */
+ public @Nullable CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ /**
+ * Returns a description suitable for display or null.
+ *
+ * @return A description or null.
+ */
+ public @Nullable CharSequence getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Returns a bitmap icon suitable for display or null.
+ *
+ * @return An icon or null.
+ */
+ public @Nullable Bitmap getIconBitmap() {
+ return mIcon;
+ }
+
+ /**
+ * Returns a Uri for an icon suitable for display or null.
+ *
+ * @return An icon uri or null.
+ */
+ public @Nullable Uri getIconUri() {
+ return mIconUri;
+ }
+
+ /**
+ * Returns any extras that were added to the description.
+ *
+ * @return A bundle of extras or null.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mMediaId);
+ dest.writeCharSequence(mTitle);
+ dest.writeCharSequence(mSubtitle);
+ dest.writeCharSequence(mDescription);
+ dest.writeParcelable(mIcon, flags);
+ dest.writeParcelable(mIconUri, flags);
+ dest.writeBundle(mExtras);
+ }
+
+ @Override
+ public String toString() {
+ return mTitle + ", " + mSubtitle + ", " + mDescription;
+ }
+
+ public static final Parcelable.Creator<MediaDescription> CREATOR =
+ new Parcelable.Creator<MediaDescription>() {
+ @Override
+ public MediaDescription createFromParcel(Parcel in) {
+ return new MediaDescription(in);
+ }
+
+ @Override
+ public MediaDescription[] newArray(int size) {
+ return new MediaDescription[size];
+ }
+ };
+
+ /**
+ * Builder for {@link MediaDescription} objects.
+ */
+ public static class Builder {
+ private String mMediaId;
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+ private CharSequence mDescription;
+ private Bitmap mIcon;
+ private Uri mIconUri;
+ private Bundle mExtras;
+
+ /**
+ * Creates an initially empty builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the media id.
+ *
+ * @param mediaId The unique id for the item or null.
+ * @return this
+ */
+ public Builder setMediaId(@Nullable String mediaId) {
+ mMediaId = mediaId;
+ return this;
+ }
+
+ /**
+ * Sets the title.
+ *
+ * @param title A title suitable for display to the user or null.
+ * @return this
+ */
+ public Builder setTitle(@Nullable CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Sets the subtitle.
+ *
+ * @param subtitle A subtitle suitable for display to the user or null.
+ * @return this
+ */
+ public Builder setSubtitle(@Nullable CharSequence subtitle) {
+ mSubtitle = subtitle;
+ return this;
+ }
+
+ /**
+ * Sets the description.
+ *
+ * @param description A description suitable for display to the user or
+ * null.
+ * @return this
+ */
+ public Builder setDescription(@Nullable CharSequence description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets the icon.
+ *
+ * @param icon A {@link Bitmap} icon suitable for display to the user or
+ * null.
+ * @return this
+ */
+ public Builder setIconBitmap(@Nullable Bitmap icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets the icon uri.
+ *
+ * @param iconUri A {@link Uri} for an icon suitable for display to the
+ * user or null.
+ * @return this
+ */
+ public Builder setIconUri(@Nullable Uri iconUri) {
+ mIconUri = iconUri;
+ return this;
+ }
+
+ /**
+ * Sets a bundle of extras.
+ *
+ * @param extras The extras to include with this description or null.
+ * @return this
+ */
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ public MediaDescription build() {
+ return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
+ mExtras);
+ }
+ }
+}
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 74f7a96..b4e6033 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -17,15 +17,22 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.text.format.Time;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Size;
import android.util.SparseArray;
import java.util.Set;
@@ -119,7 +126,9 @@
public static final String METADATA_KEY_ART = "android.media.metadata.ART";
/**
- * The artwork for the media as a Uri.
+ * The artwork for the media as a Uri formatted String. The artwork can be
+ * loaded using a combination of {@link ContentResolver#openInputStream} and
+ * {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
@@ -130,7 +139,10 @@
public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
/**
- * The artwork for the album of the media's original source as a Uri.
+ * The artwork for the album of the media's original source as a Uri
+ * formatted String. The artwork can be loaded using a combination of
+ * {@link ContentResolver#openInputStream} and
+ * {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
@@ -181,13 +193,26 @@
= "android.media.metadata.DISPLAY_ICON";
/**
- * An icon or thumbnail that is suitable for display to the user. When
- * displaying more information for media described by this metadata the
- * display description should be preferred to other fields when present.
+ * A Uri formatted String for an icon or thumbnail that is suitable for
+ * display to the user. When displaying more information for media described
+ * by this metadata the display description should be preferred to other
+ * fields when present. The icon can be loaded using a combination of
+ * {@link ContentResolver#openInputStream} and
+ * {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_DISPLAY_ICON_URI
= "android.media.metadata.DISPLAY_ICON_URI";
+ /**
+ * A String key for identifying the content. This value is specific to the
+ * service providing the content. If used, this should be a persistent
+ * unique key for the underlying content. It may be used with
+ * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
+ * to initiate playback when provided by a {@link MediaBrowser} connected to
+ * the same app.
+ */
+ public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+
private static final String[] PREFERRED_DESCRIPTION_ORDER = {
METADATA_KEY_TITLE,
METADATA_KEY_ARTIST,
@@ -277,7 +302,7 @@
}
private final Bundle mBundle;
- private Description mDescription;
+ private MediaDescription mDescription;
private MediaMetadata(Bundle bundle) {
mBundle = new Bundle(bundle);
@@ -406,11 +431,13 @@
*
* @return A simple description of this metadata.
*/
- public @NonNull Description getDescription() {
+ public @NonNull MediaDescription getDescription() {
if (mDescription != null) {
return mDescription;
}
+ String mediaId = getString(METADATA_KEY_MEDIA_ID);
+
CharSequence[] text = new CharSequence[3];
Bitmap icon = null;
Uri iconUri = null;
@@ -454,7 +481,15 @@
}
}
- mDescription = new Description(text[0], text[1], text[2], icon, iconUri);
+ MediaDescription.Builder bob = new MediaDescription.Builder();
+ bob.setMediaId(mediaId);
+ bob.setTitle(text[0]);
+ bob.setSubtitle(text[1]);
+ bob.setDescription(text[2]);
+ bob.setIconBitmap(icon);
+ bob.setIconUri(iconUri);
+ mDescription = bob.build();
+
return mDescription;
}
@@ -668,90 +703,4 @@
return new MediaMetadata(mBundle);
}
}
-
- /**
- * A simple form of the metadata that can be used for display.
- */
- public final class Description {
- /**
- * A primary title suitable for display or null.
- */
- private final CharSequence mTitle;
- /**
- * A subtitle suitable for display or null.
- */
- private final CharSequence mSubtitle;
- /**
- * A description suitable for display or null.
- */
- private final CharSequence mDescription;
- /**
- * A bitmap icon suitable for display or null.
- */
- private final Bitmap mIcon;
- /**
- * A Uri for an icon suitable for display or null.
- */
- private final Uri mIconUri;
-
- /**
- * Returns the best available title or null.
- *
- * @return A title or null.
- */
- public @Nullable CharSequence getTitle() {
- return mTitle;
- }
-
- /**
- * Returns the best available subtitle or null.
- *
- * @return A subtitle or null.
- */
- public @Nullable CharSequence getSubtitle() {
- return mSubtitle;
- }
-
- /**
- * Returns the best available description or null.
- *
- * @return A description or null.
- */
- public @Nullable CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Returns the best available icon or null.
- *
- * @return An icon or null.
- */
- public @Nullable Bitmap getIcon() {
- return mIcon;
- }
-
- /**
- * Returns the best available icon Uri or null.
- *
- * @return An icon uri or null.
- */
- public @Nullable Uri getIconUri() {
- return mIconUri;
- }
-
- private Description(CharSequence title, CharSequence subtitle, CharSequence description,
- Bitmap icon, Uri iconUri) {
- mTitle = title;
- mSubtitle = subtitle;
- mDescription = description;
- mIcon = icon;
- mIconUri = iconUri;
- }
-
- @Override
- public String toString() {
- return mTitle + ", " + mSubtitle + ", " + mDescription;
- }
- }
-
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index a221104..7d075ba 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -115,6 +115,15 @@
}
/**
+ * Returns the {@link AudioAttributes} used by this object.
+ * @return the {@link AudioAttributes} that were set with
+ * {@link #setAudioAttributes(AudioAttributes)} or the default attributes if none were set.
+ */
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttributes;
+ }
+
+ /**
* Returns a human-presentable title for ringtone. Looks in media
* content provider. If not in either, uses the filename
*
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 1c6d81f..debaf45 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -16,6 +16,7 @@
package android.media.browse;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -23,26 +24,27 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
+import android.media.MediaDescription;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
+import android.service.media.MediaBrowserService;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.SparseArray;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
-import java.util.Set;
/**
* Browses media content offered by a link MediaBrowserService.
@@ -67,8 +69,6 @@
private final Handler mHandler = new Handler();
private final ArrayMap<Uri,Subscription> mSubscriptions =
new ArrayMap<Uri, MediaBrowser.Subscription>();
- private final SparseArray<IconRequest> mIconRequests =
- new SparseArray<IconRequest>();
private int mState = CONNECT_STATE_DISCONNECTED;
private MediaServiceConnection mServiceConnection;
@@ -77,7 +77,6 @@
private Uri mRootUri;
private MediaSession.Token mMediaSessionToken;
private Bundle mExtras;
- private int mNextSeq;
/**
* Creates a media browser for the specified media browse service.
@@ -363,49 +362,6 @@
}
/**
- * Loads the icon of a media item.
- *
- * @param uri The uri of the Icon.
- * @param width The preferred width of the icon in dp.
- * @param height The preferred width of the icon in dp.
- * @param callback The callback to receive the icon.
- */
- public void loadIcon(final @NonNull Uri uri, final int width, final int height,
- final @NonNull IconCallback callback) {
- if (uri == null) {
- throw new IllegalArgumentException("Icon uri cannot be null");
- }
- if (callback == null) {
- throw new IllegalArgumentException("Icon callback cannot be null");
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < mIconRequests.size(); i++) {
- IconRequest existingRequest = mIconRequests.valueAt(i);
- if (existingRequest.isSameRequest(uri, width, height)) {
- existingRequest.addCallback(callback);
- return;
- }
- }
- final int seq = mNextSeq++;
- IconRequest request = new IconRequest(seq, uri, width, height);
- request.addCallback(callback);
- mIconRequests.put(seq, request);
- if (mState == CONNECT_STATE_CONNECTED) {
- try {
- mServiceBinder.loadIcon(seq, uri, width, height, mServiceCallbacks);
- } catch (RemoteException e) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reload the icons. So nothing to do here.
- Log.d(TAG, "loadIcon failed with RemoteException uri=" + uri);
- }
- }
- }
- });
- }
-
- /**
* For debugging.
*/
private static String getStateLabel(int state) {
@@ -461,18 +417,6 @@
Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + uri);
}
}
-
- for (int i = 0; i < mIconRequests.size(); i++) {
- IconRequest request = mIconRequests.valueAt(i);
- try {
- mServiceBinder.loadIcon(request.mSeq, request.mUri,
- request.mWidth, request.mHeight, mServiceCallbacks);
- } catch (RemoteException e) {
- // Process is crashing. We will disconnect, and upon reconnect we will
- // automatically reload. So nothing to do here.
- Log.d(TAG, "loadIcon failed with RemoteException request=" + request);
- }
- }
}
});
}
@@ -515,7 +459,7 @@
return;
}
- List<MediaBrowserItem> data = list.getList();
+ List<MediaItem> data = list.getList();
if (DBG) {
Log.d(TAG, "onLoadChildren for " + mServiceComponent + " uri=" + uri);
}
@@ -539,32 +483,6 @@
});
}
- private final void onLoadIcon(final IMediaBrowserServiceCallbacks callback,
- final int seqNum, final Bitmap bitmap) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Check that there hasn't been a disconnect or a different
- // ServiceConnection.
- if (!isCurrent(callback, "onLoadIcon")) {
- return;
- }
-
- IconRequest request = mIconRequests.get(seqNum);
- if (request == null) {
- Log.d(TAG, "onLoadIcon called for seqNum=" + seqNum + " request="
- + request + " but the request is not registered");
- return;
- }
- mIconRequests.delete(seqNum);
- for (IconCallback IconCallback : request.getCallbacks()) {
- IconCallback.onIconLoaded(request.mUri, bitmap);
- }
- }
- });
- }
-
-
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
*/
@@ -600,6 +518,126 @@
Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken);
}
+ public static class MediaItem implements Parcelable {
+ private final int mFlags;
+ private final MediaDescription mDescription;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+ public @interface Flags { }
+
+ /**
+ * Flag: Indicates that the item has children of its own.
+ */
+ public static final int FLAG_BROWSABLE = 1 << 0;
+
+ /**
+ * Flag: Indicates that the item is playable.
+ * <p>
+ * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
+ * to start playing it.
+ * </p>
+ */
+ public static final int FLAG_PLAYABLE = 1 << 1;
+
+ /**
+ * Create a new MediaItem for use in browsing media.
+ *
+ * @param flags The flags for this item.
+ * @param description The description of the media, which must include a
+ * media id.
+ */
+ public MediaItem(@Flags int flags, @NonNull MediaDescription description) {
+ if (description == null) {
+ throw new IllegalArgumentException("description cannot be null");
+ }
+ if (TextUtils.isEmpty(description.getMediaId())) {
+ throw new IllegalArgumentException("description must have a non-empty media id");
+ }
+ mFlags = flags;
+ mDescription = description;
+ }
+
+ /**
+ * Private constructor.
+ */
+ private MediaItem(Parcel in) {
+ mFlags = in.readInt();
+ mDescription = MediaDescription.CREATOR.createFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mFlags);
+ mDescription.writeToParcel(out, flags);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("MediaItem{");
+ sb.append("mFlags=").append(mFlags);
+ sb.append(", mDescription=").append(mDescription);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public static final Parcelable.Creator<MediaItem> CREATOR =
+ new Parcelable.Creator<MediaItem>() {
+ @Override
+ public MediaItem createFromParcel(Parcel in) {
+ return new MediaItem(in);
+ }
+
+ @Override
+ public MediaItem[] newArray(int size) {
+ return new MediaItem[size];
+ }
+ };
+
+ /**
+ * Gets the flags of the item.
+ */
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns whether this item is browsable.
+ * @see #FLAG_BROWSABLE
+ */
+ public boolean isBrowsable() {
+ return (mFlags & FLAG_BROWSABLE) != 0;
+ }
+
+ /**
+ * Returns whether this item is playable.
+ * @see #FLAG_PLAYABLE
+ */
+ public boolean isPlayable() {
+ return (mFlags & FLAG_PLAYABLE) != 0;
+ }
+
+ /**
+ * Returns the description of the media.
+ */
+ public @NonNull MediaDescription getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Returns the media id for this item.
+ */
+ public @NonNull String getMediaId() {
+ return mDescription.getMediaId();
+ }
+ }
+
/**
* Callbacks for connection related events.
@@ -632,7 +670,7 @@
* Called when the list of children is loaded or updated.
*/
public void onChildrenLoaded(@NonNull Uri parentUri,
- @NonNull List<MediaBrowserItem> children) {
+ @NonNull List<MediaItem> children) {
}
/**
@@ -647,87 +685,6 @@
}
/**
- * Callbacks for icon loading.
- */
- public static abstract class IconCallback {
- /**
- * Called when the icon is loaded.
- */
- public void onIconLoaded(@NonNull Uri uri, @NonNull Bitmap bitmap) {
- }
-
- /**
- * Called when the Uri doesn’t exist or the bitmap cannot be loaded.
- */
- public void onError(@NonNull Uri uri) {
- }
- }
-
- private static class IconRequest {
- final int mSeq;
- final Uri mUri;
- final int mWidth;
- final int mHeight;
- final List<IconCallback> mCallbacks;
-
- /**
- * Constructs an icon request.
- * @param seq The unique sequence number assigned to the request by the media browser.
- * @param uri The Uri for the icon.
- * @param width The width for the icon.
- * @param height The height for the icon.
- */
- IconRequest(int seq, @NonNull Uri uri, int width, int height) {
- if (uri == null) {
- throw new IllegalArgumentException("Icon uri cannot be null");
- }
- this.mSeq = seq;
- this.mUri = uri;
- this.mWidth = width;
- this.mHeight = height;
- mCallbacks = new ArrayList<IconCallback>();
- }
-
- /**
- * Adds a callback to the icon request.
- * If the callback already exists, it will not be added again.
- */
- public void addCallback(@NonNull IconCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null in IconRequest");
- }
- if (!mCallbacks.contains(callback)) {
- mCallbacks.add(callback);
- }
- }
-
- /**
- * Checks if the icon request has the same uri, width, and height as the given values.
- */
- public boolean isSameRequest(@Nullable Uri uri, int width, int height) {
- return Objects.equals(mUri, uri) && mWidth == width && mHeight == height;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("IconRequest{");
- sb.append("uri=").append(mUri);
- sb.append(", width=").append(mWidth);
- sb.append(", height=").append(mHeight);
- sb.append(", seq=").append(mSeq);
- sb.append('}');
- return sb.toString();
- }
-
- /**
- * Gets an unmodifiable view of the list of callbacks associated with the request.
- */
- public List<IconCallback> getCallbacks() {
- return Collections.unmodifiableList(mCallbacks);
- }
- }
-
- /**
* ServiceConnection to the other app.
*/
private class MediaServiceConnection implements ServiceConnection {
@@ -809,7 +766,7 @@
}
return true;
}
- };
+ }
/**
* Callbacks from the service.
@@ -852,14 +809,6 @@
mediaBrowser.onLoadChildren(this, uri, list);
}
}
-
- @Override
- public void onLoadIcon(final int seqNum, final Bitmap bitmap) {
- MediaBrowser mediaBrowser = mMediaBrowser.get();
- if (mediaBrowser != null) {
- mediaBrowser.onLoadIcon(this, seqNum, bitmap);
- }
- }
}
private static class Subscription {
diff --git a/media/java/android/media/browse/MediaBrowserItem.java b/media/java/android/media/browse/MediaBrowserItem.java
deleted file mode 100644
index 47ec46b..0000000
--- a/media/java/android/media/browse/MediaBrowserItem.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.browse;
-
-import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.net.Uri;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Describes a media item in the list of items offered by a {@link MediaBrowserService}.
- */
-public final class MediaBrowserItem implements Parcelable {
- private final Uri mUri;
- private final int mFlags;
- private final CharSequence mTitle;
- private final CharSequence mSummary;
- private final Uri mIconUri;
- private final int mIconResourceId;
- private final Bundle mExtras;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
- public @interface Flags { }
-
- /**
- * Flag: Indicates that the item has children of its own.
- */
- public static final int FLAG_BROWSABLE = 1 << 0;
-
- /**
- * Flag: Indicates that the item is playable.
- * <p>
- * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
- * to start playing it.
- * </p>
- */
- public static final int FLAG_PLAYABLE = 1 << 1;
-
- /**
- * Initialize a MediaBrowserItem object.
- */
- private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
- CharSequence summary, @Nullable Uri iconUri, int iconResourceId, Bundle extras) {
- if (uri == null) {
- throw new IllegalArgumentException("uri can not be null");
- }
- if (title == null) {
- throw new IllegalArgumentException("title can not be null");
- }
- mUri = uri;
- mFlags = flags;
- mTitle = title;
- mSummary = summary;
- mIconUri = iconUri;
- mIconResourceId = iconResourceId;
- mExtras = extras;
- }
-
- /**
- * Private constructor.
- */
- private MediaBrowserItem(Parcel in) {
- mUri = Uri.CREATOR.createFromParcel(in);
- mFlags = in.readInt();
- mTitle = in.readCharSequence();
- if (in.readInt() != 0) {
- mSummary = in.readCharSequence();
- } else {
- mSummary = null;
- }
- if (in.readInt() != 0) {
- mIconUri = Uri.CREATOR.createFromParcel(in);
- } else {
- mIconUri = null;
- }
- mIconResourceId = in.readInt();
- if (in.readInt() != 0) {
- mExtras = Bundle.CREATOR.createFromParcel(in);
- } else {
- mExtras = null;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- mUri.writeToParcel(out, flags);
- out.writeInt(mFlags);
- out.writeCharSequence(mTitle);
- if (mSummary != null) {
- out.writeInt(1);
- out.writeCharSequence(mSummary);
- } else {
- out.writeInt(0);
- }
- if (mIconUri != null) {
- out.writeInt(1);
- mIconUri.writeToParcel(out, flags);
- } else {
- out.writeInt(0);
- }
- out.writeInt(mIconResourceId);
- if (mExtras != null) {
- out.writeInt(1);
- mExtras.writeToParcel(out, flags);
- } else {
- out.writeInt(0);
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("MediaBrowserItem{");
- sb.append("mUri=").append(mUri);
- sb.append(", mFlags=").append(mFlags);
- sb.append(", mTitle=").append(mTitle);
- sb.append(", mSummary=").append(mSummary);
- sb.append(", mIconUri=").append(mIconUri);
- sb.append(", mIconResourceId=").append(mIconResourceId);
- sb.append('}');
- return sb.toString();
- }
-
- public static final Parcelable.Creator<MediaBrowserItem> CREATOR =
- new Parcelable.Creator<MediaBrowserItem>() {
- @Override
- public MediaBrowserItem createFromParcel(Parcel in) {
- return new MediaBrowserItem(in);
- }
-
- @Override
- public MediaBrowserItem[] newArray(int size) {
- return new MediaBrowserItem[size];
- }
- };
-
- /**
- * Gets the Uri of the item.
- */
- public @NonNull Uri getUri() {
- return mUri;
- }
-
- /**
- * Gets the flags of the item.
- */
- public @Flags int getFlags() {
- return mFlags;
- }
-
- /**
- * Returns whether this item is browsable.
- * @see #FLAG_BROWSABLE
- */
- public boolean isBrowsable() {
- return (mFlags & FLAG_BROWSABLE) != 0;
- }
-
- /**
- * Returns whether this item is playable.
- * @see #FLAG_PLAYABLE
- */
- public boolean isPlayable() {
- return (mFlags & FLAG_PLAYABLE) != 0;
- }
-
- /**
- * Gets the title of the item.
- * @more
- * The title will be shown as the first line of text when
- * describing each item to the user.
- */
- public @NonNull CharSequence getTitle() {
- return mTitle;
- }
-
- /**
- * Gets summary of the item, or null if none.
- * @more
- * The summary will be shown as the second line of text when
- * describing each item to the user.
- */
- public @Nullable CharSequence getSummary() {
- return mSummary;
- }
-
- /**
- * Gets the Uri of the icon.
- */
- public @Nullable Uri getIconUri() {
- return mIconUri;
- }
-
- /**
- * Gets the resource id of the icon.
- */
- public @DrawableRes int getIconResourceId() {
- return mIconResourceId;
- }
-
- /**
- * Gets additional service-specified extras about the
- * item or its content, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Builder for {@link MediaBrowserItem} objects.
- */
- public static final class Builder {
- private final Uri mUri;
- private final int mFlags;
- private final CharSequence mTitle;
- private CharSequence mSummary;
- private Uri mIconUri;
- private int mIconResourceId;
- private Bundle mExtras;
-
- /**
- * Creates an item builder.
- */
- public Builder(@NonNull Uri uri, @Flags int flags, @NonNull CharSequence title) {
- if (uri == null) {
- throw new IllegalArgumentException("uri can not be null");
- }
- if (title == null) {
- throw new IllegalArgumentException("title can not be null");
- }
- mUri = uri;
- mFlags = flags;
- mTitle = title;
- }
-
- /**
- * Sets summary of the item, or null if none.
- */
- public @NonNull Builder setSummary(@Nullable CharSequence summary) {
- mSummary = summary;
- return this;
- }
-
- /**
- * Sets the uri of the icon.
- * <p>
- * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be called.
- * If both are specified, the resource id will be used to load the icon.
- * </p>
- */
- public @NonNull Builder setIconUri(@Nullable Uri iconUri) {
- mIconUri = iconUri;
- return this;
- }
-
- /**
- * Sets the resource id of the icon.
- * <p>
- * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be specified.
- * If both are specified, the resource id will be used to load the icon.
- * </p>
- */
- public @NonNull Builder setIconResourceId(@DrawableRes int ResourceId) {
- mIconResourceId = ResourceId;
- return this;
- }
-
- /**
- * Sets additional service-specified extras about the
- * item or its content.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the item.
- */
- public @NonNull MediaBrowserItem build() {
- return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mIconUri,
- mIconResourceId, mExtras);
- }
- }
-}
-
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9911129..49087b0 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -30,7 +30,7 @@
// These callbacks are for the TransportPerformer
void onPlay();
- void onPlayUri(in Uri uri, in Bundle extras);
+ void onPlayFromMediaId(String uri, in Bundle extras);
void onPlayFromSearch(String query, in Bundle extras);
void onSkipToTrack(long id);
void onPause();
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 6b80477..d684688 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -51,9 +51,9 @@
// These commands are for the TransportControls
void play();
- void playUri(in Uri uri, in Bundle extras);
+ void playFromMediaId(String uri, in Bundle extras);
void playFromSearch(String string, in Bundle extras);
- void skipToTrack(long id);
+ void skipToQueueItem(long id);
void pause();
void stop();
void next();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index d7baaa9..9641f83 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -173,7 +173,7 @@
*
* @return The current play queue or null.
*/
- public @Nullable List<MediaSession.Item> getQueue() {
+ public @Nullable List<MediaSession.QueueItem> getQueue() {
try {
ParceledListSlice queue = mSessionBinder.getQueue();
if (queue != null) {
@@ -538,9 +538,9 @@
* @param queue A list of items in the current play queue. It should
* include the currently playing item as well as previous and
* upcoming items if applicable.
- * @see MediaSession.Item
+ * @see MediaSession.QueueItem
*/
- public void onQueueChanged(@Nullable List<MediaSession.Item> queue) {
+ public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
}
/**
@@ -594,18 +594,19 @@
/**
* Request that the player start playback for a specific {@link Uri}.
*
- * @param uri The uri of the requested media.
+ * @param mediaId The uri of the requested media.
* @param extras Optional extras that can include extra information about the media item
* to be played.
*/
- public void playUri(Uri uri, Bundle extras) {
- if (uri == null) {
- throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
+ public void playFromMediaId(String mediaId, Bundle extras) {
+ if (TextUtils.isEmpty(mediaId)) {
+ throw new IllegalArgumentException(
+ "You must specify a non-empty String for playFromMediaId.");
}
try {
- mSessionBinder.playUri(uri, extras);
+ mSessionBinder.playFromMediaId(mediaId, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+ Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
}
}
@@ -631,9 +632,9 @@
* Play an item with a specific id in the play queue. If you specify an
* id that is not in the play queue, the behavior is undefined.
*/
- public void skipToItem(long id) {
+ public void skipToQueueItem(long id) {
try {
- mSessionBinder.skipToTrack(id);
+ mSessionBinder.skipToQueueItem(id);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
}
@@ -904,7 +905,7 @@
@Override
public void onQueueChanged(ParceledListSlice parceledQueue) {
- List<MediaSession.Item> queue = parceledQueue.getList();
+ List<MediaSession.QueueItem> queue = parceledQueue.getList();
MediaController controller = mController.get();
if (controller != null) {
controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -960,7 +961,7 @@
mCallback.onMetadataChanged((MediaMetadata) msg.obj);
break;
case MSG_UPDATE_QUEUE:
- mCallback.onQueueChanged((List<MediaSession.Item>) msg.obj);
+ mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
break;
case MSG_UPDATE_QUEUE_TITLE:
mCallback.onQueueTitleChanged((CharSequence) msg.obj);
diff --git a/media/java/android/media/session/MediaSession.aidl b/media/java/android/media/session/MediaSession.aidl
index 0ad58c4..f657cef 100644
--- a/media/java/android/media/session/MediaSession.aidl
+++ b/media/java/android/media/session/MediaSession.aidl
@@ -16,4 +16,4 @@
package android.media.session;
parcelable MediaSession.Token;
-parcelable MediaSession.Track;
\ No newline at end of file
+parcelable MediaSession.QueueItem;
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1670097..ae8ce4b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
+import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -38,6 +39,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
+import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -180,18 +182,26 @@
* @param handler The handler that events should be posted on.
*/
public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
- if (callback == null) {
- mCallback = null;
- return;
- }
synchronized (mLock) {
- if (mCallback != null && mCallback.mCallback == callback) {
- Log.w(TAG, "Tried to set same callback, ignoring");
+ if (callback == null) {
+ if (mCallback != null) {
+ mCallback.mCallback.mSession = null;
+ }
+ mCallback = null;
return;
}
+ if (mCallback != null) {
+ if (mCallback.mCallback == callback) {
+ Log.w(TAG, "Tried to set same callback, ignoring");
+ return;
+ }
+ // We're changing callbacks, clear the session from the old one.
+ mCallback.mCallback.mSession = null;
+ }
if (handler == null) {
handler = new Handler();
}
+ callback.mSession = this;
CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
callback);
mCallback = msgHandler;
@@ -418,9 +428,9 @@
*
* @param queue A list of items in the play queue.
*/
- public void setQueue(@Nullable List<Item> queue) {
+ public void setQueue(@Nullable List<QueueItem> queue) {
try {
- mBinder.setQueue(new ParceledListSlice<Item>(queue));
+ mBinder.setQueue(new ParceledListSlice<QueueItem>(queue));
} catch (RemoteException e) {
Log.wtf("Dead object in setQueue.", e);
}
@@ -478,8 +488,8 @@
postToCallback(CallbackMessageHandler.MSG_PLAY);
}
- private void dispatchPlayUri(Uri uri, Bundle extras) {
- postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
+ private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
}
private void dispatchPlayFromSearch(String query, Bundle extras) {
@@ -673,6 +683,7 @@
*
* @param mediaButtonIntent an intent containing the KeyEvent as an
* extra
+ * @return True if the event was handled, false otherwise.
*/
public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
if (mSession != null
@@ -685,36 +696,43 @@
case KeyEvent.KEYCODE_MEDIA_PLAY:
if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
onPlay();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
onPause();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
onSkipToNext();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
onSkipToPrevious();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_STOP:
if ((validActions & PlaybackState.ACTION_STOP) != 0) {
onStop();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
onFastForward();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_REWIND:
if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
onRewind();
+ return true;
}
break;
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -727,8 +745,10 @@
| PlaybackState.ACTION_PAUSE)) != 0;
if (isPlaying && canPause) {
onPause();
+ return true;
} else if (!isPlaying && canPlay) {
onPlay();
+ return true;
}
break;
}
@@ -744,9 +764,10 @@
}
/**
- * Override to handle requests to play a specific {@link Uri}.
+ * Override to handle requests to play a specific mediaId that was
+ * provided by your app's {@link MediaBrowserService}.
*/
- public void onPlayUri(Uri uri, Bundle extras) {
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
}
/**
@@ -759,7 +780,7 @@
* Override to handle requests to play an item with a given id from the
* play queue.
*/
- public void onSkipToItem(long id) {
+ public void onSkipToQueueItem(long id) {
}
/**
@@ -824,10 +845,6 @@
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
-
- private void setSession(MediaSession session) {
- mSession = session;
- }
}
/**
@@ -872,10 +889,10 @@
}
@Override
- public void onPlayUri(Uri uri, Bundle extras) {
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPlayUri(uri, extras);
+ session.dispatchPlayFromMediaId(mediaId, extras);
}
}
@@ -990,125 +1007,59 @@
}
/**
- * A single item that is part of the play queue. It contains information
- * necessary to display a single item in the queue.
+ * A single item that is part of the play queue. It contains a description
+ * of the item and its id in the queue.
*/
- public static final class Item implements Parcelable {
+ public static final class QueueItem implements Parcelable {
/**
* This id is reserved. No items can be explicitly asigned this id.
*/
public static final int UNKNOWN_ID = -1;
- private final MediaMetadata mMetadata;
+ private final MediaDescription mDescription;
private final long mId;
- private final Uri mUri;
- private final Bundle mExtras;
/**
- * Create a new {@link MediaSession.Item}.
+ * Create a new {@link MediaSession.QueueItem}.
*
- * @param metadata The metadata for this item.
+ * @param description The {@link MediaDescription} for this item.
* @param id An identifier for this item. It must be unique within the
- * play queue.
- * @param uri The uri for this item.
- * @param extras A bundle of extras that can be used to add extra
- * information about this item.
+ * play queue and cannot be {@link #UNKNOWN_ID}.
*/
- private Item(MediaMetadata metadata, long id, Uri uri, Bundle extras) {
- mMetadata = metadata;
+ public QueueItem(MediaDescription description, long id) {
+ if (description == null) {
+ throw new IllegalArgumentException("Description cannot be null.");
+ }
+ if (id == UNKNOWN_ID) {
+ throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
+ }
+ mDescription = description;
mId = id;
- mUri = uri;
- mExtras = extras;
}
- private Item(Parcel in) {
- mMetadata = MediaMetadata.CREATOR.createFromParcel(in);
+ private QueueItem(Parcel in) {
+ mDescription = MediaDescription.CREATOR.createFromParcel(in);
mId = in.readLong();
- mUri = Uri.CREATOR.createFromParcel(in);
- mExtras = in.readBundle();
}
/**
- * Get the metadata for this item.
+ * Get the description for this item.
*/
- public MediaMetadata getMetadata() {
- return mMetadata;
+ public MediaDescription getDescription() {
+ return mDescription;
}
/**
- * Get the id for this item.
+ * Get the queue id for this item.
*/
- public long getId() {
+ public long getQueueId() {
return mId;
}
- /**
- * Get the Uri for this item.
- */
- public Uri getUri() {
- return mUri;
- }
-
- /**
- * Get the extras for this item.
- */
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Builder for {@link MediaSession.Item} objects.
- */
- public static final class Builder {
- private final MediaMetadata mMetadata;
- private final long mId;
- private final Uri mUri;
-
- private Bundle mExtras;
-
- /**
- * Create a builder with the metadata, id, and uri already set.
- */
- public Builder(MediaMetadata metadata, long id, Uri uri) {
- if (metadata == null) {
- throw new IllegalArgumentException(
- "You must specify a non-null MediaMetadata to build an Item.");
- }
- if (uri == null) {
- throw new IllegalArgumentException(
- "You must specify a non-null Uri to build an Item.");
- }
- if (id == UNKNOWN_ID) {
- throw new IllegalArgumentException(
- "You must specify an id other than UNKNOWN_ID to build an Item.");
- }
- mMetadata = metadata;
- mId = id;
- mUri = uri;
- }
-
- /**
- * Set optional extras for the item.
- */
- public MediaSession.Item.Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Create the {@link Item}.
- */
- public MediaSession.Item build() {
- return new MediaSession.Item(mMetadata, mId, mUri, mExtras);
- }
- }
-
@Override
public void writeToParcel(Parcel dest, int flags) {
- mMetadata.writeToParcel(dest, flags);
+ mDescription.writeToParcel(dest, flags);
dest.writeLong(mId);
- mUri.writeToParcel(dest, flags);
- dest.writeBundle(mExtras);
}
@Override
@@ -1116,28 +1067,24 @@
return 0;
}
- public static final Creator<MediaSession.Item> CREATOR
- = new Creator<MediaSession.Item>() {
+ public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
@Override
- public MediaSession.Item createFromParcel(Parcel p) {
- return new MediaSession.Item(p);
+ public MediaSession.QueueItem createFromParcel(Parcel p) {
+ return new MediaSession.QueueItem(p);
}
@Override
- public MediaSession.Item[] newArray(int size) {
- return new MediaSession.Item[size];
+ public MediaSession.QueueItem[] newArray(int size) {
+ return new MediaSession.QueueItem[size];
}
};
@Override
public String toString() {
- return "MediaSession.Item {" +
- "Metadata=" + mMetadata +
- ", Id=" + mId +
- ", Uri=" + mUri +
- ", Extras=" + mExtras +
- " }";
+ return "MediaSession.QueueItem {" +
+ "Description=" + mDescription +
+ ", Id=" + mId + " }";
}
}
@@ -1156,7 +1103,7 @@
private class CallbackMessageHandler extends Handler {
private static final int MSG_PLAY = 1;
- private static final int MSG_PLAY_URI = 2;
+ private static final int MSG_PLAY_MEDIA_ID = 2;
private static final int MSG_PLAY_SEARCH = 3;
private static final int MSG_SKIP_TO_ITEM = 4;
private static final int MSG_PAUSE = 5;
@@ -1202,14 +1149,14 @@
case MSG_PLAY:
mCallback.onPlay();
break;
- case MSG_PLAY_URI:
- mCallback.onPlayUri((Uri) msg.obj, msg.getData());
+ case MSG_PLAY_MEDIA_ID:
+ mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
break;
case MSG_PLAY_SEARCH:
mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
break;
case MSG_SKIP_TO_ITEM:
- mCallback.onSkipToItem((Long) msg.obj);
+ mCallback.onSkipToQueueItem((Long) msg.obj);
case MSG_PAUSE:
mCallback.onPause();
break;
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 2ca97dd..267d1ff 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -36,95 +36,95 @@
private static final String TAG = "PlaybackState";
/**
- * Indicates this performer supports the stop command.
+ * Indicates this session supports the stop command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_STOP = 1 << 0;
/**
- * Indicates this performer supports the pause command.
+ * Indicates this session supports the pause command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PAUSE = 1 << 1;
/**
- * Indicates this performer supports the play command.
+ * Indicates this session supports the play command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY = 1 << 2;
/**
- * Indicates this performer supports the rewind command.
+ * Indicates this session supports the rewind command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_REWIND = 1 << 3;
/**
- * Indicates this performer supports the previous command.
+ * Indicates this session supports the previous command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
/**
- * Indicates this performer supports the next command.
+ * Indicates this session supports the next command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
/**
- * Indicates this performer supports the fast forward command.
+ * Indicates this session supports the fast forward command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_FAST_FORWARD = 1 << 6;
/**
- * Indicates this performer supports the set rating command.
+ * Indicates this session supports the set rating command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SET_RATING = 1 << 7;
/**
- * Indicates this performer supports the seek to command.
+ * Indicates this session supports the seek to command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SEEK_TO = 1 << 8;
/**
- * Indicates this performer supports the play/pause toggle command.
+ * Indicates this session supports the play/pause toggle command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_PAUSE = 1 << 9;
/**
- * Indicates this performer supports the play from uri command.
+ * Indicates this session supports the play from media id command.
*
* @see Builder#setActions(long)
*/
- public static final long ACTION_PLAY_URI = 1 << 10;
+ public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
/**
- * Indicates this performer supports the play from search command.
+ * Indicates this session supports the play from search command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
/**
- * Indicates this performer supports the skip to item command.
+ * Indicates this session supports the skip to queue item command.
*
* @see Builder#setActions(long)
*/
- public static final long ACTION_SKIP_TO_ITEM = 1 << 12;
+ public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
/**
* This is the default playback state and indicates that no media has been
@@ -211,6 +211,14 @@
public final static int STATE_SKIPPING_TO_NEXT = 10;
/**
+ * State indicating the player is currently skipping to a specific item in
+ * the queue.
+ *
+ * @see Builder#setState
+ */
+ public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
+
+ /**
* Use this value for the position to indicate the position is not known.
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
@@ -374,6 +382,18 @@
}
/**
+ * Get the id of the currently active item in the queue. If there is no
+ * queue or a queue is not supported by the session this will be
+ * {@link MediaSession.QueueItem#UNKNOWN_ID}.
+ *
+ * @return The id of the currently active item in the queue or
+ * {@link MediaSession.QueueItem#UNKNOWN_ID}.
+ */
+ public long getActiveQueueItemId() {
+ return mActiveItemId;
+ }
+
+ /**
* Get the {@link PlaybackState} state for the given
* {@link RemoteControlClient} state.
*
@@ -716,7 +736,7 @@
private long mActions;
private CharSequence mErrorMessage;
private long mUpdateTime;
- private long mActiveItemId = MediaSession.Item.UNKNOWN_ID;
+ private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
/**
* Creates an initially empty state builder.
@@ -904,12 +924,12 @@
/**
* Set the active item in the play queue by specifying its id. The
- * default value is {@link MediaSession.Item#UNKNOWN_ID}
+ * default value is {@link MediaSession.QueueItem#UNKNOWN_ID}
*
* @param id The id of the active item.
* @return this
*/
- public Builder setActiveItem(long id) {
+ public Builder setActiveQueueItemId(long id) {
mActiveItemId = id;
return this;
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 0bda2b3..ccc6ad8 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -136,7 +136,7 @@
* <table>
* <tr>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td>com.android.tv</td>
@@ -148,7 +148,7 @@
* <table>
* <tr>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td>AM_TV_RS</td>
@@ -346,7 +346,7 @@
* <tr>
* <th>Rating System</th>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td valign="top" rowspan="6">AM_TV_RS</td>
@@ -1419,7 +1419,7 @@
* <tr>
* <th>Rating System</th>
* <th>Constant Value</th>
- * <th>Comment</th>
+ * <th>Description</th>
* </tr>
* <tr>
* <td valign="top" rowspan="6">NL_TV</td>
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 00c7ad4..7f1c304 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -680,7 +680,7 @@
* <p>
* A value of 1 indicates the channel is included in the channel list that applications use
* to browse channels, a value of 0 indicates the channel is not included in the list. If
- * not specified, this value is set to 1 (browsable) by default.
+ * not specified, this value is set to 0 (not browsable) by default.
* </p><p>
* Type: INTEGER (boolean)
* </p>
diff --git a/media/java/android/media/browse/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
similarity index 76%
rename from media/java/android/media/browse/IMediaBrowserService.aidl
rename to media/java/android/service/media/IMediaBrowserService.aidl
index 8acd724..2631ddd 100644
--- a/media/java/android/media/browse/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -1,9 +1,9 @@
// Copyright 2014 Google Inc. All Rights Reserved.
-package android.media.browse;
+package android.service.media;
import android.content.res.Configuration;
-import android.media.browse.IMediaBrowserServiceCallbacks;
+import android.service.media.IMediaBrowserServiceCallbacks;
import android.net.Uri;
import android.os.Bundle;
@@ -18,6 +18,4 @@
void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
- void loadIcon(in int seqNum, in Uri uri, int width, int height,
- IMediaBrowserServiceCallbacks callbacks);
}
\ No newline at end of file
diff --git a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
similarity index 91%
rename from media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
rename to media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 06fabcc..7702a50 100644
--- a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -1,6 +1,6 @@
// Copyright 2014 Google Inc. All Rights Reserved.
-package android.media.browse;
+package android.service.media;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -24,5 +24,4 @@
void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
void onConnectFailed();
void onLoadChildren(in Uri uri, in ParceledListSlice list);
- void onLoadIcon(int seqNum, in Bitmap bitmap);
}
diff --git a/media/java/android/media/browse/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
similarity index 81%
rename from media/java/android/media/browse/MediaBrowserService.java
rename to media/java/android/service/media/MediaBrowserService.java
index 99126c9..4d6fd7b 100644
--- a/media/java/android/media/browse/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.media.browse;
+package android.service.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +27,8 @@
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Binder;
@@ -34,6 +36,9 @@
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.service.media.IMediaBrowserService.Stub;
import android.util.ArrayMap;
import android.util.Log;
@@ -264,59 +269,6 @@
}
});
}
-
- @Override
- public void loadIcon(final int seq, final Uri uri, final int width, final int height,
- final IMediaBrowserServiceCallbacks callbacks) {
- if (uri == null) {
- throw new IllegalStateException("loadIcon sent null list for uri " + uri);
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // In theory we could return a result to a new connection, but in practice
- // the other side in MediaBrowser uses a new IMediaBrowserServiceCallbacks
- // object every time it calls connect(), so as long as it does that we won't
- // see results sent for the wrong connection.
- final ConnectionRecord connection = mConnections.get(callbacks.asBinder());
- if (connection == null) {
- if (DBG) {
- Log.d(TAG, "Not loading bitmap for invalid connection. uri=" + uri);
- }
- return;
- }
-
- final Result<Bitmap> result = new Result<Bitmap>(uri) {
- @Override
- void onResultSent(Bitmap bitmap) {
- if (mConnections.get(connection.callbacks.asBinder()) != connection) {
- if (DBG) {
- Log.d(TAG, "Not sending onLoadIcon result for connection"
- + " that has been disconnected. pkg=" + connection.pkg
- + " uri=" + uri);
- }
- return;
- }
-
- try {
- callbacks.onLoadIcon(seq, bitmap);
- } catch (RemoteException e) {
- // The other side is in the process of crashing.
- Log.w(TAG, "RemoteException in calling onLoadIcon", e);
- }
- }
- };
-
- onLoadIcon(uri, width, height, result);
-
- if (!result.isDone()) {
- throw new IllegalStateException("onLoadIcon must call detach() or"
- + " sendResult() before returning for package=" + connection.pkg
- + " uri=" + uri);
- }
- }
- });
- }
}
@Override
@@ -372,26 +324,7 @@
* @return The list of children, or null if the uri is invalid.
*/
public abstract void onLoadChildren(@NonNull Uri parentUri,
- @NonNull Result<List<MediaBrowserItem>> result);
-
- /**
- * Called to get the icon of a particular media item.
- * <p>
- * Implementations must call result.{@link Result#sendResult result.sendResult} with the bitmap.
- * If loading the bitmap will be an expensive operation that should be performed
- * on another thread, result.{@link Result#detach result.detach} may be called before returning
- * from this function, and then {@link Result#sendResult result.sendResult} called when
- * the loading is complete.
- *
- * @param uri The uri of the media item.
- * @param width The requested width of the icon in dp.
- * @param height The requested height of the icon in dp.
- *
- * @return The file descriptor of the icon, which may then be loaded
- * using a bitmap factory, or null if the item does not have an icon.
- */
- public abstract void onLoadIcon(@NonNull Uri uri, int width, int height,
- @NonNull Result<Bitmap> result);
+ @NonNull Result<List<MediaBrowser.MediaItem>> result);
/**
* Call to set the media session.
@@ -478,9 +411,11 @@
* Callers must make sure that this connection is still connected.
*/
private void performLoadChildren(final Uri uri, final ConnectionRecord connection) {
- final Result<List<MediaBrowserItem>> result = new Result<List<MediaBrowserItem>>(uri) {
+ final Result<List<MediaBrowser.MediaItem>> result
+ = new Result<List<MediaBrowser.MediaItem>>(
+ uri) {
@Override
- void onResultSent(List<MediaBrowserItem> list) {
+ void onResultSent(List<MediaBrowser.MediaItem> list) {
if (list == null) {
throw new IllegalStateException("onLoadChildren sent null list for uri " + uri);
}
@@ -492,7 +427,7 @@
return;
}
- final ParceledListSlice<MediaBrowserItem> pls = new ParceledListSlice(list);
+ final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(list);
try {
connection.callbacks.onLoadChildren(uri, pls);
} catch (RemoteException ex) {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c678ac7..0fed27e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -263,7 +263,7 @@
String8 vendorMessage;
if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
- vendorMessage.format("DRM vendor-defined error: %d", err);
+ vendorMessage = String8::format("DRM vendor-defined error: %d", err);
drmMessage = vendorMessage.string();
}
@@ -285,7 +285,7 @@
if (msg == NULL) {
msg = drmMessage;
} else {
- errbuf.format("%s: %s", msg, drmMessage);
+ errbuf = String8::format("%s: %s", msg, drmMessage);
msg = errbuf.string();
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 38d7e3e..86c23c7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -25,6 +25,9 @@
public class MediaNames {
// A directory to hold all kinds of media files
public static final String MEDIA_SAMPLE_POOL = "/sdcard/media_api/samples/";
+ // A file to hold all streaming URLs
+ public static final String MEDIA_STREAMING_SRC = "/sdcard/media_api/streaming.txt";
+
// Audio files
public static final String MP3CBR = "/sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3";
public static final String MP3VBR = "/sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_VBR.mp3";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index e84f762..66ed933 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -42,13 +42,13 @@
import java.util.Random;
/**
* Junit / Instrumentation test case for the media player api
-
- */
-public class CodecTest {
+
+ */
+public class CodecTest {
private static String TAG = "CodecTest";
private static MediaPlayer mMediaPlayer;
private MediaPlayer.OnPreparedListener mOnPreparedListener;
-
+
private static int WAIT_FOR_COMMAND_TO_COMPLETE = 60000; //1 min max.
private static boolean mInitialized = false;
private static boolean mPrepareReset = false;
@@ -66,18 +66,18 @@
public static int mMediaInfoNotSeekableCount = 0;
public static int mMediaInfoMetdataUpdateCount = 0;
- public static String printCpuInfo(){
+ public static String printCpuInfo(){
String cm = "dumpsys cpuinfo";
String cpuinfo =null;
int ch;
try{
Process p = Runtime.getRuntime().exec(cm);
- InputStream in = p.getInputStream();
+ InputStream in = p.getInputStream();
StringBuffer sb = new StringBuffer(512);
- while ( ( ch = in.read() ) != -1 ){
- sb.append((char) ch);
+ while ( ( ch = in.read() ) != -1 ){
+ sb.append((char) ch);
}
- cpuinfo = sb.toString();
+ cpuinfo = sb.toString();
}catch (IOException e){
Log.v(TAG, e.toString());
}
@@ -90,14 +90,14 @@
MediaPlayer mp = new MediaPlayer();
try{
mp.setDataSource(filePath);
- mp.prepare();
+ mp.prepare();
}catch (Exception e){
Log.v(TAG, e.toString());
}
int duration = mp.getDuration();
Log.v(TAG, "Duration " + duration);
mp.release();
- Log.v(TAG, "release");
+ Log.v(TAG, "release");
return duration;
}
@@ -122,7 +122,7 @@
}
currentPosition = mp.getCurrentPosition();
mp.stop();
- mp.release();
+ mp.release();
Log.v(TAG, "mp currentPositon = " + currentPosition + " play duration = " + (t2-t1));
//The currentposition should be within 10% of the sleep time
//For the very short mp3, it should return the length instead of 10 seconds
@@ -130,11 +130,11 @@
if (currentPosition < 1000 )
return true;
}
- if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0))
+ if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0))
return true;
else
return false;
- }
+ }
public static boolean seekTo(String filePath){
Log.v(TAG, "seekTo " + filePath);
@@ -149,12 +149,12 @@
currentPosition = mp.getCurrentPosition();
}catch (Exception e){
Log.v(TAG, e.getMessage());
- }
+ }
mp.stop();
mp.release();
Log.v(TAG, "CurrentPosition = " + currentPosition);
//The currentposition should be at least greater than the 80% of seek time
- if ((currentPosition > MediaNames.SEEK_TIME *0.8))
+ if ((currentPosition > MediaNames.SEEK_TIME *0.8))
return true;
else
return false;
@@ -170,7 +170,7 @@
try{
mp.setDataSource(filePath);
mp.prepare();
- duration = mp.getDuration();
+ duration = mp.getDuration();
Log.v(TAG, "setLooping duration " + duration);
mp.setLooping(true);
mp.start();
@@ -180,14 +180,14 @@
Thread.sleep(20000);
t2=SystemClock.uptimeMillis();
Log.v(TAG, "pause");
- //Bug# 1106852 - IllegalStateException will be thrown if pause is called
+ //Bug# 1106852 - IllegalStateException will be thrown if pause is called
//in here
//mp.pause();
currentPosition = mp.getCurrentPosition();
Log.v(TAG, "looping position " + currentPosition + "duration = " + (t2-t1));
}catch (Exception e){
Log.v(TAG, "Exception : " + e.toString());
- }
+ }
mp.stop();
mp.release();
//The current position should be within 20% of the sleep time
@@ -196,7 +196,7 @@
return true;
else
return false;
- }
+ }
public static boolean pause(String filePath) throws Exception {
Log.v(TAG, "pause - " + filePath);
@@ -206,7 +206,7 @@
long t2=0;
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(filePath);
- mp.prepare();
+ mp.prepare();
int duration = mp.getDuration();
mp.start();
t1=SystemClock.uptimeMillis();
@@ -244,7 +244,7 @@
mp.pause();
mp.release();
}
-
+
static MediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener =
new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
@@ -258,7 +258,7 @@
//Register the videoSizeChanged listener
public static int videoHeight(String filePath) throws Exception {
Log.v(TAG, "videoHeight - " + filePath);
- int videoHeight = 0;
+ int videoHeight = 0;
synchronized (lock) {
initializeMessageLooper();
try {
@@ -286,7 +286,7 @@
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
-
+
return videoHeight;
}
@@ -321,12 +321,12 @@
terminateMessageLooper();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
- }
+ }
return videoWidth;
}
//This also test the streaming video which may take a long
- //time to start the playback.
+ //time to start the playback.
public static boolean videoSeekTo(String filePath) throws Exception {
Log.v(TAG, "videoSeekTo - " + filePath);
int currentPosition = 0;
@@ -392,12 +392,12 @@
currentPosition = mp.getCurrentPosition();
Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
mp.stop();
- mp.release();
+ mp.release();
Log.v(TAG, "duration = " + duration);
if (currentPosition < 0.9 * duration || isPlaying)
return false;
else
- return true;
+ return true;
}
public static boolean shortMediaStop(String filePath){
@@ -419,12 +419,12 @@
currentPosition = mp.getCurrentPosition();
Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
mp.stop();
- mp.release();
+ mp.release();
Log.v(TAG, "duration = " + duration);
if (currentPosition > duration || isPlaying)
return false;
else
- return true;
+ return true;
}
public static boolean playToEnd(String filePath){
@@ -449,13 +449,13 @@
//updateDuration = mp.getDuration();
Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
mp.stop();
- mp.release();
+ mp.release();
//Log.v(TAG, "duration = " + duration);
//Log.v(TAG, "Update duration = " + updateDuration);
if (currentPosition > duration || isPlaying)
return false;
else
- return true;
+ return true;
}
public static boolean seektoBeforeStart(String filePath){
@@ -478,7 +478,7 @@
if (currentPosition < duration/2)
return false;
else
- return true;
+ return true;
}
public static boolean mediaRecorderRecord(String filePath){
@@ -499,7 +499,7 @@
mRecorder.release();
}catch (Exception e){
Log.v(TAG, e.toString());
- }
+ }
//Verify the recorded file
MediaPlayer mp = new MediaPlayer();
@@ -540,7 +540,7 @@
}
Bitmap outThumbnail = mMediaMetadataRetriever.getFrameAtTime(-1);
- //Verify the thumbnail
+ //Verify the thumbnail
Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
outputWidth = outThumbnail.getWidth();
outputHeight = outThumbnail.getHeight();
@@ -586,8 +586,8 @@
return false;
}
- public static boolean prepareAsyncReset(String filePath){
- //preparesAsync
+ public static boolean prepareAsyncReset(String filePath){
+ //preparesAsync
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(filePath);
@@ -602,9 +602,9 @@
}
- public static boolean isLooping(String filePath) {
+ public static boolean isLooping(String filePath) {
MediaPlayer mp = null;
-
+
try {
mp = new MediaPlayer();
if (mp.isLooping()) {
@@ -619,7 +619,7 @@
Log.v(TAG, "MediaPlayer.isLooping() returned false after setLooping(true)");
return false;
}
-
+
mp.setLooping(false);
if (mp.isLooping()) {
Log.v(TAG, "MediaPlayer.isLooping() returned true after setLooping(false)");
@@ -659,9 +659,9 @@
return true;
}
-
+
/*
- * Initializes the message looper so that the mediaPlayer object can
+ * Initializes the message looper so that the mediaPlayer object can
* receive the callback messages.
*/
private static void initializeMessageLooper() {
@@ -672,10 +672,10 @@
// Set up a looper to be used by camera.
Looper.prepare();
Log.v(TAG, "start loopRun");
- // Save the looper so that we can terminate this thread
+ // Save the looper so that we can terminate this thread
// after we are done with it.
- mLooper = Looper.myLooper();
- mMediaPlayer = new MediaPlayer();
+ mLooper = Looper.myLooper();
+ mMediaPlayer = new MediaPlayer();
synchronized (lock) {
mInitialized = true;
lock.notify();
@@ -685,7 +685,7 @@
}
}.start();
}
-
+
/*
* Terminates the message looper thread.
*/
@@ -693,7 +693,7 @@
mLooper.quit();
mMediaPlayer.release();
}
-
+
static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
synchronized (prepareDone) {
@@ -707,14 +707,14 @@
}
}
};
-
+
public static boolean prepareAsyncCallback(String filePath, boolean reset) throws Exception {
//Added the PrepareReset flag which allow us to switch to different
//test case.
if (reset){
mPrepareReset = true;
}
-
+
synchronized (lock) {
initializeMessageLooper();
try {
@@ -728,18 +728,18 @@
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setDataSource(filePath);
mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
- mMediaPlayer.prepareAsync();
+ mMediaPlayer.prepareAsync();
synchronized (prepareDone) {
try {
prepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
} catch (Exception e) {
Log.v(TAG, "wait was interrupted.");
}
- }
+ }
terminateMessageLooper();
}catch (Exception e){
Log.v(TAG,e.getMessage());
- }
+ }
return onPrepareSuccess;
}
@@ -784,8 +784,12 @@
}
};
- // For each media file, forward twice and backward once, then play to the end
public static boolean playMediaSamples(String filePath) throws Exception {
+ return playMediaSamples(filePath, 2000);
+ }
+
+ // For each media file, forward twice and backward once, then play to the end
+ public static boolean playMediaSamples(String filePath, int buffertime) throws Exception {
int duration = 0;
int curPosition = 0;
int nextPosition = 0;
@@ -822,14 +826,14 @@
waittime = duration - mMediaPlayer.getCurrentPosition();
synchronized(onCompletion){
try {
- onCompletion.wait(waittime + 2000);
+ onCompletion.wait(waittime + buffertime);
}catch (Exception e) {
Log.v(TAG, "playMediaSamples are interrupted");
return false;
}
}
terminateMessageLooper();
- }catch (Exception e) {
+ } catch (Exception e) {
Log.v(TAG, "playMediaSamples:" + e.getMessage());
}
return onCompleteSuccess;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
new file mode 100644
index 0000000..d92c857
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.functional.CodecTest;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Junit / Instrumentation test case for the media player
+ */
+public class MediaPlayerStreamingStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerStreamingStressTest";
+ private String mStreamingSrc;
+
+ public MediaPlayerStreamingStressTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ //Insert a 2 second before launching the test activity. This is
+ //the workaround for the race condition of requesting the updated surface.
+ Thread.sleep(2000);
+ getActivity();
+ MediaPlayerStressTestRunner mRunner = (MediaPlayerStressTestRunner)getInstrumentation();
+ Bundle arguments = mRunner.getArguments();
+ mStreamingSrc = arguments.getString("streaming-source");
+ if (mStreamingSrc == null) {
+ mStreamingSrc = MediaNames.MEDIA_STREAMING_SRC;
+ }
+ super.setUp();
+ }
+
+ private int mTotalPlaybackError = 0;
+ private int mTotalComplete = 0;
+ private int mTotalInfoUnknown = 0;
+ private int mTotalVideoTrackLagging = 0;
+ private int mTotalBadInterleaving = 0;
+ private int mTotalNotSeekable = 0;
+ private int mTotalMetaDataUpdate = 0;
+
+ //Test result output file
+ private static final String PLAYBACK_RESULT = "StreamingTestResult.txt";
+
+ private void writeTestOutput(String filename, Writer output) throws Exception{
+ output.write("URL: " + filename);
+ output.write(" Complete: " + CodecTest.onCompleteSuccess);
+ output.write(" Error: " + CodecTest.mPlaybackError);
+ output.write(" Unknown Info: " + CodecTest.mMediaInfoUnknownCount);
+ output.write(" Track Lagging: " + CodecTest.mMediaInfoVideoTrackLaggingCount);
+ output.write(" Bad Interleaving: " + CodecTest.mMediaInfoBadInterleavingCount);
+ output.write(" Not Seekable: " + CodecTest.mMediaInfoNotSeekableCount);
+ output.write(" Info Meta data update: " + CodecTest.mMediaInfoMetdataUpdateCount);
+ output.write("\n");
+ }
+
+ private void writeTestSummary(Writer output) throws Exception{
+ output.write("Total Result:\n");
+ output.write("Total Complete: " + mTotalComplete + "\n");
+ output.write("Total Error: " + mTotalPlaybackError + "\n");
+ output.write("Total Unknown Info: " + mTotalInfoUnknown + "\n");
+ output.write("Total Track Lagging: " + mTotalVideoTrackLagging + "\n" );
+ output.write("Total Bad Interleaving: " + mTotalBadInterleaving + "\n");
+ output.write("Total Not Seekable: " + mTotalNotSeekable + "\n");
+ output.write("Total Info Meta data update: " + mTotalMetaDataUpdate + "\n");
+ output.write("\n");
+ }
+
+ private void updateTestResult(){
+ if (CodecTest.onCompleteSuccess){
+ mTotalComplete++;
+ }
+ else if (CodecTest.mPlaybackError){
+ mTotalPlaybackError++;
+ }
+ mTotalInfoUnknown += CodecTest.mMediaInfoUnknownCount;
+ mTotalVideoTrackLagging += CodecTest.mMediaInfoVideoTrackLaggingCount;
+ mTotalBadInterleaving += CodecTest.mMediaInfoBadInterleavingCount;
+ mTotalNotSeekable += CodecTest.mMediaInfoNotSeekableCount;
+ mTotalMetaDataUpdate += CodecTest.mMediaInfoMetdataUpdateCount;
+ }
+
+ //Test that will start the playback for all the videos
+ //under the samples folder
+ @LargeTest
+ public void testVideoPlayback() throws Exception {
+ String fileWithError = "Filename:\n";
+ File playbackOutput = new File(Environment.getExternalStorageDirectory(), PLAYBACK_RESULT);
+ Writer output = new BufferedWriter(new FileWriter(playbackOutput, true));
+
+ boolean testResult = true;
+ // load directory files
+ boolean onCompleteSuccess = false;
+
+
+ Log.i(TAG, "Streaming src file: " + mStreamingSrc);
+ //TODO: add try catch
+
+ File f = new File(mStreamingSrc);
+ BufferedReader br = new BufferedReader(new FileReader(f));
+ List<String> urls = new ArrayList<String>();
+ String line;
+ while ((line = br.readLine()) != null) {
+ urls.add(line.trim());
+ }
+ br.close();
+ if (urls == null) {
+ Log.v("MediaPlayerStreamingTest:testVideoPlayback", "no url found");
+ return;
+ } else {
+ for (int i = 0; i < urls.size(); i++) {
+ //Get url
+ String filename = urls.get(i);
+ onCompleteSuccess =
+ CodecTest.playMediaSamples(filename, 60000);
+ if (!onCompleteSuccess){
+ //Don't fail the test right away, print out the failure file.
+ fileWithError += filename + '\n';
+ Log.v(TAG, "Failure File : " + fileWithError);
+ testResult = false;
+ }
+ Thread.sleep(3000);
+ //Write test result to an output file
+ writeTestOutput(filename,output);
+ //Get the summary
+ updateTestResult();
+ }
+ writeTestSummary(output);
+ output.close();
+ assertTrue("testMediaSamples", testResult);
+ }
+ }
+}
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index ee5d42a..761d58a 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -27,6 +27,7 @@
android:id="@+id/static_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:paddingStart="8dip"
android:elevation="@dimen/preview_controls_elevation"
android:background="?android:attr/colorPrimary">
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0629481..c2a0da9 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -55,8 +55,7 @@
android:text="@string/label_copies">
</TextView>
- <view
- class="com.android.printspooler.widget.FirstFocusableEditText"
+ <EditText
android:id="@+id/copies_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
android:singleLine="true"
android:ellipsize="end"
android:inputType="numberDecimal">
- </view>
+ </EditText>
</LinearLayout>
@@ -198,8 +197,7 @@
android:visibility="visible">
</TextView>
- <view
- class="com.android.printspooler.widget.FirstFocusableEditText"
+ <EditText
android:id="@+id/page_range_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -208,7 +206,7 @@
android:ellipsize="end"
android:visibility="visible"
android:inputType="textNoSuggestions">
- </view>
+ </EditText>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 43d8aaf..4381a7a 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -17,8 +17,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
+ android:paddingStart="8dip"
+ android:paddingEnd="8dip"
android:minHeight="56dip"
android:orientation="horizontal"
android:gravity="start|center_vertical">
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 173057b..77c500a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -23,8 +23,6 @@
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:paddingStart="@dimen/printer_list_view_padding_start"
- android:paddingEnd="@dimen/printer_list_view_padding_end"
android:scrollbarStyle="outsideOverlay"
android:cacheColorHint="@android:color/transparent"
android:scrollbarAlwaysDrawVerticalTrack="true" >
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
deleted file mode 100644
index 14403a1..0000000
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:orientation="vertical"
- android:gravity="start|center_vertical">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="?android:attr/spinnerDropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:ellipsize="end"
- android:textIsSelectable="false"
- android:gravity="top|left"
- android:duplicateParentState="true">
- </TextView>
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="?android:attr/spinnerDropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:ellipsize="end"
- android:textIsSelectable="false"
- android:visibility="gone"
- android:duplicateParentState="true">
- </TextView>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index a4666a5..6cf9754b5 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -16,10 +16,6 @@
<resources>
- <dimen name="printer_list_view_padding_start">48dip</dimen>
-
- <dimen name="printer_list_view_padding_end">48dip</dimen>
-
<integer name="preview_page_per_row_count">2</integer>
<integer name="print_option_column_count">3</integer>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index b95703b..b4e4777 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -28,9 +28,6 @@
<dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
- <dimen name="printer_list_view_padding_start">16dip</dimen>
- <dimen name="printer_list_view_padding_end">16dip</dimen>
-
<dimen name="selected_page_elevation">6dip</dimen>
<dimen name="unselected_page_elevation">2dip</dimen>
@@ -46,6 +43,6 @@
<fraction name="page_unselected_alpha">50%</fraction>
<dimen name="preview_page_footer_height">32dip</dimen>
- <dimen name="preview_page_min_width">130dip</dimen>
+ <dimen name="preview_page_min_width">128dip</dimen>
</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index db319e9..532b01f 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,7 +16,10 @@
<resources>
- <style name="PrintActivity" parent="@android:style/Theme.Material.Settings">
+ <style name="PrintActivity" parent="@android:style/Theme.Material">
+ <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+ <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+ <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 5bcdb9f..d949673 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -289,15 +289,11 @@
+ " for position: " + position);
}
- final int pageCount = getItemCount();
MyViewHolder myHolder = (MyViewHolder) holder;
View page = holder.itemView;
- if (pageCount > 1) {
- page.setOnClickListener(mPageClickListener);
- } else {
- page.setOnClickListener(null);
- }
+ page.setOnClickListener(mPageClickListener);
+
page.setTag(holder);
myHolder.mPageInAdapter = position;
@@ -339,16 +335,9 @@
}
content.init(provider, mMediaSize, mMinMargins);
-
View pageSelector = page.findViewById(R.id.page_selector);
pageSelector.setTag(myHolder);
- if (pageCount > 1) {
- pageSelector.setOnClickListener(mPageClickListener);
- pageSelector.setVisibility(View.VISIBLE);
- } else {
- pageSelector.setOnClickListener(null);
- pageSelector.setVisibility(View.GONE);
- }
+ pageSelector.setOnClickListener(mPageClickListener);
if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
pageSelector.setSelected(true);
@@ -449,8 +438,9 @@
final int verticalPadding;
if (mPageContentHeight + mFooterHeight + mPreviewListPadding > availableHeight) {
- verticalPadding = Math.max(mPreviewPageMargin,
- (availableHeight - totalContentHeight) / 2);
+ verticalPadding = Math.max(0,
+ (availableHeight - mPageContentHeight - mFooterHeight) / 2
+ - mPreviewPageMargin);
} else {
verticalPadding = Math.max(mPreviewListPadding,
(availableHeight - totalContentHeight) / 2);
@@ -791,6 +781,9 @@
page.animate().translationZ(mSelectedPageElevation)
.alpha(mSelectedPageAlpha);
} else {
+ if (mConfirmedPagesInDocument.size() <= 1) {
+ return;
+ }
mConfirmedPagesInDocument.remove(pageInDocument);
pageSelector.setSelected(false);
page.animate().translationZ(mUnselectedPageElevation)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index fe17516..01c9746 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -982,21 +982,21 @@
// Media size.
mMediaSizeSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
mMediaSizeSpinner.setOnItemSelectedListener(itemSelectedListener);
// Color mode.
mColorModeSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
// Orientation
mOrientationSpinnerAdapter = new ArrayAdapter<>(
- this, R.layout.spinner_dropdown_item, R.id.title);
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
String[] orientationLabels = getResources().getStringArray(
R.array.orientation_labels);
mOrientationSpinnerAdapter.add(new SpinnerItem<>(
@@ -1008,8 +1008,8 @@
mOrientationSpinner.setOnItemSelectedListener(itemSelectedListener);
// Range options
- ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
- new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
+ ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1075,6 +1075,7 @@
mDestinationSpinner.setEnabled(false);
}
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
@@ -1089,6 +1090,7 @@
// available, we disable all print options except the destination.
if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
@@ -1316,8 +1318,10 @@
// Copies
if (mDestinationSpinnerAdapter.getPdfPrinter() != mCurrentPrinter) {
mCopiesEditText.setEnabled(true);
+ mCopiesEditText.setFocusableInTouchMode(true);
} else {
mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
}
if (mCopiesEditText.getError() == null
&& TextUtils.isEmpty(mCopiesEditText.getText())) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
deleted file mode 100644
index d6bb7c8..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
+++ /dev/null
@@ -1,69 +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.printspooler.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
-public final class FirstFocusableEditText extends EditText {
- private boolean mClickedBeforeFocus;
- private CharSequence mError;
-
- public FirstFocusableEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean performClick() {
- super.performClick();
- if (isFocused() && !mClickedBeforeFocus) {
- clearFocus();
- requestFocus();
- }
- mClickedBeforeFocus = true;
- return true;
- }
-
- @Override
- public CharSequence getError() {
- return mError;
- }
-
- @Override
- public void setError(CharSequence error, Drawable icon) {
- setCompoundDrawables(null, null, icon, null);
- mError = error;
- }
-
- protected void onFocusChanged(boolean gainFocus, int direction,
- Rect previouslyFocusedRect) {
- if (!gainFocus) {
- mClickedBeforeFocus = false;
- }
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
-}
\ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index e428948..c84b06a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -152,6 +152,17 @@
// Make sure we start in a closed options state.
onDragProgress(1.0f);
+
+ // The framework gives focus to the frist focusable and we
+ // do not want that, hence we will take focus instead.
+ setFocusableInTouchMode(true);
+ }
+
+ @Override
+ public void focusableViewAvailable(View v) {
+ // The framework gives focus to the frist focusable and we
+ // do not want that, hence do not announce new focusables.
+ return;
}
@Override
@@ -309,6 +320,7 @@
mSummaryContent.setLayerType(View.LAYER_TYPE_NONE, null);
mDraggableContent.setLayerType(View.LAYER_TYPE_NONE, null);
mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
}
mDragProgress = progress;
@@ -320,7 +332,6 @@
mMoreOptionsButton.setAlpha(inverseAlpha);
mEmbeddedContentScrim.setBackgroundColor(computeScrimColor());
-
if (progress == 0) {
if (mOptionsStateChangeListener != null) {
mOptionsStateChangeListener.onOptionsOpened();
@@ -354,14 +365,15 @@
}
private void ensureImeClosedAndInputFocusCleared() {
- View focus = findFocus();
- if (focus != null) {
+ View focused = findFocus();
+
+ if (focused != null && focused.isFocused()) {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
Context.INPUT_METHOD_SERVICE);
- if (imm.isActive(focus)) {
+ if (imm.isActive(focused)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
- focus.clearFocus();
+ focused.clearFocus();
}
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 299e50c..a3bed4f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,6 +187,9 @@
<!-- Default for Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1==on -->
<integer name="def_lock_screen_show_notifications">1</integer>
+ <!-- Default for Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS -->
+ <bool name="def_lock_screen_allow_private_notifications">true</bool>
+
<!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on -->
<integer name="def_heads_up_enabled">1</integer>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index edefb13..fd5e6fe 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 @@
// 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 = 108;
+ private static final int DATABASE_VERSION = 109;
private Context mContext;
private int mUserHandle;
@@ -1673,8 +1673,8 @@
try {
stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- R.bool.def_guest_user_enabled);
+ loadIntegerSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ R.integer.def_lock_screen_show_notifications);
if (mUserHandle == UserHandle.USER_OWNER) {
final int oldShow = getIntValueFromTable(db,
TABLE_GLOBAL, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, -1);
@@ -1733,6 +1733,22 @@
upgradeVersion = 108;
}
+ if (upgradeVersion < 109) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ R.bool.def_lock_screen_allow_private_notifications);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 109;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -2301,6 +2317,9 @@
loadIntegerSetting(stmt, Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
R.integer.def_lock_screen_show_notifications);
+ loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ R.bool.def_lock_screen_allow_private_notifications);
+
} finally {
if (stmt != null) stmt.close();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 87c015c..4ef2189 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -318,10 +318,10 @@
}
}
- private void checkUserRestrictions(String setting) {
+ private void checkUserRestrictions(String setting, int userId) {
String userRestriction = sRestrictedKeys.get(setting);
if (!TextUtils.isEmpty(userRestriction)
- && mUserManager.hasUserRestriction(userRestriction)) {
+ && mUserManager.hasUserRestriction(userRestriction, new UserHandle(userId))) {
throw new SecurityException(
"Permission denial: user is restricted from changing this setting.");
}
@@ -936,7 +936,7 @@
try {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
- checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME));
+ checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME), callingUser);
if (db.insert(args.table, null, values[i]) < 0) return 0;
SettingsCache.populate(cache, values[i]);
if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
@@ -1067,7 +1067,7 @@
// Check write permissions only after determining which table the insert will touch
checkWritePermissions(args);
- checkUserRestrictions(name);
+ checkUserRestrictions(name, desiredUserHandle);
// The global table is stored under the owner, always
if (TABLE_GLOBAL.equals(args.table)) {
@@ -1143,7 +1143,7 @@
callingUser = UserHandle.USER_OWNER;
}
checkWritePermissions(args);
- checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME));
+ checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME), callingUser);
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
mutationCount.incrementAndGet();
diff --git a/packages/SystemUI/res/drawable/ic_android.xml b/packages/SystemUI/res/drawable/ic_android.xml
new file mode 100644
index 0000000..19ee9a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_android.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M6.000000,18.000000c0.000000,0.600000 0.400000,1.000000 1.000000,1.000000l1.000000,0.000000l0.000000,3.500000C8.000000,23.299999 8.700000,24.000000 9.500000,24.000000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L11.000000,19.000000l2.000000,0.000000l0.000000,3.500000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L16.000000,19.000000l1.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L18.000000,8.000000L6.000000,8.000000L6.000000,18.000000zM3.500000,8.000000C2.700000,8.000000 2.000000,8.700000 2.000000,9.500000l0.000000,7.000000C2.000000,17.299999 2.700000,18.000000 3.500000,18.000000C4.300000,18.000000 5.000000,17.299999 5.000000,16.500000l0.000000,-7.000000C5.000000,8.700000 4.300000,8.000000 3.500000,8.000000zM20.500000,8.000000C19.700001,8.000000 19.000000,8.700000 19.000000,9.500000l0.000000,7.000000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000l0.000000,-7.000000C22.000000,8.700000 21.299999,8.000000 20.500000,8.000000zM15.500000,2.200000l1.300000,-1.300000c0.200000,-0.200000 0.200000,-0.500000 0.000000,-0.700000c-0.200000,-0.200000 -0.500000,-0.200000 -0.700000,0.000000l-1.500000,1.500000C13.900000,1.200000 13.000000,1.000000 12.000000,1.000000c-1.000000,0.000000 -1.900000,0.200000 -2.700000,0.600000L7.900000,0.100000C7.700000,0.000000 7.300000,0.000000 7.100000,0.100000C7.000000,0.300000 7.000000,0.700000 7.100000,0.900000l1.300000,1.300000C7.000000,3.300000 6.000000,5.000000 6.000000,7.000000l12.000000,0.000000C18.000000,5.000000 17.000000,3.200000 15.500000,2.200000zM10.000000,5.000000L9.000000,5.000000L9.000000,4.000000l1.000000,0.000000L10.000000,5.000000zM15.000000,5.000000l-1.000000,0.000000L14.000000,4.000000l1.000000,0.000000L15.000000,5.000000z"
+ android:fillColor="#ffffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_close.xml b/packages/SystemUI/res/drawable/ic_close.xml
new file mode 100644
index 0000000..7d93d45
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index ef0c9bb..ca07c87 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,6 @@
android:src="@drawable/ic_lock_24dp"
android:scaleType="center"
android:tint="#ffffffff"
- android:contentDescription="@string/accessibility_unlock_button_not_secured" />
+ android:contentDescription="@string/accessibility_unlock_button" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index d65a23e..566c304 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -53,7 +53,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_weight="1"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
android:textColor="@color/notification_guts_title_color"
/>
<DateTimeView
@@ -62,7 +62,7 @@
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center_vertical|start"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Material.EventContent.Time"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Time"
android:textColor="@color/notification_guts_text_color"
/>
<TextView
@@ -70,7 +70,7 @@
android:layout_height="wrap_content"
android:id="@+id/debug_info"
android:layout_weight="0"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Material.EventContent.Time"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Time"
android:layout_gravity="bottom|start"
android:visibility="gone"
android:textColor="@color/notification_guts_text_color"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index eff3758..351177b 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -38,8 +38,8 @@
android:id="@+id/more_text"
android:layout_width="32dp"
android:layout_height="32dp"
- android:layout_marginStart="20dp"
- android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="12dp"
android:layout_gravity="center_vertical"
android:background="@drawable/keyguard_overflow_number_background"
android:gravity="center"
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 3cd5f67..dd158c2 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -58,6 +58,9 @@
<!-- Size of fading edge for scrolling -->
<dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
+ <!-- The radius of the rounded corners on a task view. -->
+ <dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
+
<!-- Where to place the app icon over the thumbnail -->
<dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
<dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b000a48..b488c56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,6 +213,8 @@
<string name="accessibility_camera_button">Camera</string>
<!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_phone_button">Phone</string>
+ <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_unlock_button">Unlock</string>
<!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
<string name="unlock_label">unlock</string>
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
@@ -220,17 +222,6 @@
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
<string name="camera_label">open camera</string>
- <!-- Content description of the lock icon when device is secured (lock closed) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_secured">Device secured.</string>
- <!-- Content description of the lock icon when device is not secured (lock open) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_not_secured">Device not secured.</string>
- <!-- Content description of the lock icon when device is secured (lock closed) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_secured_trust_managed">Device secured, trust agent active.</string>
- <!-- Content description of the lock icon when device is not secured (lock open) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_not_secured_trust_managed">Device not secured, trust agent active.</string>
- <!-- Content description of the lock icon when face unlock is running (face icon) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button_face_unlock_running">Face detection running, trust agent active.</string>
-
<!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ime_switch_button">Switch input method button.</string>
<!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -323,6 +314,15 @@
<!-- Content description of an item with full signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_signal_full">Signal full.</string>
+ <!-- Content description of an item that is turned on for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_on">On.</string>
+ <!-- Content description of an item that is turned off for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_off">Off.</string>
+ <!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_connected">Connected.</string>
+ <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_connecting">Connecting.</string>
+
<!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_data_connection_gprs">GPRS</string>
@@ -894,6 +894,18 @@
<!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] -->
<string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string>
+ <!-- Title of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=40] -->
+ <string name="hidden_notifications_title">Get notifications faster</string>
+
+ <!-- Body of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=60] -->
+ <string name="hidden_notifications_text">See them before you unlock</string>
+
+ <!-- Cancel action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+ <string name="hidden_notifications_cancel">No thanks</string>
+
+ <!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+ <string name="hidden_notifications_setup">Set up</string>
+
<!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
<string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d236c7e..efd4fb4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -234,8 +234,8 @@
<item name="android:windowExitAnimation">@*android:anim/popup_exit_material</item>
</style>
- <style name="TextAppearance.StatusBar.Material.EventContent.Parenthetical"
- parent="@*android:style/TextAppearance.StatusBar.Material.EventContent">
+ <style name="TextAppearance.Material.Notification.Parenthetical"
+ parent="@*android:style/TextAppearance.Material.Notification">
<item name="android:textStyle">italic</item>
<item name="android:textColor">#60000000</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 7ac6644..46d8a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -54,12 +54,7 @@
@Override
protected void handleClick() {
final boolean wasEnabled = (Boolean) mState.value;
- final boolean changed = mController.setLocationEnabled(!wasEnabled);
- if (!wasEnabled && changed) {
- // If we've successfully switched from location off to on, close the
- // notifications tray to show the network location provider consent dialog.
- mHost.collapsePanels();
- }
+ mController.setLocationEnabled(!wasEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index d328660..ec39d77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -72,6 +72,7 @@
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
Context mContext;
+ LayoutInflater mInflater;
SystemServicesProxy mSystemServicesProxy;
Handler mHandler;
boolean mBootCompleted;
@@ -98,32 +99,20 @@
public AlternateRecentsComponent(Context context) {
RecentsTaskLoader.initialize(context);
- Resources res = context.getResources();
+ mInflater = LayoutInflater.from(context);
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
mHandler = new Handler();
- mConfig = RecentsConfiguration.reinitialize(context, mSystemServicesProxy);
- mWindowRect = mSystemServicesProxy.getWindowRect();
mTaskStackBounds = new Rect();
- mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
- mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
- mNavBarWidth, mTaskStackBounds);
- if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
- mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
- } else {
- mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
- }
}
- public void onStart() {
- // Initialize some static datastructures
- TaskStackViewLayoutAlgorithm.initializeCurve();
- reloadHeaderBarLayout();
- }
+ public void onStart() {}
public void onBootCompleted() {
+ // Initialize some static datastructures
+ TaskStackViewLayoutAlgorithm.initializeCurve();
+ // Load the header bar layout
+ reloadHeaderBarLayout();
mBootCompleted = true;
}
@@ -235,9 +224,19 @@
}
public void onConfigurationChanged(Configuration newConfig) {
+ reloadHeaderBarLayout();
+ sLastScreenshot = null;
+ }
+
+ /** Prepares the header bar layout. */
+ void reloadHeaderBarLayout() {
+ Resources res = mContext.getResources();
+ mWindowRect = mSystemServicesProxy.getWindowRect();
+ mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+ mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
mConfig.updateOnConfigurationChange();
- mWindowRect = mSystemServicesProxy.getWindowRect();
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
mNavBarWidth, mTaskStackBounds);
if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
@@ -245,14 +244,8 @@
} else {
mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
}
- sLastScreenshot = null;
- reloadHeaderBarLayout();
- }
- /** Prepares the header bar layout. */
- void reloadHeaderBarLayout() {
// Inflate the header bar layout so that we can rebind and draw it for the transition
- Resources res = mContext.getResources();
TaskStack stack = new TaskStack();
mDummyStackView = new TaskStackView(mContext, stack);
TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
@@ -261,8 +254,7 @@
algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
Rect taskViewSize = algo.getUntransformedTaskViewSize();
int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null,
+ mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
false);
mHeaderBar.measure(
View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
@@ -419,10 +411,6 @@
return null;
}
- // Get the stack
- mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
- mDummyStackView.getScroller().setStackScrollToInitialState();
-
// Find the running task in the TaskStack
Task task = null;
ArrayList<Task> tasks = stack.getTasks();
@@ -444,6 +432,8 @@
}
// Get the transform for the running task
+ mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+ mDummyStackView.getScroller().setStackScrollToInitialState();
mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task,
mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index a93e244..f7ad35b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
@@ -28,6 +27,8 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UserHandle;
+import android.util.Log;
+
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -232,6 +233,8 @@
/* Recents task loader
* NOTE: We should not hold any references to a Context from a static instance */
public class RecentsTaskLoader {
+ private static final String TAG = "RecentsTaskLoader";
+
static RecentsTaskLoader sInstance;
SystemServicesProxy mSystemServicesProxy;
@@ -335,11 +338,15 @@
infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
taskKey.userId);
}
- icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
- mApplicationIconCache.put(taskKey, icon);
- return icon;
+ if (infoHandle.info != null) {
+ icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
+ if (icon != null) {
+ mApplicationIconCache.put(taskKey, icon);
+ return icon;
+ }
+ }
}
- // If we are not preloading, return the default icon to show
+ // If we couldn't load any icon, return null
return null;
}
@@ -361,8 +368,13 @@
infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
taskKey.userId);
}
- label = ssp.getActivityLabel(infoHandle.info);
- mActivityLabelCache.put(taskKey, label);
+ if (infoHandle.info != null) {
+ label = ssp.getActivityLabel(infoHandle.info);
+ mActivityLabelCache.put(taskKey, label);
+ } else {
+ Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
+ + " u=" + taskKey.userId);
+ }
return label;
}
@@ -401,8 +413,8 @@
List<Task> tasksToLoadOut) {
RecentsConfiguration config = RecentsConfiguration.getInstance();
List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
- HashMap<ComponentName, ActivityInfoHandle> activityInfoCache =
- new HashMap<ComponentName, ActivityInfoHandle>();
+ HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
+ new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
ArrayList<Task> tasksToAdd = new ArrayList<Task>();
TaskStack stack = new TaskStack();
@@ -410,19 +422,21 @@
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = tasks.get(i);
- // Get an existing activity info handle if possible
- ComponentName cn = t.baseIntent.getComponent();
- ActivityInfoHandle infoHandle = new ActivityInfoHandle();
- boolean hasCachedActivityInfo = false;
- if (activityInfoCache.containsKey(cn)) {
- infoHandle = activityInfoCache.get(cn);
- hasCachedActivityInfo = true;
- }
-
// Compose the task key
Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
t.firstActiveTime, t.lastActiveTime);
+ // Get an existing activity info handle if possible
+ Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+ ActivityInfoHandle infoHandle;
+ boolean hasCachedActivityInfo = false;
+ if (activityInfoCache.containsKey(cnKey)) {
+ infoHandle = activityInfoCache.get(cnKey);
+ hasCachedActivityInfo = true;
+ } else {
+ infoHandle = new ActivityInfoHandle();
+ }
+
// Determine whether to preload this task
boolean preloadTask = false;
if (preloadTaskId > 0) {
@@ -440,7 +454,7 @@
// Update the activity info cache
if (!hasCachedActivityInfo && infoHandle.info != null) {
- activityInfoCache.put(cn, infoHandle);
+ activityInfoCache.put(cnKey, infoHandle);
}
// Add the task to the stack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 977db60..406e03f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,12 +16,15 @@
package com.android.systemui.recents.model;
+import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import com.android.systemui.recents.misc.Utilities;
+import java.util.Objects;
+
/**
* A task represents the top most task in the system's task stack.
@@ -35,8 +38,35 @@
public void onTaskDataUnloaded();
}
+ /** The ComponentNameKey represents the unique primary key for a component
+ * belonging to a specified user. */
+ public static class ComponentNameKey {
+ final ComponentName component;
+ final int userId;
+
+ public ComponentNameKey(ComponentName cn, int user) {
+ component = cn;
+ userId = user;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(component, userId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ComponentNameKey)) {
+ return false;
+ }
+ return component.equals(((ComponentNameKey) o).component) &&
+ userId == ((ComponentNameKey) o).userId;
+ }
+ }
+
/* The Task Key represents the unique primary key for the task */
public static class TaskKey {
+ final ComponentNameKey mComponentNameKey;
public final int id;
public final Intent baseIntent;
public final int userId;
@@ -44,6 +74,7 @@
public long lastActiveTime;
public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
+ mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
this.id = id;
this.baseIntent = intent;
this.userId = userId;
@@ -51,6 +82,11 @@
this.lastActiveTime = lastActiveTime;
}
+ /** Returns the component name key for this task. */
+ public ComponentNameKey getComponentNameKey() {
+ return mComponentNameKey;
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof TaskKey)) {
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 1ac3bc3..0c6e7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -67,6 +67,7 @@
ArrayList<TaskStack> mStacks;
View mSearchBar;
RecentsViewCallbacks mCb;
+ boolean mAlreadyLaunchingTask;
public RecentsView(Context context) {
super(context);
@@ -120,6 +121,9 @@
}
addView(stackView);
}
+
+ // Reset the launched state
+ mAlreadyLaunchingTask = false;
}
/** Removes all the task stack views from this recents view. */
@@ -381,6 +385,11 @@
if (mCb != null) {
mCb.onTaskViewClicked();
}
+ // Skip if we are already launching tasks
+ if (mAlreadyLaunchingTask) {
+ return;
+ }
+ mAlreadyLaunchingTask = true;
// Upfront the processing of the thumbnail
TaskViewTransform transform = new TaskViewTransform();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 35ed3e5..f04c8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.app.admin.DevicePolicyManager;
@@ -35,7 +36,11 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
@@ -78,6 +83,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.util.NotificationColorUtil;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
@@ -126,6 +132,12 @@
public static final int EXPANDED_LEAVE_ALONE = -10000;
public static final int EXPANDED_FULL_OPEN = -10001;
+ private static final int HIDDEN_NOTIFICATION_ID = 10000;
+ private static final String BANNER_ACTION_CANCEL =
+ "com.android.systemui.statusbar.banner_action_cancel";
+ private static final String BANNER_ACTION_SETUP =
+ "com.android.systemui.statusbar.banner_action_setup";
+
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
@@ -308,6 +320,20 @@
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
updateNotifications();
+ } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+ NotificationManager noMan = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.cancel(HIDDEN_NOTIFICATION_ID);
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ if (BANNER_ACTION_SETUP.equals(action)) {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+ mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ );
+ }
}
}
};
@@ -490,12 +516,61 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(BANNER_ACTION_CANCEL);
+ filter.addAction(BANNER_ACTION_SETUP);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
updateCurrentProfilesCache();
}
+ protected void notifyUserAboutHiddenNotifications() {
+ if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
+ Log.d(TAG, "user hasn't seen notification about hidden notifications");
+ final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ if (!lockPatternUtils.isSecure()) {
+ Log.d(TAG, "insecure lockscreen, skipping notification");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ return;
+ }
+ Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+ // disable lockscreen notifications until user acts on the banner.
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+
+ final String packageName = mContext.getPackageName();
+ PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ final Resources res = mContext.getResources();
+ final int colorRes = com.android.internal.R.color.system_notification_accent_color;
+ Notification.Builder note = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_android)
+ .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
+ .setContentText(mContext.getString(R.string.hidden_notifications_text))
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setOngoing(true)
+ .setColor(res.getColor(colorRes))
+ .setContentIntent(setupIntent)
+ .addAction(R.drawable.ic_close,
+ mContext.getString(R.string.hidden_notifications_cancel),
+ cancelIntent)
+ .addAction(R.drawable.ic_settings,
+ mContext.getString(R.string.hidden_notifications_setup),
+ setupIntent);
+
+ NotificationManager noMan =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
+ }
+ }
+
public void userSwitched(int newUserId) {
// should be overridden
}
@@ -582,9 +657,11 @@
protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
NotificationData.Entry entry) {
+ PackageManager pmUser = getPackageManagerForUser(
+ entry.notification.getUser().getIdentifier());
int version = 0;
try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
+ ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
version = info.targetSdkVersion;
} catch (NameNotFoundException ex) {
Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
@@ -1232,7 +1309,7 @@
if (text != null) {
text.setText(R.string.notification_hidden_text);
text.setTextAppearance(mContext,
- R.style.TextAppearance_StatusBar_Material_EventContent_Parenthetical);
+ R.style.TextAppearance_Material_Notification_Parenthetical);
}
int topPadding = Notification.Builder.calculateTopPadding(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index fb13126..9da209a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -45,6 +45,7 @@
private boolean mIsAirplaneMode = false;
private int mAirplaneIconId = 0;
private String mWifiDescription, mMobileDescription, mMobileTypeDescription;
+ private boolean mRoaming;
ViewGroup mWifiGroup, mMobileGroup;
ImageView mWifi, mMobile, mMobileType, mAirplane;
@@ -106,12 +107,13 @@
@Override
public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
- String contentDescription, String typeContentDescription) {
+ String contentDescription, String typeContentDescription, boolean roaming) {
mMobileVisible = visible;
mMobileStrengthId = strengthIcon;
mMobileTypeId = typeIcon;
mMobileDescription = contentDescription;
mMobileTypeDescription = typeContentDescription;
+ mRoaming = roaming;
apply();
}
@@ -207,8 +209,7 @@
(mMobileVisible ? "VISIBLE" : "GONE"),
mMobileStrengthId, mMobileTypeId));
- mMobileType.setVisibility(
- !mWifiVisible ? View.VISIBLE : View.GONE);
+ mMobileType.setVisibility(!mWifiVisible || mRoaming ? View.VISIBLE : View.GONE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 62552b2..f9da30f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -380,21 +380,10 @@
mLockIcon.setImageResource(iconRes);
boolean trustManaged = mUnlockMethodCache.isTrustManaged();
mTrustDrawable.setTrustManaged(trustManaged);
-
updateLockIconClickability();
- updateLockIconContentDescription(mUnlockMethodCache.isFaceUnlockRunning(),
- mUnlockMethodCache.isMethodInsecure(), trustManaged);
}
- private void updateLockIconContentDescription(boolean faceUnlockRunning, boolean insecure,
- boolean trustManaged) {
- mLockIcon.setContentDescription(getResources().getString(
- faceUnlockRunning ? R.string.accessibility_unlock_button_face_unlock_running
- : insecure && !trustManaged ? R.string.accessibility_unlock_button_not_secured
- : insecure ? R.string.accessibility_unlock_button_not_secured_trust_managed
- : !trustManaged ? R.string.accessibility_unlock_button_secured
- : R.string.accessibility_unlock_button_secured_trust_managed));
- }
+
public KeyguardAffordanceView getPhoneView() {
return mPhoneImageView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9fd3d9c..1a0d2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -582,6 +582,8 @@
putComponent(PhoneStatusBar.class, this);
setControllerUsers();
+
+ notifyUserAboutHiddenNotifications();
}
// ================================================================================
@@ -3612,7 +3614,8 @@
}
public boolean onSpacePressed() {
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ if (mScreenOn != null && mScreenOn
+ && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
animateCollapsePanels(0 /* flags */, true /* force */);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 15a7047..9b59814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -64,6 +64,7 @@
static final boolean CHATTY = false; // additional diagnostics, but not logspew
private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode;
+ private static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
// telephony
boolean mHspaDataDistinguishable;
@@ -164,7 +165,7 @@
public interface SignalCluster {
void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
- String contentDescription, String typeContentDescription);
+ String contentDescription, String typeContentDescription, boolean roaming);
void setIsAirplaneMode(boolean is, int airplaneIcon);
}
@@ -372,7 +373,8 @@
mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
mDataTypeIconId,
mContentDescriptionWimax,
- mContentDescriptionDataType);
+ mContentDescriptionDataType,
+ mDataTypeIconId == ROAMING_ICON);
} else {
// normal mobile data
cluster.setMobileDataIndicators(
@@ -380,7 +382,8 @@
mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
mDataTypeIconId,
mContentDescriptionPhoneSignal,
- mContentDescriptionDataType);
+ mContentDescriptionDataType,
+ mDataTypeIconId == ROAMING_ICON);
}
cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId);
}
@@ -777,11 +780,11 @@
if (isCdma()) {
if (isCdmaEri()) {
- mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+ mDataTypeIconId = ROAMING_ICON;
mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
}
} else if (mPhone.isNetworkRoaming()) {
- mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+ mDataTypeIconId = ROAMING_ICON;
mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
}
}
@@ -1195,11 +1198,11 @@
mQSDataTypeIconId = 0;
if (isCdma()) {
if (isCdmaEri()) {
- mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+ mDataTypeIconId = ROAMING_ICON;
mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
}
} else if (mPhone.isNetworkRoaming()) {
- mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+ mDataTypeIconId = ROAMING_ICON;
mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
}
}
@@ -1544,8 +1547,7 @@
datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g :
datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h :
datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte :
- datatype.equals("roam")
- ? R.drawable.stat_sys_data_fully_connected_roam :
+ datatype.equals("roam") ? ROAMING_ICON :
0;
mDemoQSDataTypeIconId =
datatype.equals("1x") ? R.drawable.ic_qs_signal_1x :
@@ -1572,7 +1574,8 @@
iconId,
mDemoDataTypeIconId,
"Demo",
- "Demo");
+ "Demo",
+ mDemoDataTypeIconId == ROAMING_ICON);
}
refreshViews();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index f03c5eb..12c887e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,7 +45,6 @@
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
-import android.provider.Settings.Global;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -58,6 +56,7 @@
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -97,6 +96,7 @@
private static final int TIMEOUT_DELAY_SHORT = 1500;
private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
+ private static final int TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK = 25000;
private static final int TIMEOUT_DELAY_EXPANDED = 10000;
private static final int MSG_VOLUME_CHANGED = 0;
@@ -161,6 +161,7 @@
private int mActiveStreamType = -1;
/** All the slider controls mapped by stream type */
private SparseArray<StreamControl> mStreamControls;
+ private final AccessibilityManager mAccessibilityManager;
private enum StreamResources {
BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
@@ -332,6 +333,8 @@
mContext = context;
mZenController = zenController;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
// For now, only show master volume if master volume is supported
final Resources res = context.getResources();
@@ -791,7 +794,8 @@
}
private void updateTimeoutDelay() {
- mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
+ mTimeoutDelay = sSafetyWarning != null ? mAccessibilityManager.isEnabled() ?
+ TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK : TIMEOUT_DELAY_SAFETY_WARNING
: mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
: mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
: isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
@@ -1214,6 +1218,7 @@
}
updateStates();
}
+ updateTimeoutDelay();
resetTimeout();
}
diff --git a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
index 71b0d53..6f79f58 100644
--- a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
+++ b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
@@ -83,6 +83,7 @@
private final Context mContext;
+ private final Runnable mOnAccessibilityEnabledCallback;
private final UserManager mUserManager;
private final TextToSpeech mTts;
private final Ringtone mTone;
@@ -97,8 +98,9 @@
private float mSecondPointerDownX;
private float mSecondPointerDownY;
- public EnableAccessibilityController(Context context) {
+ public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) {
mContext = context;
+ mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
@@ -275,5 +277,7 @@
/* ignore */
}
}
+
+ mOnAccessibilityEnabledCallback.run();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index ae94654..41695c1 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -1073,7 +1073,13 @@
// is dismissed on the first down while the global gesture is a long press
// with two fingers anywhere on the screen.
if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
- mEnableAccessibilityController = new EnableAccessibilityController(mContext);
+ mEnableAccessibilityController = new EnableAccessibilityController(mContext,
+ new Runnable() {
+ @Override
+ public void run() {
+ dismiss();
+ }
+ });
super.setCanceledOnTouchOutside(false);
} else {
mEnableAccessibilityController = null;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index a13da609..93591a9 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2754,11 +2754,13 @@
mStatusColorView = updateColorViewInt(mStatusColorView,
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
mStatusBarColor, mLastTopInset, Gravity.TOP,
- STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+ STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.statusBarBackground);
mNavigationColorView = updateColorViewInt(mNavigationColorView,
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
- NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
+ NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.navigationBarBackground);
}
if (insets != null) {
insets = insets.consumeStableInsets();
@@ -2767,7 +2769,7 @@
}
private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
- int color, int height, int verticalGravity, String transitionName) {
+ int color, int height, int verticalGravity, String transitionName, int id) {
boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
&& (getAttributes().flags & translucentFlag) == 0
&& (color & Color.BLACK) != 0
@@ -2778,6 +2780,7 @@
view = new View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(transitionName);
+ view.setId(id);
addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
Gravity.START | verticalGravity));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index ac0ca0a..af5c13d 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -708,6 +708,7 @@
// Send an event to the end of the drag gesture.
sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
+ mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8bb094b..0919f77 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -185,7 +185,7 @@
private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
- private static final boolean VDBG = true; // STOPSHIP
+ private static final boolean VDBG = false;
// network sampling debugging
private static final boolean SAMPLE_DBG = false;
@@ -830,11 +830,11 @@
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (VDBG) log("returning Blocked NetworkInfo");
+ if (DBG) log("returning Blocked NetworkInfo");
}
if (mLockdownTracker != null) {
info = mLockdownTracker.augmentNetworkInfo(info);
- if (VDBG) log("returning Locked NetworkInfo");
+ if (DBG) log("returning Locked NetworkInfo");
}
return info;
}
@@ -1202,7 +1202,7 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- if (VDBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
+ if (DBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
try {
mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
@@ -1401,7 +1401,7 @@
mInitialBroadcast = new Intent(intent);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (VDBG) {
+ if (DBG) {
log("sendStickyBroadcast: action=" + intent.getAction());
}
@@ -1558,7 +1558,7 @@
}
try {
- if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
+ if (DBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.setMtu(iface, mtu);
} catch (Exception e) {
Slog.e(TAG, "exception in setMtu()" + e);
@@ -1579,7 +1579,7 @@
}
if (values == null || values.length != 6) {
- if (VDBG) log("Invalid tcpBufferSizes string: " + tcpBufferSizes +", using defaults");
+ if (DBG) log("Invalid tcpBufferSizes string: " + tcpBufferSizes +", using defaults");
tcpBufferSizes = DEFAULT_TCP_BUFFER_SIZES;
values = tcpBufferSizes.split(",");
}
@@ -1587,7 +1587,7 @@
if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
try {
- if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+ if (DBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
final String prefix = "/sys/kernel/ipv4/tcp_";
FileUtils.stringToFile(prefix + "rmem_min", values[0]);
@@ -1750,7 +1750,7 @@
}
if (officialNai != null && officialNai.equals(nai)) return true;
if (officialNai != null || VDBG) {
- loge(msg + " - validateNetworkAgent found mismatched netId: " + officialNai +
+ loge(msg + " - isLiveNetworkAgent found mismatched netId: " + officialNai +
" - " + nai);
}
return false;
@@ -1794,7 +1794,7 @@
loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
} else {
if (VDBG) {
- log("Update of Linkproperties for " + nai.name() +
+ log("Update of LinkProperties for " + nai.name() +
"; created=" + nai.created);
}
LinkProperties oldLp = nai.linkProperties;
@@ -2073,11 +2073,10 @@
for (int i = 0; i < nai.networkRequests.size(); i++) {
NetworkRequest request = nai.networkRequests.valueAt(i);
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
- if (VDBG) {
- log(" checking request " + request + ", currentNetwork = " +
- (currentNetwork != null ? currentNetwork.name() : "null"));
- }
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
+ if (DBG) {
+ log("Checking for replacement network to handle request " + request );
+ }
mNetworkForRequestId.remove(request.requestId);
sendUpdatedScoreToFactories(request, 0);
NetworkAgentInfo alternative = null;
@@ -2091,8 +2090,11 @@
alternative = existing;
}
}
- if (alternative != null && !toActivate.contains(alternative)) {
- toActivate.add(alternative);
+ if (alternative != null) {
+ if (DBG) log(" found replacement in " + alternative.name());
+ if (!toActivate.contains(alternative)) {
+ toActivate.add(alternative);
+ }
}
}
}
@@ -2115,9 +2117,9 @@
// Check for the best currently alive network that satisfies this request
NetworkAgentInfo bestNetwork = null;
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
- if (VDBG) log("handleRegisterNetworkRequest checking " + network.name());
+ if (DBG) log("handleRegisterNetworkRequest checking " + network.name());
if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
- if (VDBG) log("apparently satisfied. currentScore=" + network.currentScore);
+ if (DBG) log("apparently satisfied. currentScore=" + network.currentScore);
if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
if (!nri.isRequest) {
// Not setting bestNetwork here as a listening NetworkRequest may be
@@ -2132,7 +2134,7 @@
}
}
if (bestNetwork != null) {
- if (VDBG) log("using " + bestNetwork.name());
+ if (DBG) log("using " + bestNetwork.name());
if (bestNetwork.networkInfo.isConnected()) {
// Cancel any lingering so the linger timeout doesn't teardown this network
// even though we have a request for it.
@@ -2173,7 +2175,7 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (nai.networkRequests.get(nri.request.requestId) != null) {
nai.networkRequests.remove(nri.request.requestId);
- if (VDBG) {
+ if (DBG) {
log(" Removing from current network " + nai.name() +
", leaving " + nai.networkRequests.size() +
" requests.");
@@ -3557,7 +3559,7 @@
// Check for apps that can handle provisioning first
Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
List<String> carrierPackages =
- mTelephonyManager.getCarrierPackageNamesForBroadcastIntent(provisioningIntent);
+ mTelephonyManager.getCarrierPackageNamesForIntent(provisioningIntent);
if (carrierPackages != null && !carrierPackages.isEmpty()) {
if (carrierPackages.size() != 1) {
if (DBG) log("Multiple matching carrier apps found, launching the first.");
@@ -3920,7 +3922,7 @@
private void handleNetworkSamplingTimeout() {
- log("Sampling interval elapsed, updating statistics ..");
+ if (SAMPLE_DBG) log("Sampling interval elapsed, updating statistics ..");
// initialize list of interfaces ..
Map<String, SamplingDataTracker.SamplingSnapshot> mapIfaceToSample =
@@ -3951,13 +3953,15 @@
}
}
- log("Done.");
+ if (SAMPLE_DBG) log("Done.");
int samplingIntervalInSeconds = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
DEFAULT_SAMPLING_INTERVAL_IN_SECONDS);
- if (DBG) log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds");
+ if (SAMPLE_DBG) {
+ log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds");
+ }
setAlarm(samplingIntervalInSeconds * 1000, mSampleIntervalElapsedIntent);
}
@@ -4125,7 +4129,7 @@
}
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
- if (VDBG) log("Got NetworkFactory Messenger for " + nfi.name);
+ if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
mNetworkFactoryInfos.put(nfi.messenger, nfi);
nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
}
@@ -4139,10 +4143,10 @@
private void handleUnregisterNetworkFactory(Messenger messenger) {
NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
if (nfi == null) {
- if (VDBG) log("Failed to find Messenger in unregisterNetworkFactory");
+ loge("Failed to find Messenger in unregisterNetworkFactory");
return;
}
- if (VDBG) log("unregisterNetworkFactory for " + nfi.name);
+ if (DBG) log("unregisterNetworkFactory for " + nfi.name);
}
/**
@@ -4180,7 +4184,7 @@
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
- if (VDBG) log("registerNetworkAgent " + nai);
+ if (DBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
}
@@ -4241,6 +4245,7 @@
}
for (String iface : interfaceDiff.added) {
try {
+ if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.addInterfaceToNetwork(iface, netId);
} catch (Exception e) {
loge("Exception adding interface: " + e);
@@ -4248,6 +4253,7 @@
}
for (String iface : interfaceDiff.removed) {
try {
+ if (DBG) log("Removing iface " + iface + " from network " + netId);
mNetd.removeInterfaceFromNetwork(iface, netId);
} catch (Exception e) {
loge("Exception removing interface: " + e);
@@ -4272,22 +4278,29 @@
// do this twice, adding non-nexthop routes first, then routes they are dependent on
for (RouteInfo route : routeDiff.added) {
if (route.hasGateway()) continue;
+ if (DBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.addRoute(netId, route);
} catch (Exception e) {
- loge("Exception in addRoute for non-gateway: " + e);
+ if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
+ loge("Exception in addRoute for non-gateway: " + e);
+ }
}
}
for (RouteInfo route : routeDiff.added) {
if (route.hasGateway() == false) continue;
+ if (DBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.addRoute(netId, route);
} catch (Exception e) {
- loge("Exception in addRoute for gateway: " + e);
+ if ((route.getGateway() instanceof Inet4Address) || VDBG) {
+ loge("Exception in addRoute for gateway: " + e);
+ }
}
}
for (RouteInfo route : routeDiff.removed) {
+ if (DBG) log("Removing Route [" + route + "] from network " + netId);
try {
mNetd.removeRoute(netId, route);
} catch (Exception e) {
@@ -4306,6 +4319,7 @@
loge("no dns provided for netId " + netId + ", so using defaults");
}
}
+ if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
try {
mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
newLp.getDomains());
@@ -4395,7 +4409,10 @@
msg.obj = o;
msg.what = notificationType;
try {
- if (VDBG) log("sending notification " + notificationType + " for " + nri.request);
+ if (VDBG) {
+ log("sending notification " + notifyTypeToName(notificationType) +
+ " for " + nri.request);
+ }
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
@@ -4418,7 +4435,7 @@
}
private void makeDefault(NetworkAgentInfo newNetwork) {
- if (VDBG) log("Switching to new default network: " + newNetwork);
+ if (DBG) log("Switching to new default network: " + newNetwork);
mActiveDefaultNetwork = newNetwork.networkInfo.getType();
setupDataActivityTracking(newNetwork);
try {
@@ -4444,7 +4461,7 @@
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (newNetwork == currentNetwork) {
- if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" +
+ if (DBG) log("Network " + newNetwork.name() + " was already satisfying" +
" request " + nri.request.requestId + ". No change.");
keep = true;
continue;
@@ -4468,12 +4485,12 @@
if (currentNetwork == null ||
currentNetwork.currentScore < newNetwork.currentScore) {
if (currentNetwork != null) {
- if (VDBG) log(" accepting network in place of " + currentNetwork.name());
+ if (DBG) log(" accepting network in place of " + currentNetwork.name());
currentNetwork.networkRequests.remove(nri.request.requestId);
currentNetwork.networkLingered.add(nri.request);
affectedNetworks.add(currentNetwork);
} else {
- if (VDBG) log(" accepting network in place of null");
+ if (DBG) log(" accepting network in place of null");
}
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
newNetwork.addRequest(nri.request);
@@ -4574,7 +4591,7 @@
loge(" " + newNetwork.networkRequests.valueAt(i));
}
}
- if (VDBG) log("Validated network turns out to be unwanted. Tear it down.");
+ if (DBG) log("Validated network turns out to be unwanted. Tear it down.");
newNetwork.asyncChannel.disconnect();
}
}
@@ -4745,7 +4762,7 @@
}
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
- if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
+ if (DBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
@@ -4754,6 +4771,20 @@
}
}
+ private String notifyTypeToName(int notifyType) {
+ switch (notifyType) {
+ case ConnectivityManager.CALLBACK_PRECHECK: return "PRECHECK";
+ case ConnectivityManager.CALLBACK_AVAILABLE: return "AVAILABLE";
+ case ConnectivityManager.CALLBACK_LOSING: return "LOSING";
+ case ConnectivityManager.CALLBACK_LOST: return "LOST";
+ case ConnectivityManager.CALLBACK_UNAVAIL: return "UNAVAILABLE";
+ case ConnectivityManager.CALLBACK_CAP_CHANGED: return "CAP_CHANGED";
+ case ConnectivityManager.CALLBACK_IP_CHANGED: return "IP_CHANGED";
+ case ConnectivityManager.CALLBACK_RELEASED: return "RELEASED";
+ }
+ return "UNKNOWN";
+ }
+
private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
if (nai != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6ca536c..6310764 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1198,11 +1198,12 @@
*/
private boolean mUserIsMonkey;
- /** Flag whether the device has a recents UI */
- final boolean mHasRecents;
+ /** Flag whether the device has a Recents UI */
+ boolean mHasRecents;
- final int mThumbnailWidth;
- final int mThumbnailHeight;
+ /** The dimensions of the thumbnails in the Recents UI. */
+ int mThumbnailWidth;
+ int mThumbnailHeight;
final ServiceThread mHandlerThread;
final MainHandler mHandler;
@@ -2257,11 +2258,6 @@
mConfigurationSeq = mConfiguration.seq = 1;
mProcessCpuTracker.init();
- final Resources res = mContext.getResources();
- mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
- mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
- mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
-
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mStackSupervisor = new ActivityStackSupervisor(this);
@@ -8952,6 +8948,14 @@
return false;
}
+ private void checkTime(long startTime, String where) {
+ long now = SystemClock.elapsedRealtime();
+ if ((now-startTime) > 1000) {
+ // If we are taking more than a second, log about it.
+ Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
+ }
+ }
+
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
@@ -8959,6 +8963,8 @@
ProviderInfo cpi = null;
synchronized(this) {
+ long startTime = SystemClock.elapsedRealtime();
+
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
@@ -8972,6 +8978,8 @@
boolean checkCrossUser = true;
+ checkTime(startTime, "getContentProviderImpl: getProviderByName");
+
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
@@ -8996,10 +9004,12 @@
if (providerRunning) {
cpi = cpr.info;
String msg;
+ checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
!= null) {
throw new SecurityException(msg);
}
+ checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (r != null && cpr.canRunHere(r)) {
// This provider has been published or is in the process
@@ -9015,6 +9025,8 @@
final long origId = Binder.clearCallingIdentity();
+ checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+
// In this case the provider instance already exists, so we can
// return it right away.
conn = incProviderCountLocked(r, cpr, token, stable);
@@ -9024,7 +9036,9 @@
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
+ checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
updateLruProcessLocked(cpr.proc, false, null);
+ checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
@@ -9037,7 +9051,9 @@
Process.killProcess(cpr.proc.pid);
}
}
+ checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
boolean success = updateOomAdjLocked(cpr.proc);
+ checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
// NOTE: there is still a race here where a signal could be
// pending on the process even though we managed to update its
@@ -9052,7 +9068,9 @@
"Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ checkTime(startTime, "getContentProviderImpl: before appDied");
appDiedLocked(cpr.proc);
+ checkTime(startTime, "getContentProviderImpl: after appDied");
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we have now been killed, bail.
@@ -10680,6 +10698,14 @@
}
}
+ /** Loads resources after the current configuration has been set. */
+ private void loadResourcesOnSystemReady() {
+ final Resources res = mContext.getResources();
+ mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
+ mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+ }
+
public boolean testIsSystemReady() {
// no need to synchronize(this) just to read & return the value
return mSystemReady;
@@ -10961,6 +10987,7 @@
}
retrieveSettings();
+ loadResourcesOnSystemReady();
synchronized (this) {
readGrantedUriPermissionsLocked();
@@ -15208,6 +15235,10 @@
final int is24Hour = intent.getBooleanExtra(
Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1 : 0;
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteCurrentTimeChangedLocked();
+ }
}
if (Intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d066940..3efd049 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -229,9 +229,6 @@
private ActivityRecord mLastScreenshotActivity = null;
private Bitmap mLastScreenshotBitmap = null;
- int mThumbnailWidth = -1;
- int mThumbnailHeight = -1;
-
int mCurrentUser;
final int mStackId;
@@ -355,10 +352,6 @@
mWindowManager = mService.mWindowManager;
mStackId = activityContainer.mStackId;
mCurrentUser = mService.mCurrentUserId;
- // Get the activity screenshot thumbnail dimensions
- Resources res = mService.mContext.getResources();
- mThumbnailWidth = mService.mThumbnailWidth;
- mThumbnailHeight = mService.mThumbnailHeight;
}
/**
@@ -773,8 +766,8 @@
return null;
}
- int w = mThumbnailWidth;
- int h = mThumbnailHeight;
+ int w = mService.mThumbnailWidth;
+ int h = mService.mThumbnailHeight;
if (w > 0) {
if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
|| mLastScreenshotActivity.state == ActivityState.RESUMED
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 9b32b65..6545134 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1543,6 +1543,15 @@
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
+ // In some flows in to this function, we retrieve the task record and hold on to it
+ // without a lock before calling back in to here... so the task at this point may
+ // not actually be in recents. Check for that, and if it isn't in recents just
+ // consider it invalid.
+ if (inTask != null && !inTask.inRecents) {
+ Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
+ inTask = null;
+ }
+
final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -1686,32 +1695,50 @@
// If the caller is not coming from another activity, but has given us an
// explicit task into which they would like us to launch the new activity,
// then let's see about doing that.
- if (sourceRecord == null && inTask != null && inTask.stack != null && inTask.inRecents) {
+ if (sourceRecord == null && inTask != null && inTask.stack != null) {
+ final Intent baseIntent = inTask.getBaseIntent();
+ final ActivityRecord root = inTask.getRootActivity();
+ if (baseIntent == null) {
+ ActivityOptions.abort(options);
+ throw new IllegalArgumentException("Launching into task without base intent: "
+ + inTask);
+ }
+
// If this task is empty, then we are adding the first activity -- it
// determines the root, and must be launching as a NEW_TASK.
- if (inTask.getRootActivity() == null) {
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
- && !launchSingleInstance && !launchSingleTask) {
- throw new IllegalStateException("Caller has inTask " + inTask
- + " but target is not a new task");
- } else if (inTask.getBaseIntent() == null || !intent.getComponent().equals(
- inTask.getBaseIntent().getComponent())) {
- throw new IllegalStateException("Caller requested " + inTask + " is component "
- + inTask.getBaseIntent() + " but starting " + intent);
+ if (launchSingleInstance || launchSingleTask) {
+ if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
+ ActivityOptions.abort(options);
+ throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+ + r + " into different task " + inTask);
}
+ if (root != null) {
+ ActivityOptions.abort(options);
+ throw new IllegalArgumentException("Caller with inTask " + inTask
+ + " has root " + root + " but target is singleInstance/Task");
+ }
+ }
+
+ // If task is empty, then adopt the interesting intent launch flags in to the
+ // activity being started.
+ if (root == null) {
+ final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+ launchFlags = (launchFlags&~flagsOfInterest)
+ | (baseIntent.getFlags()&flagsOfInterest);
+ intent.setFlags(launchFlags);
inTask.setIntent(r);
// If the task is not empty, then we are going to add the new activity on top
// of the task, so it can not be launching as a new task.
- } else {
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
- || launchSingleInstance || launchSingleTask) {
- throw new IllegalStateException("Caller has inTask " + inTask
- + " but target is a new task");
- }
+ } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ ActivityOptions.abort(options);
+ throw new IllegalStateException("Caller has inTask " + inTask
+ + " but target is a new task");
}
- sourceStack = inTask.stack;
reuseTask = inTask;
+ addingToTask = true;
} else {
inTask = null;
}
@@ -1724,10 +1751,11 @@
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance || launchSingleTask) {
- // If bring to front is requested, and no result is requested, and
+ // If bring to front is requested, and no result is requested and we have not
+ // been given an explicit task to launch in to, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
+ if (inTask == null && r.resultTo == null) {
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
@@ -1957,13 +1985,8 @@
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- if (inTask == null) {
- // If we have an incoming task, we are just going to use that.
- newTask = true;
- targetStack = adjustStackFocus(r, newTask);
- } else {
- targetStack = inTask.stack;
- }
+ newTask = true;
+ targetStack = adjustStackFocus(r, newTask);
if (!launchTaskBehind) {
targetStack.moveToFront();
}
@@ -2048,8 +2071,27 @@
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = inTask.stack;
- targetStack.moveToFront();
- mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
+ targetStack.moveTaskToFrontLocked(inTask, r, options);
+ mWindowManager.moveTaskToTop(inTask.taskId);
+
+ // Check whether we should actually launch the new activity in to the task,
+ // or just reuse the current activity on top.
+ ActivityRecord top = inTask.getTopActivity();
+ if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
+ if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || launchSingleTop || launchSingleTask) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it!
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ }
+
r.setTask(inTask, null);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in explicit task " + r.task);
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index ff22764..a37249d 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -166,8 +166,16 @@
}
if (full) {
if (hasExternalProcessHandles()) {
- pw.print(prefix); pw.print("externals=");
- pw.println(externalProcessTokenToHandle.size());
+ pw.print(prefix); pw.print("externals:");
+ if (externalProcessTokenToHandle != null) {
+ pw.print(" w/token=");
+ pw.print(externalProcessTokenToHandle.size());
+ }
+ if (externalProcessNoHandleCount > 0) {
+ pw.print(" notoken=");
+ pw.print(externalProcessNoHandleCount);
+ }
+ pw.println();
}
} else {
if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 77c324f..1287dce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -21,6 +21,7 @@
import java.nio.ByteBuffer;
import android.app.ActivityManager;
+import android.os.SystemClock;
import com.android.internal.util.MemInfoReader;
import com.android.server.wm.WindowManagerService;
@@ -528,12 +529,18 @@
if (amt == UNKNOWN_ADJ)
return;
+ long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
+ long now = SystemClock.elapsedRealtime();
+ if ((now-start) > 250) {
+ Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+ + " = " + amt);
+ }
}
/*
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 38077eb..8c342dd 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1009,7 +1009,7 @@
y[i] = normalizeAbsoluteBrightness(brightness[i]);
}
- Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ Spline spline = Spline.createSpline(x, y);
if (DEBUG) {
Slog.d(TAG, "Auto-brightness spline: " + spline);
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 827b3ed..bb22b4d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -23,6 +23,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Predicate;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -30,6 +31,8 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -446,7 +449,7 @@
allocated.add(address);
}
}
- mIoThreadLogger.debug("DevicePollingResult:" + allocated);
+ mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
if (callback != null) {
runOnServiceThread(new Runnable() {
@Override
@@ -548,7 +551,7 @@
runOnIoThread(new Runnable() {
@Override
public void run() {
- mIoThreadLogger.debug("SendCommand:" + cecMessage);
+ mIoThreadLogger.debug("[S]:" + cecMessage);
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
int i = 0;
int errorCode = Constants.SEND_RESULT_SUCCESS;
@@ -583,7 +586,7 @@
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
- mServiceThreadLogger.debug("ReceiveCommand:" + command);
+ mServiceThreadLogger.debug("[R]:" + command);
onReceiveCommand(command);
}
@@ -598,6 +601,15 @@
mService.onHotplug(port, connected);
}
+ void dump(final IndentingPrintWriter pw) {
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ pw.println("HdmiCecLocalDevice #" + i + ":");
+ pw.increaseIndent();
+ mLocalDevices.valueAt(i).dump(pw);
+ pw.decreaseIndent();
+ }
+ }
+
private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
int dstAddress, byte[] body);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 26d2cde..85f5be2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -43,6 +43,9 @@
*/
abstract class HdmiCecFeatureAction {
private static final String TAG = "HdmiCecFeatureAction";
+ // As all actions run in the same thread (service thread), it's fine to have single logger.
+ // TODO: create global logger for each threads and use them.
+ protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
// Timer handler message used for timeout event
protected static final int MSG_TIMEOUT = 100;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a12e4fc..38addba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -28,6 +28,7 @@
import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import java.util.ArrayList;
@@ -97,6 +98,17 @@
public int hashCode() {
return logicalAddress * 29 + physicalAddress;
}
+ @Override
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
+ ? "invalid" : String.format("0x%02x", logicalAddress);
+ s.append("logical_address: ").append(logicalAddressString);
+ String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
+ ? "invalid" : String.format("0x%04x", physicalAddress);
+ s.append(", physical_address: ").append(physicalAddressString);
+ return s.toString();
+ }
}
// Logical address of the active source.
@GuardedBy("mLock")
@@ -793,4 +805,16 @@
protected void sendKeyEvent(int keyCode, boolean isPressed) {
Slog.w(TAG, "sendKeyEvent not implemented");
}
+
+ /**
+ * Dump internal status of HdmiCecLocalDevice object.
+ */
+ protected void dump(final IndentingPrintWriter pw) {
+ pw.println("mDeviceType: " + mDeviceType);
+ pw.println("mAddress: " + mAddress);
+ pw.println("mPreferredAddress: " + mPreferredAddress);
+ pw.println("mDeviceInfo: " + mDeviceInfo);
+ pw.println("mActiveSource: " + mActiveSource);
+ pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 5a2fa9c..6603a71 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,6 +23,7 @@
import android.os.SystemProperties;
import android.util.Slog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
/**
@@ -219,4 +220,10 @@
mIsActiveSource = false;
checkIfPendingActionsCleared();
}
+
+ @Override
+ protected void dump(final IndentingPrintWriter pw) {
+ super.dump(pw);
+ pw.println("mIsActiveSource: " + mIsActiveSource);
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index cd56cfc..1ab8069 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -45,6 +45,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -1610,4 +1611,16 @@
invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
+
+ @Override
+ protected void dump(final IndentingPrintWriter pw) {
+ super.dump(pw);
+ pw.println("mArcEstablished: " + mArcEstablished);
+ pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
+ pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+ pw.println("mSystemAudioMute: " + mSystemAudioMute);
+ pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
+ pw.println("mAutoWakeup: " + mAutoWakeup);
+ pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e7b920a..d13e1de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -67,6 +67,7 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
@@ -75,6 +76,8 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1395,6 +1398,28 @@
enforceAccessPermission();
HdmiControlService.this.addHdmiMhlScratchpadCommandListener(listener);
}
+
+ @Override
+ protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+
+ pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+ pw.println("mProhibitMode: " + mProhibitMode);
+ if (mCecController != null) {
+ pw.println("mCecController: ");
+ pw.increaseIndent();
+ mCecController.dump(pw);
+ pw.decreaseIndent();
+ }
+ pw.println("mPortInfo: ");
+ pw.increaseIndent();
+ for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
+ pw.println("- " + hdmiPortInfo);
+ }
+ pw.decreaseIndent();
+ pw.println("mPowerStatus: " + mPowerStatus);
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index ee9379d..c7add75 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -42,7 +42,7 @@
private final String mTag;
HdmiLogger(String tag) {
- mTag = tag;
+ mTag = "HDMI:" + tag;
}
void warning(String logMessage) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index ac2c7b9..d15ffb0 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -73,9 +73,9 @@
// Seq #27
protected void sendSystemAudioModeRequest() {
- mState = STATE_CHECK_ROUTING_IN_PRGRESS;
List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
if (!routingActions.isEmpty()) {
+ mState = STATE_CHECK_ROUTING_IN_PRGRESS;
// Should have only one Routing Control Action
RoutingControlAction routingAction = routingActions.get(0);
routingAction.addOnFinishedCallback(this, new Runnable() {
@@ -97,20 +97,21 @@
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
- if (error == Constants.SEND_RESULT_SUCCESS) {
- mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
- addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
- } else {
+ if (error != Constants.SEND_RESULT_SUCCESS) {
+ DLOGGER.debug("Failed to send <System Audio Mode Request>:" + error);
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
}
}
});
+ mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+ addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
}
private void handleSendSystemAudioModeRequestTimeout() {
if (!mTargetAudioStatus // Don't retry for Off case.
|| mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+ DLOGGER.debug("[T]:wait for <Set System Audio Mode>.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
return;
@@ -129,6 +130,7 @@
if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
&& (cmd.getParams()[0] & 0xFF)
== Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
+ DLOGGER.debug("Failed to start system audio mode request.");
setSystemAudioMode(false);
finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
return true;
@@ -143,6 +145,7 @@
startAudioStatusAction();
return true;
} else {
+ DLOGGER.debug("Unexpected system audio mode request:" + receivedStatus);
// Unexpected response, consider the request is newly initiated by AVR.
// To return 'false' will initiate new SystemAudioActionFromAvr by the control
// service.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 379ec94..c3bc306 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -819,6 +819,7 @@
};
void dumpInternal(PrintWriter pw) {
+ final long now = SystemClock.elapsedRealtime();
synchronized (mJobs) {
pw.print("Started users: ");
for (int i=0; i<mStartedUsers.size(); i++) {
@@ -833,15 +834,14 @@
job.dump(pw, " ");
}
} else {
- pw.println();
- pw.println("No jobs scheduled.");
+ pw.println(" None.");
}
for (int i=0; i<mControllers.size(); i++) {
pw.println();
mControllers.get(i).dumpControllerState(pw);
}
pw.println();
- pw.println("Pending");
+ pw.println("Pending:");
for (int i=0; i<mPendingJobs.size(); i++) {
pw.println(mPendingJobs.get(i).hashCode());
}
@@ -852,10 +852,14 @@
if (jsc.isAvailable()) {
continue;
} else {
- pw.println(jsc.getRunningJob().hashCode() + " for: " +
- (SystemClock.elapsedRealtime()
- - jsc.getExecutionStartTimeElapsed())/1000 + "s " +
- "timeout: " + jsc.getTimeoutElapsed());
+ final long timeout = jsc.getTimeoutElapsed();
+ pw.print("Running for: ");
+ pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
+ pw.print("s timeout=");
+ pw.print(timeout);
+ pw.print(" fromnow=");
+ pw.println(timeout-now);
+ jsc.getRunningJob().dump(pw, " ");
}
}
pw.println();
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 6f5d3c2..f562721 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -251,6 +251,7 @@
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
pw.println(this.toString());
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 978a9f4..0da2cfa 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -23,6 +23,7 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
+import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -441,7 +442,7 @@
private String getShortMetadataString() {
int fields = mMetadata == null ? 0 : mMetadata.size();
- MediaMetadata.Description description = mMetadata == null ? null : mMetadata
+ MediaDescription description = mMetadata == null ? null : mMetadata
.getDescription();
return "size=" + fields + ", description=" + description;
}
@@ -820,9 +821,9 @@
}
}
- public void playUri(Uri uri, Bundle extras) {
+ public void playFromMediaId(String mediaId, Bundle extras) {
try {
- mCb.onPlayUri(uri, extras);
+ mCb.onPlayFromMediaId(mediaId, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playUri.", e);
}
@@ -1042,8 +1043,8 @@
}
@Override
- public void playUri(Uri uri, Bundle extras) throws RemoteException {
- mSessionCb.playUri(uri, extras);
+ public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
+ mSessionCb.playFromMediaId(mediaId, extras);
}
@Override
@@ -1052,7 +1053,7 @@
}
@Override
- public void skipToTrack(long id) {
+ public void skipToQueueItem(long id) {
mSessionCb.skipToTrack(id);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8c0d2c9..d0f4054 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.TRIM_FULL;
+import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -50,6 +52,7 @@
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -57,7 +60,6 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -127,6 +129,7 @@
static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
static final int MESSAGE_SEND_RANKING_UPDATE = 6;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
+ static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -178,6 +181,7 @@
private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
private ComponentName mEffectsSuppressor;
private int mListenerHints; // right now, all hints are global
+ private int mInterruptionFilter; // current ZEN mode as communicated to listeners
// for enabling and disabling notification pulse behavior
private boolean mScreenOn = true;
@@ -806,7 +810,7 @@
@Override
void onZenModeChanged() {
synchronized(mNotificationList) {
- updateListenerHintsLocked();
+ updateInterruptionFilterLocked();
}
}
});
@@ -938,8 +942,7 @@
}
private void updateListenerHintsLocked() {
- final int hints = (mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS) |
- mZenModeHelper.getZenModeListenerHint();
+ final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
if (hints == mListenerHints) return;
mListenerHints = hints;
scheduleListenerHintsChanged(hints);
@@ -954,6 +957,13 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
}
+ private void updateInterruptionFilterLocked() {
+ int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
+ if (interruptionFilter == mInterruptionFilter) return;
+ mInterruptionFilter = interruptionFilter;
+ scheduleInterruptionFilterChanged(interruptionFilter);
+ }
+
private final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -1283,24 +1293,23 @@
*/
@Override
public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
- INotificationListener token, String[] keys) {
+ INotificationListener token, String[] keys, int trim) {
synchronized (mNotificationList) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<StatusBarNotification> list
- = new ArrayList<StatusBarNotification>();
final boolean getKeys = keys != null;
final int N = getKeys ? keys.length : mNotificationList.size();
- list.ensureCapacity(N);
+ final ArrayList<StatusBarNotification> list
+ = new ArrayList<StatusBarNotification>(N);
for (int i=0; i<N; i++) {
final NotificationRecord r = getKeys
? mNotificationsByKey.get(keys[i])
: mNotificationList.get(i);
- if (r != null) {
- StatusBarNotification sbn = r.sbn;
- if (isVisibleToListener(sbn, info)) {
- list.add(sbn);
- }
- }
+ if (r == null) continue;
+ StatusBarNotification sbn = r.sbn;
+ if (!isVisibleToListener(sbn, info)) continue;
+ StatusBarNotification sbnToSend =
+ (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
+ list.add(sbnToSend);
}
return new ParceledListSlice<StatusBarNotification>(list);
}
@@ -1318,7 +1327,6 @@
} else {
mListenersDisablingEffects.remove(info);
}
- mZenModeHelper.requestFromListener(hints);
updateListenerHintsLocked();
updateEffectsSuppressorLocked();
}
@@ -1335,6 +1343,39 @@
}
@Override
+ public void requestInterruptionFilterFromListener(INotificationListener token,
+ int interruptionFilter) throws RemoteException {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ mListeners.checkServiceTokenLocked(token);
+ mZenModeHelper.requestFromListener(interruptionFilter);
+ updateInterruptionFilterLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public int getInterruptionFilterFromListener(INotificationListener token)
+ throws RemoteException {
+ synchronized (mNotificationLight) {
+ return mInterruptionFilter;
+ }
+ }
+
+ @Override
+ public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
+ throws RemoteException {
+ synchronized (mNotificationList) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ if (info == null) return;
+ mListeners.setOnNotificationPostedTrimLocked(info, trim);
+ }
+ }
+
+ @Override
public ZenModeConfig getZenModeConfig() {
enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
@@ -1398,7 +1439,7 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump NotificationManager from from pid="
+ pw.println("Permission Denial: can't dump NotificationManager from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
@@ -1412,6 +1453,13 @@
enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
return mEffectsSuppressor;
}
+
+ @Override
+ public boolean matchesCallFilter(Bundle extras) {
+ enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
+ return mZenModeHelper.matchesCallFilter(extras,
+ mRankingHelper.findExtractor(ValidateNotificationPeople.class));
+ }
};
private String[] getActiveNotificationKeys(INotificationListener token) {
@@ -2058,12 +2106,26 @@
mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
}
+ private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
+ mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ mHandler.obtainMessage(
+ MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
+ listenerInterruptionFilter,
+ 0).sendToTarget();
+ }
+
private void handleListenerHintsChanged(int hints) {
synchronized (mNotificationList) {
mListeners.notifyListenerHintsChangedLocked(hints);
}
}
+ private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
+ synchronized (mNotificationList) {
+ mListeners.notifyInterruptionFilterChanged(interruptionFilter);
+ }
+ }
+
private final class WorkerHandler extends Handler
{
@Override
@@ -2083,6 +2145,9 @@
case MESSAGE_LISTENER_HINTS_CHANGED:
handleListenerHintsChanged(msg.arg1);
break;
+ case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
+ handleListenerInterruptionFilterChanged(msg.arg1);
+ break;
}
}
@@ -2565,6 +2630,8 @@
public class NotificationListeners extends ManagedServices {
+ private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
}
@@ -2605,6 +2672,20 @@
if (mListenersDisablingEffects.remove(removed)) {
updateListenerHintsLocked();
}
+ mLightTrimListeners.remove(removed);
+ }
+
+ public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
+ if (trim == TRIM_LIGHT) {
+ mLightTrimListeners.add(info);
+ } else {
+ mLightTrimListeners.remove(info);
+ }
+ }
+
+ public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
+ return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
+
}
/**
@@ -2615,8 +2696,10 @@
* but isn't anymore.
*/
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
- // make a copy in case changes are made to the underlying Notification object
- final StatusBarNotification sbnClone = sbn.clone();
+ // Lazily initialized snapshots of the notification.
+ StatusBarNotification sbnClone = null;
+ StatusBarNotification sbnCloneLight = null;
+
for (final ManagedServiceInfo info : mServices) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -2638,10 +2721,20 @@
continue;
}
+ final int trim = mListeners.getOnNotificationPostedTrim(info);
+
+ if (trim == TRIM_LIGHT && sbnCloneLight == null) {
+ sbnCloneLight = sbn.cloneLight();
+ } else if (trim == TRIM_FULL && sbnClone == null) {
+ sbnClone = sbn.clone();
+ }
+ final StatusBarNotification sbnToPost =
+ (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
+
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyPosted(info, sbnClone, update);
+ notifyPosted(info, sbnToPost, update);
}
});
}
@@ -2701,6 +2794,20 @@
}
}
+ public void notifyInterruptionFilterChanged(final int interruptionFilter) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
+ if (!serviceInfo.isEnabledForCurrentProfiles()) {
+ continue;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
+ }
+ });
+ }
+ }
+
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener)info.service;
@@ -2743,6 +2850,16 @@
}
}
+ private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
+ int interruptionFilter) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ try {
+ listener.onInterruptionFilterChanged(interruptionFilter);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
+ }
+ }
+
private boolean isListenerPackage(String packageName) {
if (packageName == null) {
return false;
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 01188af..435177b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -87,6 +87,17 @@
mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
}
+ public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
+ final int N = mSignalExtractors.length;
+ for (int i = 0; i < N; i++) {
+ final NotificationSignalExtractor extractor = mSignalExtractors[i];
+ if (extractorClass.equals(extractor.getClass())) {
+ return (T) extractor;
+ }
+ }
+ return null;
+ }
+
public void extractSignals(NotificationRecord r) {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index bdc364c..aa47858 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -71,8 +71,17 @@
private LruCache<String, LookupResult> mPeopleCache;
private RankingReconsideration validatePeople(final NotificationRecord record) {
+ final String key = record.getKey();
+ final Bundle extras = record.getNotification().extras;
+ final float[] affinityOut = new float[1];
+ final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
+ record.setContactAffinity(affinityOut[0]);
+ return rr;
+ }
+
+ private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
+ float[] affinityOut) {
float affinity = NONE;
- Bundle extras = record.getNotification().extras;
if (extras == null) {
return null;
}
@@ -82,7 +91,7 @@
return null;
}
- if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+ if (INFO) Slog.i(TAG, "Validating: " + key);
final LinkedList<String> pendingLookups = new LinkedList<String>();
for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
final String handle = people[personIdx];
@@ -102,51 +111,15 @@
}
// record the best available data, so far:
- record.setContactAffinity(affinity);
+ affinityOut[0] = affinity;
if (pendingLookups.isEmpty()) {
if (INFO) Slog.i(TAG, "final affinity: " + affinity);
return null;
}
- if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
- return new RankingReconsideration(record.getKey()) {
- float mContactAffinity = NONE;
- @Override
- public void work() {
- if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
- for (final String handle: pendingLookups) {
- LookupResult lookupResult = null;
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
- } else if ("mailto".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(uri);
- } else {
- lookupResult = new LookupResult(); // invalid person for the cache
- Slog.w(TAG, "unsupported URI " + handle);
- }
- if (lookupResult != null) {
- synchronized (mPeopleCache) {
- mPeopleCache.put(handle, lookupResult);
- }
- mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
- }
- }
- }
-
- @Override
- public void applyChangesLocked(NotificationRecord operand) {
- float affinityBound = operand.getContactAffinity();
- operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
- if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
- }
- };
+ if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
+ return new PeopleRankingReconsideration(key, pendingLookups);
}
// VisibleForTesting
@@ -269,6 +242,19 @@
// ignore: config has no relevant information yet.
}
+ public float getContactAffinity(Bundle extras) {
+ if (extras == null) return NONE;
+ final String key = Long.toString(System.nanoTime());
+ final float[] affinityOut = new float[1];
+ final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
+ float affinity = affinityOut[0];
+ if (prr != null) {
+ prr.work();
+ affinity = Math.max(prr.getContactAffinity(), affinity);
+ }
+ return affinity;
+ }
+
private static class LookupResult {
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
public static final int INVALID_ID = -1;
@@ -328,5 +314,55 @@
return this;
}
}
+
+ private class PeopleRankingReconsideration extends RankingReconsideration {
+ private final LinkedList<String> mPendingLookups;
+
+ private float mContactAffinity = NONE;
+
+ private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
+ super(key);
+ mPendingLookups = pendingLookups;
+ }
+
+ @Override
+ public void work() {
+ if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey);
+ for (final String handle: mPendingLookups) {
+ LookupResult lookupResult = null;
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
+ } else if ("mailto".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
+ lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = searchContacts(uri);
+ } else {
+ lookupResult = new LookupResult(); // invalid person for the cache
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
+ if (lookupResult != null) {
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
+ }
+ }
+ }
+
+ @Override
+ public void applyChangesLocked(NotificationRecord operand) {
+ float affinityBound = operand.getContactAffinity();
+ operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
+ if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
+ }
+
+ public float getContactAffinity() {
+ return mContactAffinity;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0b93690..fd35ede 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -34,6 +34,7 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings.Global;
@@ -115,35 +116,35 @@
mAudioManager = audioManager;
}
- public int getZenModeListenerHint() {
- switch(mZenMode) {
+ public int getZenModeListenerInterruptionFilter() {
+ switch (mZenMode) {
case Global.ZEN_MODE_OFF:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL;
+ return NotificationListenerService.INTERRUPTION_FILTER_ALL;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY;
+ return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY;
case Global.ZEN_MODE_NO_INTERRUPTIONS:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE;
+ return NotificationListenerService.INTERRUPTION_FILTER_NONE;
default:
return 0;
}
}
- private static int zenFromListenerHint(int hints, int defValue) {
- final int level = hints & NotificationListenerService.HOST_INTERRUPTION_LEVEL_MASK;
- switch(level) {
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL:
+ private static int zenModeFromListenerInterruptionFilter(int listenerInterruptionFilter,
+ int defValue) {
+ switch (listenerInterruptionFilter) {
+ case NotificationListenerService.INTERRUPTION_FILTER_ALL:
return Global.ZEN_MODE_OFF;
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY:
+ case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY:
return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE:
+ case NotificationListenerService.INTERRUPTION_FILTER_NONE:
return Global.ZEN_MODE_NO_INTERRUPTIONS;
default:
return defValue;
}
}
- public void requestFromListener(int hints) {
- final int newZen = zenFromListenerHint(hints, -1);
+ public void requestFromListener(int interruptionFilter) {
+ final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1);
if (newZen != -1) {
setZenMode(newZen, "listener");
}
@@ -189,7 +190,7 @@
}
private boolean shouldInterceptAudience(NotificationRecord record) {
- if (!audienceMatches(record)) {
+ if (!audienceMatches(record.getContactAffinity())) {
ZenLog.traceIntercepted(record, "!audienceMatches");
return true;
}
@@ -372,14 +373,27 @@
return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
}
- private boolean audienceMatches(NotificationRecord record) {
+ public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
+ final int zen = mZenMode;
+ if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
+ if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ if (!mConfig.allowCalls) return false; // no calls get through
+ if (validator != null) {
+ final float contactAffinity = validator.getContactAffinity(extras);
+ return audienceMatches(contactAffinity);
+ }
+ }
+ return true;
+ }
+
+ private boolean audienceMatches(float contactAffinity) {
switch (mConfig.allowFrom) {
case ZenModeConfig.SOURCE_ANYONE:
return true;
case ZenModeConfig.SOURCE_CONTACT:
- return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
+ return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
case ZenModeConfig.SOURCE_STAR:
- return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
+ return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
default:
Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
return true;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 694669c..ca11862 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -224,10 +224,6 @@
}
}
- public int pruneDexCache(String cacheSubDir) {
- return mInstaller.execute("prunedexcache " + cacheSubDir);
- }
-
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89878ef..fbaeb37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1497,29 +1497,6 @@
}
}
- if (didDexOptLibraryOrTool) {
- // If we dexopted a library or tool, then something on the system has
- // changed. Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any dangling around.
- //
- // TODO: This should be revisited because it isn't as good an indicator
- // as it used to be. It used to include the boot classpath but at some point
- // DexFile.isDexOptNeeded started returning false for the boot
- // class path files in all cases. It is very possible in a
- // small maintenance release update that the library and tool
- // jars may be unchanged but APK could be removed resulting in
- // unused dalvik-cache files.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- mInstaller.pruneDexCache(dexCodeInstructionSet);
- }
-
- // Additionally, delete all dex files from the root directory
- // since there shouldn't be any there anyway, unless we're upgrading
- // from an older OS version or a build that contained the "old" style
- // flat scheme.
- mInstaller.pruneDexCache(".");
- }
-
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
@@ -7721,8 +7698,21 @@
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
+ installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
+ packageAbiOverride, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
+ int installFlags, String installerPackageName, VerificationParams verificationParams,
+ String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "installPackage " + userId);
+ }
final File originFile = new File(originPath);
final int uid = Binder.getCallingUid();
@@ -7740,7 +7730,7 @@
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
- user = new UserHandle(UserHandle.getUserId(uid));
+ user = new UserHandle(userId);
}
final int filteredInstallFlags;
@@ -12988,7 +12978,7 @@
}
/** Called by UserManagerService */
- void cleanUpUserLILPw(int userHandle) {
+ void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
mDirtyUsers.remove(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
@@ -12999,6 +12989,50 @@
mInstaller.removeUserDataDirs(userHandle);
}
mUserNeedsBadging.delete(userHandle);
+ removeUnusedPackagesLILPw(userManager, userHandle);
+ }
+
+ /**
+ * We're removing userHandle and would like to remove any downloaded packages
+ * that are no longer in use by any other user.
+ * @param userHandle the user being removed
+ */
+ private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+ final boolean DEBUG_CLEAN_APKS = false;
+ int [] users = userManager.getUserIdsLPr();
+ Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+ while (psit.hasNext()) {
+ PackageSetting ps = psit.next();
+ final String packageName = ps.pkg.packageName;
+ // Skip over if system app
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ continue;
+ }
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, "Checking package " + packageName);
+ }
+ boolean keep = false;
+ for (int i = 0; i < users.length; i++) {
+ if (users[i] != userHandle && ps.getInstalled(users[i])) {
+ keep = true;
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Keeping package " + packageName + " for user "
+ + users[i]);
+ }
+ break;
+ }
+ }
+ if (!keep) {
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Removing package " + packageName);
+ }
+ mHandler.post(new Runnable() {
+ public void run() {
+ deletePackageX(packageName, userHandle, 0);
+ } //end run
+ });
+ }
+ }
}
/** Called by UserManagerService */
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8ded7ca..2929939 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -293,6 +293,10 @@
private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
UserInfo user = getUserInfoLocked(userId);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ if (user == null) {
+ // Probably a dying user
+ return users;
+ }
for (int i = 0; i < mUsers.size(); i++) {
UserInfo profile = mUsers.valueAt(i);
if (!isProfileOf(user, profile)) {
@@ -1280,7 +1284,7 @@
private void removeUserStateLocked(final int userHandle) {
// Cleanup package manager settings
- mPm.cleanUpUserLILPw(userHandle);
+ mPm.cleanUpUserLILPw(this, userHandle);
// Remove this user from the list
mUsers.remove(userHandle);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a49d8ab..d22912c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -119,7 +119,7 @@
mContext = context;
mContentResolver = context.getContentResolver();
- mWatchLogHandler = new WatchLogHandler(IoThread.get().getLooper());
+ mWatchLogHandler = new WatchLogHandler(mContentResolver, IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
@@ -248,7 +248,7 @@
serviceState = new ServiceState(component, userId);
userState.serviceStateMap.put(component, serviceState);
} else {
- inputList.addAll(serviceState.mInputList);
+ inputList.addAll(serviceState.inputList);
}
} else {
try {
@@ -258,9 +258,6 @@
continue;
}
}
-
- // Reconnect the service if existing input is updated.
- updateServiceConnectionLocked(component, userId);
userState.packageSet.add(si.packageName);
}
@@ -273,7 +270,7 @@
if (state == null) {
state = new TvInputState();
}
- state.mInfo = info;
+ state.info = info;
inputMap.put(info.getId(), state);
}
@@ -285,7 +282,7 @@
for (String inputId : userState.inputMap.keySet()) {
if (!inputMap.containsKey(inputId)) {
- TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+ TvInputInfo info = userState.inputMap.get(inputId).info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState != null) {
abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
@@ -352,9 +349,9 @@
}
// Release created sessions.
for (SessionState state : userState.sessionStateMap.values()) {
- if (state.mSession != null) {
+ if (state.session != null) {
try {
- state.mSession.release();
+ state.session.release();
} catch (RemoteException e) {
Slog.e(TAG, "error in release", e);
}
@@ -364,14 +361,14 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (serviceState.mCallback != null) {
+ if (serviceState.callback != null) {
try {
- serviceState.mService.unregisterCallback(serviceState.mCallback);
+ serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in unregisterCallback", e);
}
}
- mContext.unbindService(serviceState.mConnection);
+ mContext.unbindService(serviceState.connection);
}
userState.serviceStateMap.clear();
@@ -412,7 +409,7 @@
throw new IllegalArgumentException("Session state not found for token " + sessionToken);
}
// Only the application that requested this session or the system can access it.
- if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+ if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
throw new SecurityException("Illegal access to the session with token " + sessionToken
+ " from uid " + callingUid);
}
@@ -424,10 +421,10 @@
}
private ITvInputSession getSessionLocked(SessionState sessionState) {
- ITvInputSession session = sessionState.mSession;
+ ITvInputSession session = sessionState.session;
if (session == null) {
throw new IllegalStateException("Session not yet created for token "
- + sessionState.mSessionToken);
+ + sessionState.sessionToken);
}
return session;
}
@@ -439,8 +436,8 @@
}
private static boolean shouldMaintainConnection(ServiceState serviceState) {
- return !serviceState.mSessionTokens.isEmpty() || serviceState.mIsHardware;
- // TODO: Find a way to maintain connection only when necessary.
+ return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
+ // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
}
private void updateServiceConnectionLocked(ComponentName component, int userId) {
@@ -449,18 +446,18 @@
if (serviceState == null) {
return;
}
- if (serviceState.mReconnecting) {
- if (!serviceState.mSessionTokens.isEmpty()) {
+ if (serviceState.reconnecting) {
+ if (!serviceState.sessionTokens.isEmpty()) {
// wait until all the sessions are removed.
return;
}
- serviceState.mReconnecting = false;
+ serviceState.reconnecting = false;
}
boolean maintainConnection = shouldMaintainConnection(serviceState);
- if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
+ if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
// This means that the service is not yet connected but its state indicates that we
// have pending requests. Then, connect the service.
- if (serviceState.mBound) {
+ if (serviceState.bound) {
// We have already bound to the service so we don't try to bind again until after we
// unbind later on.
return;
@@ -470,18 +467,15 @@
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
- // Binding service may fail if the service is updating.
- // In that case, the connection will be revived in buildTvInputListLocked called by
- // onSomePackagesChanged.
- serviceState.mBound = mContext.bindServiceAsUser(
- i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
- } else if (serviceState.mService != null && !maintainConnection) {
+ serviceState.bound = mContext.bindServiceAsUser(
+ i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
+ } else if (serviceState.service != null && !maintainConnection) {
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
Slog.d(TAG, "unbindService(service=" + component + ")");
}
- mContext.unbindService(serviceState.mConnection);
+ mContext.unbindService(serviceState.connection);
userState.serviceStateMap.remove(component);
}
}
@@ -491,19 +485,19 @@
// Let clients know the create session requests are failed.
UserState userState = getUserStateLocked(userId);
List<SessionState> sessionsToAbort = new ArrayList<>();
- for (IBinder sessionToken : serviceState.mSessionTokens) {
+ for (IBinder sessionToken : serviceState.sessionTokens) {
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
- if (sessionState.mSession == null && (inputId == null
- || sessionState.mInfo.getId().equals(inputId))) {
+ if (sessionState.session == null && (inputId == null
+ || sessionState.info.getId().equals(inputId))) {
sessionsToAbort.add(sessionState);
}
}
for (SessionState sessionState : sessionsToAbort) {
- removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), null, null, sessionState.mSeq);
+ removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), null, null, sessionState.seq);
}
- updateServiceConnectionLocked(serviceState.mComponent, userId);
+ updateServiceConnectionLocked(serviceState.component, userId);
}
private ClientState createClientStateLocked(IBinder clientToken, int userId) {
@@ -518,221 +512,26 @@
return clientState;
}
- private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
- final int userId) {
- final UserState userState = getUserStateLocked(userId);
- final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+ int userId) {
+ UserState userState = getUserStateLocked(userId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
- Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
+ Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
}
-
- final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+ InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
// Set up a callback to send the session token.
- ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
- @Override
- public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
- if (DEBUG) {
- Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
- }
- synchronized (mLock) {
- sessionState.mSession = session;
- sessionState.mHardwareSessionToken = harewareSessionToken;
- if (session == null) {
- removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), null, null, sessionState.mSeq);
- } else {
- try {
- session.asBinder().linkToDeath(sessionState, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "session process has already died", e);
- }
-
- IBinder clientToken = sessionState.mClient.asBinder();
- ClientState clientState = userState.clientStateMap.get(clientToken);
- if (clientState == null) {
- clientState = createClientStateLocked(clientToken, userId);
- }
- clientState.mSessionTokens.add(sessionState.mSessionToken);
-
- sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInfo.getId(), sessionToken, channels[0],
- sessionState.mSeq);
- }
- channels[0].dispose();
- }
- }
-
- @Override
- public void onChannelRetuned(Uri channelUri) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- // TODO: Consider adding this channel change in the watch log. When we do
- // that, how we can protect the watch log from malicious tv inputs should
- // be addressed. e.g. add a field which represents where the channel change
- // originated from.
- sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onChannelRetuned", e);
- }
- }
- }
-
- @Override
- public void onTracksChanged(List<TvTrackInfo> tracks) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onTracksChanged(" + tracks + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onTracksChanged", e);
- }
- }
- }
-
- @Override
- public void onTrackSelected(int type, String trackId) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onTrackSelected", e);
- }
- }
- }
-
- @Override
- public void onVideoAvailable() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onVideoAvailable()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onVideoAvailable(sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onVideoAvailable", e);
- }
- }
- }
-
- @Override
- public void onVideoUnavailable(int reason) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onVideoUnavailable", e);
- }
- }
- }
-
- @Override
- public void onContentAllowed() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onContentAllowed()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onContentAllowed(sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onContentAllowed", e);
- }
- }
- }
-
- @Override
- public void onContentBlocked(String rating) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onContentBlocked()");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onContentBlocked", e);
- }
- }
- }
-
- @Override
- public void onLayoutSurface(int left, int top, int right, int bottom) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
- + ", right=" + right + ", bottom=" + bottom + ",)");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onLayoutSurface(left, top, right, bottom,
- sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onLayoutSurface", e);
- }
- }
- }
-
- @Override
- public void onSessionEvent(String eventType, Bundle eventArgs) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
- }
- if (sessionState.mSession == null || sessionState.mClient == null) {
- return;
- }
- try {
- sessionState.mClient.onSessionEvent(eventType, eventArgs,
- sessionState.mSeq);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onSessionEvent", e);
- }
- }
- }
- };
+ ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
// Create a session. When failed, send a null token immediately.
try {
- service.createSession(channels[1], callback, sessionState.mInfo.getId());
+ service.createSession(channels[1], callback, sessionState.info.getId());
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
- null, sessionState.mSeq);
+ sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
+ null, sessionState.seq);
}
channels[1].dispose();
}
@@ -748,17 +547,17 @@
private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
- if (sessionState.mSession != null) {
+ if (sessionState.session != null) {
UserState userState = getUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
try {
- sessionState.mSession.release();
+ sessionState.session.release();
} catch (RemoteException e) {
Slog.e(TAG, "session process has already died", e);
}
- sessionState.mSession = null;
+ sessionState.session = null;
}
removeSessionStateLocked(sessionToken, userId);
}
@@ -781,22 +580,22 @@
// Also remove the session token from the session token list of the current client and
// service.
- ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+ ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
if (clientState != null) {
- clientState.mSessionTokens.remove(sessionToken);
+ clientState.sessionTokens.remove(sessionToken);
if (clientState.isEmpty()) {
- userState.clientStateMap.remove(sessionState.mClient.asBinder());
+ userState.clientStateMap.remove(sessionState.client.asBinder());
}
}
- TvInputInfo info = sessionState.mInfo;
+ TvInputInfo info = sessionState.info;
if (info != null) {
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState != null) {
- serviceState.mSessionTokens.remove(sessionToken);
+ serviceState.sessionTokens.remove(sessionToken);
}
}
- updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
+ updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
// Log the end of watch.
SomeArgs args = SomeArgs.obtain();
@@ -807,13 +606,12 @@
private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
- if (sessionState.mHardwareSessionToken != null) {
- sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
+ if (sessionState.hardwareSessionToken != null) {
+ sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, userId);
}
- ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
- userId);
- if (!serviceState.mIsHardware) {
+ ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+ if (!serviceState.isHardware) {
return;
}
ITvInputSession session = getSessionLocked(sessionState);
@@ -876,10 +674,10 @@
private void setStateLocked(String inputId, int state, int userId) {
UserState userState = getUserStateLocked(userId);
TvInputState inputState = userState.inputMap.get(inputId);
- ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent());
- int oldState = inputState.mState;
- inputState.mState = state;
- if (serviceState != null && serviceState.mService == null
+ ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
+ int oldState = inputState.state;
+ inputState.state = state;
+ if (serviceState != null && serviceState.service == null
&& shouldMaintainConnection(serviceState)) {
// We don't notify state change while reconnecting. It should remain disconnected.
return;
@@ -900,7 +698,7 @@
UserState userState = getUserStateLocked(resolvedUserId);
List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
for (TvInputState state : userState.inputMap.values()) {
- inputList.add(state.mInfo);
+ inputList.add(state.info);
}
return inputList;
}
@@ -918,7 +716,7 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
TvInputState state = userState.inputMap.get(inputId);
- return state == null ? null : state.mInfo;
+ return state == null ? null : state.info;
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -964,8 +762,8 @@
Slog.e(TAG, "client process has already died", e);
}
for (TvInputState state : userState.inputMap.values()) {
- notifyInputStateChangedLocked(userState, state.mInfo.getId(),
- state.mState, callback);
+ notifyInputStateChangedLocked(userState, state.info.getId(), state.state,
+ callback);
}
}
} finally {
@@ -1114,14 +912,14 @@
sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
- TvInputInfo info = inputState.mInfo;
+ TvInputInfo info = inputState.info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState == null) {
serviceState = new ServiceState(info.getComponent(), resolvedUserId);
userState.serviceStateMap.put(info.getComponent(), serviceState);
}
// Send a null token immediately while reconnecting.
- if (serviceState.mReconnecting == true) {
+ if (serviceState.reconnecting == true) {
sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
@@ -1135,10 +933,10 @@
userState.sessionStateMap.put(sessionToken, sessionState);
// Also, add them to the session state map of the current service.
- serviceState.mSessionTokens.add(sessionToken);
+ serviceState.sessionTokens.add(sessionToken);
- if (serviceState.mService != null) {
- createSessionInternalLocked(serviceState.mService, sessionToken,
+ if (serviceState.service != null) {
+ createSessionInternalLocked(serviceState.service, sessionToken,
resolvedUserId);
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
@@ -1213,10 +1011,10 @@
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- if (sessionState.mHardwareSessionToken == null) {
+ if (sessionState.hardwareSessionToken == null) {
getSessionLocked(sessionState).setSurface(surface);
} else {
- getSessionLocked(sessionState.mHardwareSessionToken,
+ getSessionLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
}
} catch (RemoteException e) {
@@ -1244,9 +1042,10 @@
try {
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
- getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
- if (sessionState.mHardwareSessionToken != null) {
- getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+ getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+ height);
+ if (sessionState.hardwareSessionToken != null) {
+ getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
resolvedUserId).dispatchSurfaceChanged(format, width, height);
}
} catch (RemoteException e) {
@@ -1272,10 +1071,10 @@
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
getSessionLocked(sessionState).setVolume(volume);
- if (sessionState.mHardwareSessionToken != null) {
+ if (sessionState.hardwareSessionToken != null) {
// Here, we let the hardware session know only whether volume is on or
// off to prevent that the volume is controlled in the both side.
- getSessionLocked(sessionState.mHardwareSessionToken,
+ getSessionLocked(sessionState.hardwareSessionToken,
Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
}
@@ -1309,7 +1108,7 @@
// Log the start of watch.
SomeArgs args = SomeArgs.obtain();
- args.arg1 = sessionState.mInfo.getComponent().getPackageName();
+ args.arg1 = sessionState.info.getComponent().getPackageName();
args.arg2 = System.currentTimeMillis();
args.arg3 = ContentUris.parseId(channelUri);
args.arg4 = params;
@@ -1569,10 +1368,10 @@
return false;
}
for (SessionState sessionState : userState.sessionStateMap.values()) {
- if (sessionState.mInfo.getId().equals(inputId)
- && sessionState.mHardwareSessionToken != null) {
+ if (sessionState.info.getId().equals(inputId)
+ && sessionState.hardwareSessionToken != null) {
hardwareInputId = userState.sessionStateMap.get(
- sessionState.mHardwareSessionToken).mInfo.getId();
+ sessionState.hardwareSessionToken).info.getId();
break;
}
}
@@ -1601,8 +1400,8 @@
SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
new SessionState[0]);
// Check if there is a wrapper input.
- if (sessionStates[0].mHardwareSessionToken != null
- || sessionStates[1].mHardwareSessionToken != null) {
+ if (sessionStates[0].hardwareSessionToken != null
+ || sessionStates[1].hardwareSessionToken != null) {
return true;
}
}
@@ -1662,15 +1461,15 @@
pw.increaseIndent();
- pw.println("mSessionTokens:");
+ pw.println("sessionTokens:");
pw.increaseIndent();
- for (IBinder token : client.mSessionTokens) {
+ for (IBinder token : client.sessionTokens) {
pw.println("" + token);
}
pw.decreaseIndent();
- pw.println("mClientTokens: " + client.mClientToken);
- pw.println("mUserId: " + client.mUserId);
+ pw.println("clientTokens: " + client.clientToken);
+ pw.println("userId: " + client.userId);
pw.decreaseIndent();
}
@@ -1685,17 +1484,17 @@
pw.increaseIndent();
- pw.println("mSessionTokens:");
+ pw.println("sessionTokens:");
pw.increaseIndent();
- for (IBinder token : service.mSessionTokens) {
+ for (IBinder token : service.sessionTokens) {
pw.println("" + token);
}
pw.decreaseIndent();
- pw.println("mService: " + service.mService);
- pw.println("mCallback: " + service.mCallback);
- pw.println("mBound: " + service.mBound);
- pw.println("mReconnecting: " + service.mReconnecting);
+ pw.println("service: " + service.service);
+ pw.println("callback: " + service.callback);
+ pw.println("bound: " + service.bound);
+ pw.println("reconnecting: " + service.reconnecting);
pw.decreaseIndent();
}
@@ -1709,15 +1508,15 @@
pw.println(entry.getKey() + ": " + session);
pw.increaseIndent();
- pw.println("mInfo: " + session.mInfo);
- pw.println("mClient: " + session.mClient);
- pw.println("mSeq: " + session.mSeq);
- pw.println("mCallingUid: " + session.mCallingUid);
- pw.println("mUserId: " + session.mUserId);
- pw.println("mSessionToken: " + session.mSessionToken);
- pw.println("mSession: " + session.mSession);
- pw.println("mLogUri: " + session.mLogUri);
- pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
+ pw.println("info: " + session.info);
+ pw.println("client: " + session.client);
+ pw.println("seq: " + session.seq);
+ pw.println("callingUid: " + session.callingUid);
+ pw.println("userId: " + session.userId);
+ pw.println("sessionToken: " + session.sessionToken);
+ pw.println("session: " + session.session);
+ pw.println("logUri: " + session.logUri);
+ pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
pw.decreaseIndent();
}
pw.decreaseIndent();
@@ -1736,19 +1535,6 @@
}
}
- private static final class TvInputState {
- // A TvInputInfo object which represents the TV input.
- private TvInputInfo mInfo;
-
- // The state of TV input. Connected by default.
- private int mState = INPUT_STATE_CONNECTED;
-
- @Override
- public String toString() {
- return "mInfo: " + mInfo + "; mState: " + mState;
- }
- }
-
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
@@ -1789,104 +1575,117 @@
}
private final class ClientState implements IBinder.DeathRecipient {
- private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+ private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
- private IBinder mClientToken;
- private final int mUserId;
+ private IBinder clientToken;
+ private final int userId;
ClientState(IBinder clientToken, int userId) {
- mClientToken = clientToken;
- mUserId = userId;
+ this.clientToken = clientToken;
+ this.userId = userId;
}
public boolean isEmpty() {
- return mSessionTokens.isEmpty();
+ return sessionTokens.isEmpty();
}
@Override
public void binderDied() {
synchronized (mLock) {
- UserState userState = getUserStateLocked(mUserId);
+ UserState userState = getUserStateLocked(userId);
// DO NOT remove the client state of clientStateMap in this method. It will be
// removed in releaseSessionLocked().
- ClientState clientState = userState.clientStateMap.get(mClientToken);
+ ClientState clientState = userState.clientStateMap.get(clientToken);
if (clientState != null) {
- while (clientState.mSessionTokens.size() > 0) {
+ while (clientState.sessionTokens.size() > 0) {
releaseSessionLocked(
- clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+ clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
}
}
- mClientToken = null;
+ clientToken = null;
}
}
}
private final class ServiceState {
- private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
- private final ServiceConnection mConnection;
- private final ComponentName mComponent;
- private final boolean mIsHardware;
- private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
+ private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
+ private final ServiceConnection connection;
+ private final ComponentName component;
+ private final boolean isHardware;
+ private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
- private ITvInputService mService;
- private ServiceCallback mCallback;
- private boolean mBound;
- private boolean mReconnecting;
+ private ITvInputService service;
+ private ServiceCallback callback;
+ private boolean bound;
+ private boolean reconnecting;
private ServiceState(ComponentName component, int userId) {
- mComponent = component;
- mConnection = new InputServiceConnection(component, userId);
- mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent);
+ this.component = component;
+ this.connection = new InputServiceConnection(component, userId);
+ this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
+ }
+ }
+
+ private static final class TvInputState {
+ // A TvInputInfo object which represents the TV input.
+ private TvInputInfo info;
+
+ // The state of TV input. Connected by default.
+ private int state = INPUT_STATE_CONNECTED;
+
+ @Override
+ public String toString() {
+ return "info: " + info + "; state: " + state;
}
}
private final class SessionState implements IBinder.DeathRecipient {
- private final TvInputInfo mInfo;
- private final ITvInputClient mClient;
- private final int mSeq;
- private final int mCallingUid;
- private final int mUserId;
- private final IBinder mSessionToken;
- private ITvInputSession mSession;
- private Uri mLogUri;
+ private final TvInputInfo info;
+ private final ITvInputClient client;
+ private final int seq;
+ private final int callingUid;
+ private final int userId;
+ private final IBinder sessionToken;
+ private ITvInputSession session;
+ private Uri logUri;
// Not null if this session represents an external device connected to a hardware TV input.
- private IBinder mHardwareSessionToken;
+ private IBinder hardwareSessionToken;
private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
int seq, int callingUid, int userId) {
- mSessionToken = sessionToken;
- mInfo = info;
- mClient = client;
- mSeq = seq;
- mCallingUid = callingUid;
- mUserId = userId;
+ this.sessionToken = sessionToken;
+ this.info = info;
+ this.client = client;
+ this.seq = seq;
+ this.callingUid = callingUid;
+ this.userId = userId;
}
@Override
public void binderDied() {
synchronized (mLock) {
- mSession = null;
- if (mClient != null) {
+ session = null;
+ if (client != null) {
try {
- mClient.onSessionReleased(mSeq);
+ client.onSessionReleased(seq);
} catch(RemoteException e) {
Slog.e(TAG, "error in onSessionReleased", e);
}
}
// If there are any other sessions based on this session, they should be released.
- UserState userState = getUserStateLocked(mUserId);
+ UserState userState = getUserStateLocked(userId);
for (SessionState sessionState : userState.sessionStateMap.values()) {
- if (mSessionToken == sessionState.mHardwareSessionToken) {
- releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
- mUserId);
+ if (sessionToken == sessionState.hardwareSessionToken) {
+ releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
+ userId);
try {
- sessionState.mClient.onSessionReleased(sessionState.mSeq);
+ sessionState.client.onSessionReleased(sessionState.seq);
} catch (RemoteException e) {
Slog.e(TAG, "error in onSessionReleased", e);
}
}
}
- removeSessionStateLocked(mSessionToken, mUserId);
+ removeSessionStateLocked(sessionToken, userId);
}
}
}
@@ -1908,37 +1707,37 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(mUserId);
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
- serviceState.mService = ITvInputService.Stub.asInterface(service);
+ serviceState.service = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
- if (serviceState.mIsHardware && serviceState.mCallback == null) {
- serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+ if (serviceState.isHardware && serviceState.callback == null) {
+ serviceState.callback = new ServiceCallback(mComponent, mUserId);
try {
- serviceState.mService.registerCallback(serviceState.mCallback);
+ serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in registerCallback", e);
}
}
// And create sessions, if any.
- for (IBinder sessionToken : serviceState.mSessionTokens) {
- createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
+ for (IBinder sessionToken : serviceState.sessionTokens) {
+ createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
}
for (TvInputState inputState : userState.inputMap.values()) {
- if (inputState.mInfo.getComponent().equals(component)
- && inputState.mState != INPUT_STATE_DISCONNECTED) {
- notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
- inputState.mState, null);
+ if (inputState.info.getComponent().equals(component)
+ && inputState.state != INPUT_STATE_DISCONNECTED) {
+ notifyInputStateChangedLocked(userState, inputState.info.getId(),
+ inputState.state, null);
}
}
- if (serviceState.mIsHardware) {
+ if (serviceState.isHardware) {
List<TvInputHardwareInfo> hardwareInfoList =
mTvInputHardwareManager.getHardwareList();
for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
try {
- serviceState.mService.notifyHardwareAdded(hardwareInfo);
+ serviceState.service.notifyHardwareAdded(hardwareInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
@@ -1948,7 +1747,7 @@
mTvInputHardwareManager.getHdmiDeviceList();
for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
try {
- serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+ serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
@@ -1970,16 +1769,16 @@
UserState userState = getUserStateLocked(mUserId);
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
if (serviceState != null) {
- serviceState.mReconnecting = true;
- serviceState.mBound = false;
- serviceState.mService = null;
- serviceState.mCallback = null;
+ serviceState.reconnecting = true;
+ serviceState.bound = false;
+ serviceState.service = null;
+ serviceState.callback = null;
abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
for (TvInputState inputState : userState.inputMap.values()) {
- if (inputState.mInfo.getComponent().equals(component)) {
- notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+ if (inputState.info.getComponent().equals(component)) {
+ notifyInputStateChangedLocked(userState, inputState.info.getId(),
INPUT_STATE_DISCONNECTED, null);
}
}
@@ -2012,7 +1811,7 @@
private void addTvInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- serviceState.mInputList.add(inputInfo);
+ serviceState.inputList.add(inputInfo);
buildTvInputListLocked(mUserId);
}
@@ -2042,7 +1841,7 @@
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
boolean removed = false;
- for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
+ for (Iterator<TvInputInfo> it = serviceState.inputList.iterator();
it.hasNext(); ) {
if (it.next().getId().equals(inputId)) {
it.remove();
@@ -2060,7 +1859,211 @@
}
}
- private final class WatchLogHandler extends Handler {
+ private final class SessionCallback extends ITvInputSessionCallback.Stub {
+ private final SessionState sessionState;
+ private final InputChannel[] mChannels;
+
+ SessionCallback(SessionState sessionState, InputChannel[] channels) {
+ this.sessionState = sessionState;
+ mChannels = channels;
+ }
+
+ @Override
+ public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
+ if (DEBUG) {
+ Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")");
+ }
+ synchronized (mLock) {
+ sessionState.session = session;
+ sessionState.hardwareSessionToken = harewareSessionToken;
+ if (session == null) {
+ removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), null, null, sessionState.seq);
+ } else {
+ try {
+ session.asBinder().linkToDeath(sessionState, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "session process has already died", e);
+ }
+
+ IBinder clientToken = sessionState.client.asBinder();
+ UserState userState = getUserStateLocked(sessionState.userId);
+ ClientState clientState = userState.clientStateMap.get(clientToken);
+ if (clientState == null) {
+ clientState = createClientStateLocked(clientToken, sessionState.userId);
+ }
+ clientState.sessionTokens.add(sessionState.sessionToken);
+
+ sendSessionTokenToClientLocked(sessionState.client,
+ sessionState.info.getId(), sessionState.sessionToken, mChannels[0],
+ sessionState.seq);
+ }
+ mChannels[0].dispose();
+ }
+ }
+
+ @Override
+ public void onChannelRetuned(Uri channelUri) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ // TODO: Consider adding this channel change in the watch log. When we do
+ // that, how we can protect the watch log from malicious tv inputs should
+ // be addressed. e.g. add a field which represents where the channel change
+ // originated from.
+ sessionState.client.onChannelRetuned(channelUri, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onChannelRetuned", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTracksChanged(List<TvTrackInfo> tracks) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTracksChanged(" + tracks + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onTracksChanged(tracks, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onTracksChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTrackSelected(int type, String trackId) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onTrackSelected(type, trackId, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onTrackSelected", e);
+ }
+ }
+ }
+
+ @Override
+ public void onVideoAvailable() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onVideoAvailable()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onVideoAvailable(sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onVideoAvailable", e);
+ }
+ }
+ }
+
+ @Override
+ public void onVideoUnavailable(int reason) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onVideoUnavailable(reason, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onVideoUnavailable", e);
+ }
+ }
+ }
+
+ @Override
+ public void onContentAllowed() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onContentAllowed()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onContentAllowed(sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onContentAllowed", e);
+ }
+ }
+ }
+
+ @Override
+ public void onContentBlocked(String rating) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onContentBlocked()");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onContentBlocked(rating, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onContentBlocked", e);
+ }
+ }
+ }
+
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+ + ", right=" + right + ", bottom=" + bottom + ",)");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onLayoutSurface", e);
+ }
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String eventType, Bundle eventArgs) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+ }
+ if (sessionState.session == null || sessionState.client == null) {
+ return;
+ }
+ try {
+ sessionState.client.onSessionEvent(eventType, eventArgs,
+ sessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionEvent", e);
+ }
+ }
+ }
+ }
+
+ private static final class WatchLogHandler extends Handler {
// There are only two kinds of watch events that can happen on the system:
// 1. The current TV input session is tuned to a new channel.
// 2. The session is released for some reason.
@@ -2072,8 +2075,11 @@
private static final int MSG_LOG_WATCH_START = 1;
private static final int MSG_LOG_WATCH_END = 2;
- public WatchLogHandler(Looper looper) {
+ private final ContentResolver mContentResolver;
+
+ public WatchLogHandler(ContentResolver contentResolver, Looper looper) {
super(looper);
+ mContentResolver = contentResolver;
}
@Override
@@ -2159,7 +2165,7 @@
}
}
- final class HardwareListener implements TvInputHardwareManager.Listener {
+ private final class HardwareListener implements TvInputHardwareManager.Listener {
@Override
public void onStateChanged(String inputId, int state) {
synchronized (mLock) {
@@ -2173,9 +2179,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHardwareAdded(info);
+ serviceState.service.notifyHardwareAdded(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
@@ -2189,9 +2195,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHardwareRemoved(info);
+ serviceState.service.notifyHardwareRemoved(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareRemoved", e);
}
@@ -2205,9 +2211,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+ serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
@@ -2221,9 +2227,9 @@
UserState userState = getUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+ if (!serviceState.isHardware || serviceState.service == null) continue;
try {
- serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
+ serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ef74205..69c9144 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -114,6 +114,10 @@
transformation.clear();
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
hasTransformation = true;
+
+ if (!mAppToken.appFullscreen) {
+ anim.setBackgroundColor(0);
+ }
}
public void setDummyAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 38433ae..4b7dd08 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -754,7 +754,11 @@
final boolean isHwAccelerated = (attrs.flags &
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
- if (!PixelFormat.formatHasAlpha(attrs.format)) {
+ if (!PixelFormat.formatHasAlpha(attrs.format)
+ && attrs.surfaceInsets.left == 0
+ && attrs.surfaceInsets.top == 0
+ && attrs.surfaceInsets.right == 0
+ && attrs.surfaceInsets.bottom == 0) {
flags |= SurfaceControl.OPAQUE;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ffadcf2..564a3df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3740,6 +3740,7 @@
if (!mHasFeature) {
return;
}
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
// Check for permissions
if (who == null) {
@@ -3753,7 +3754,7 @@
try {
mUserManager.setUserEnabled(userId);
Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
- intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
+ intent.putExtra(Intent.EXTRA_USER, new UserHandle(userHandle));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
Intent.FLAG_RECEIVER_FOREGROUND);
// TODO This should send to parent of profile (which is always owner at the moment).
@@ -3940,6 +3941,8 @@
@Override
public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
ComponentName activity) {
+ final int userHandle = UserHandle.getCallingUserId();
+
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3949,7 +3952,7 @@
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- pm.addPersistentPreferredActivity(filter, activity, UserHandle.getCallingUserId());
+ pm.addPersistentPreferredActivity(filter, activity, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
@@ -3960,6 +3963,8 @@
@Override
public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
+ final int userHandle = UserHandle.getCallingUserId();
+
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3969,7 +3974,7 @@
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- pm.clearPackagePersistentPreferredActivities(packageName, UserHandle.getCallingUserId());
+ pm.clearPackagePersistentPreferredActivities(packageName, userHandle);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
@@ -4595,7 +4600,6 @@
@Override
public void setUserRestriction(ComponentName who, String key, boolean enabled) {
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
-
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -4606,13 +4610,28 @@
if (!isDeviceOwner && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
throw new SecurityException("Profile owners cannot set user restriction " + key);
}
+ boolean alreadyRestricted = mUserManager.hasUserRestriction(key, userHandle);
+ IAudioService iAudioService = null;
+ if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)
+ || UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ iAudioService = IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
+ }
+
+ if (enabled && !alreadyRestricted) {
+ try {
+ if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ iAudioService.setMicrophoneMute(true, who.getPackageName());
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ iAudioService.setMasterMute(true, 0, who.getPackageName(), null);
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
+ }
+ }
long id = Binder.clearCallingIdentity();
try {
- AudioManager audioManager =
- (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
-
if (enabled && !alreadyRestricted) {
if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -4643,26 +4662,23 @@
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
userHandle.getIdentifier());
- } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- audioManager.setMicrophoneMute(true);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- audioManager.setMasterMute(true);
}
}
-
mUserManager.setUserRestriction(key, enabled, userHandle);
-
- if (!enabled && alreadyRestricted) {
- if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
- audioManager.setMicrophoneMute(false);
- } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- audioManager.setMasterMute(false);
- }
- }
-
} finally {
restoreCallingIdentity(id);
}
+ if (!enabled && alreadyRestricted) {
+ try {
+ if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ iAudioService.setMicrophoneMute(false, who.getPackageName());
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ iAudioService.setMasterMute(false, 0, who.getPackageName(), null);
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
+ }
+ }
}
}
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index ecb0d4b..7c596c1 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -89,6 +89,7 @@
private final int mCallerDisplayNamePresentation;
private final PhoneAccountHandle mAccountHandle;
private final int mCallCapabilities;
+ private final int mCallProperties;
private final int mDisconnectCauseCode;
private final String mDisconnectCauseMessage;
private final long mConnectTimeMillis;
@@ -145,6 +146,14 @@
}
/**
+ * @return A bitmask of the properties of the {@code Call}, as defined in
+ * {@link CallProperties}.
+ */
+ public int getCallProperties() {
+ return mCallProperties;
+ }
+
+ /**
* @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
* as a code chosen from among those declared in {@link DisconnectCause}.
*/
@@ -210,6 +219,7 @@
d.mCallerDisplayNamePresentation) &&
Objects.equals(mAccountHandle, d.mAccountHandle) &&
Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
+ Objects.equals(mCallProperties, d.mCallProperties) &&
Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
Objects.equals(mDisconnectCauseMessage, d.mDisconnectCauseMessage) &&
Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
@@ -230,6 +240,7 @@
Objects.hashCode(mCallerDisplayNamePresentation) +
Objects.hashCode(mAccountHandle) +
Objects.hashCode(mCallCapabilities) +
+ Objects.hashCode(mCallProperties) +
Objects.hashCode(mDisconnectCauseCode) +
Objects.hashCode(mDisconnectCauseMessage) +
Objects.hashCode(mConnectTimeMillis) +
@@ -247,6 +258,7 @@
int callerDisplayNamePresentation,
PhoneAccountHandle accountHandle,
int capabilities,
+ int properties,
int disconnectCauseCode,
String disconnectCauseMessage,
long connectTimeMillis,
@@ -260,6 +272,7 @@
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
mAccountHandle = accountHandle;
mCallCapabilities = capabilities;
+ mCallProperties = properties;
mDisconnectCauseCode = disconnectCauseCode;
mDisconnectCauseMessage = disconnectCauseMessage;
mConnectTimeMillis = connectTimeMillis;
@@ -642,6 +655,7 @@
parcelableCall.getCallerDisplayNamePresentation(),
parcelableCall.getAccountHandle(),
parcelableCall.getCapabilities(),
+ parcelableCall.getProperties(),
parcelableCall.getDisconnectCauseCode(),
parcelableCall.getDisconnectCauseMsg(),
parcelableCall.getConnectTimeMillis(),
diff --git a/telecomm/java/android/telecomm/CallProperties.java b/telecomm/java/android/telecomm/CallProperties.java
new file mode 100644
index 0000000..90eb0cb
--- /dev/null
+++ b/telecomm/java/android/telecomm/CallProperties.java
@@ -0,0 +1,26 @@
+/*
+ * 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.telecomm;
+
+/**
+ * Defines properties of a phone call which may be affected by changes to the call.
+ * @hide
+ */
+public class CallProperties {
+ /** Call is currently in a conference call. */
+ public static final int CONFERENCE = 0x00000001;
+}
diff --git a/telecomm/java/android/telecomm/ParcelableCall.java b/telecomm/java/android/telecomm/ParcelableCall.java
index 8098b94..a2aa192 100644
--- a/telecomm/java/android/telecomm/ParcelableCall.java
+++ b/telecomm/java/android/telecomm/ParcelableCall.java
@@ -40,6 +40,7 @@
private final String mDisconnectCauseMsg;
private final List<String> mCannedSmsResponses;
private final int mCapabilities;
+ private final int mProperties;
private final long mConnectTimeMillis;
private final Uri mHandle;
private final int mHandlePresentation;
@@ -63,6 +64,7 @@
String disconnectCauseMsg,
List<String> cannedSmsResponses,
int capabilities,
+ int properties,
long connectTimeMillis,
Uri handle,
int handlePresentation,
@@ -83,6 +85,7 @@
mDisconnectCauseMsg = disconnectCauseMsg;
mCannedSmsResponses = cannedSmsResponses;
mCapabilities = capabilities;
+ mProperties = properties;
mConnectTimeMillis = connectTimeMillis;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -137,6 +140,9 @@
return mCapabilities;
}
+ /** Bitmask of properties of the call. */
+ public int getProperties() { return mProperties; }
+
/** The time that the call switched to the active state. */
public long getConnectTimeMillis() {
return mConnectTimeMillis;
@@ -246,6 +252,7 @@
List<String> cannedSmsResponses = new ArrayList<>();
source.readList(cannedSmsResponses, classLoader);
int capabilities = source.readInt();
+ int properties = source.readInt();
long connectTimeMillis = source.readLong();
Uri handle = source.readParcelable(classLoader);
int handlePresentation = source.readInt();
@@ -264,10 +271,10 @@
source.readList(conferenceableCallIds, classLoader);
Bundle extras = source.readParcelable(classLoader);
return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
- cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
- callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
- accountHandle, videoCallProvider, parentCallId, childCallIds, statusHints,
- videoState, conferenceableCallIds, extras);
+ cannedSmsResponses, capabilities, properties, connectTimeMillis, handle,
+ handlePresentation, callerDisplayName, callerDisplayNamePresentation,
+ gatewayInfo, accountHandle, videoCallProvider, parentCallId, childCallIds,
+ statusHints, videoState, conferenceableCallIds, extras);
}
@Override
@@ -291,6 +298,7 @@
destination.writeString(mDisconnectCauseMsg);
destination.writeList(mCannedSmsResponses);
destination.writeInt(mCapabilities);
+ destination.writeInt(mProperties);
destination.writeLong(mConnectTimeMillis);
destination.writeParcelable(mHandle, 0);
destination.writeInt(mHandlePresentation);
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 411f48c..97a7b9d 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -93,7 +93,7 @@
private CharSequence mLabel;
private CharSequence mShortDescription;
- private Builder() {}
+ public Builder() {}
public Builder withAccountHandle(PhoneAccountHandle value) {
this.mAccountHandle = value;
diff --git a/telecomm/java/android/telecomm/PhoneCapabilities.java b/telecomm/java/android/telecomm/PhoneCapabilities.java
index 45168d5..0c6a1ef 100644
--- a/telecomm/java/android/telecomm/PhoneCapabilities.java
+++ b/telecomm/java/android/telecomm/PhoneCapabilities.java
@@ -19,7 +19,6 @@
/**
* Defines capabilities a phone call can support, such as conference calling and video telephony.
* Also defines properties of a phone call, such as whether it is using VoLTE technology.
-
*/
public final class PhoneCapabilities {
/** Call can currently be put on hold or unheld. */
diff --git a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
index 42c77d7..0ab7564 100644
--- a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
+++ b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
@@ -20,6 +20,8 @@
/**
* Simple response callback object.
+ *
+ * {@hide}
*/
oneway interface RemoteServiceCallback {
void onError();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 71b796a..cdee3de 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3105,13 +3105,13 @@
/** @hide */
@SystemApi
- public List<String> getCarrierPackageNamesForBroadcastIntent(Intent intent) {
+ public List<String> getCarrierPackageNamesForIntent(Intent intent) {
try {
- return getITelephony().getCarrierPackageNamesForBroadcastIntent(intent);
+ return getITelephony().getCarrierPackageNamesForIntent(intent);
} catch (RemoteException ex) {
- Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent RemoteException", ex);
+ Rlog.e(TAG, "getCarrierPackageNamesForIntent RemoteException", ex);
} catch (NullPointerException ex) {
- Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent NPE", ex);
+ Rlog.e(TAG, "getCarrierPackageNamesForIntent NPE", ex);
}
return null;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b1c3c4a..5c3dcdb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -710,12 +710,12 @@
* Returns the package name of the carrier apps that should handle the input intent.
*
* @param packageManager PackageManager for getting receivers.
- * @param intent Intent that will be broadcast.
+ * @param intent Intent that will be sent.
* @return list of carrier app package names that can handle the intent.
* Returns null if there is an error and an empty list if there
* are no matching packages.
*/
- List<String> getCarrierPackageNamesForBroadcastIntent(in Intent intent);
+ List<String> getCarrierPackageNamesForIntent(in Intent intent);
/**
* Set whether Android should display a simplified Mobile Network Settings UI.
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index ea0db56..e03b9c8 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -366,15 +366,11 @@
if (recent.id >= 0) {
// Stack on top.
intent.putExtra(DocActivity.LABEL, "Stacked");
- task.startActivity(ActivityTestMain.this, intent, null);
} else {
// Start root activity.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
intent.putExtra(DocActivity.LABEL, "New Root");
- task.startActivity(ActivityTestMain.this, intent, null);
}
+ task.startActivity(ActivityTestMain.this, intent, null);
}
return true;
}
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
index 526ea5d..50633db 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.media.browse.MediaBrowserService;
+import android.service.media.MediaBrowserService;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
index 2fc77dc..64602d52 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -22,8 +22,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.browse.MediaBrowser;
-import android.media.browse.MediaBrowserItem;
-import android.media.browse.MediaBrowserService;
+import android.service.media.MediaBrowserService;
import android.os.Bundle;
import android.net.Uri;
import android.support.v4.app.FragmentActivity;
@@ -57,9 +56,9 @@
private MediaBrowser mBrowser;
private static class Item {
- final MediaBrowserItem media;
+ final MediaBrowser.MediaItem media;
- Item(MediaBrowserItem m) {
+ Item(MediaBrowser.MediaItem m) {
this.media = m;
}
}
@@ -103,13 +102,13 @@
final Item item = mItems.get(position);
Log.i("BrowserListFragment", "Item clicked: " + position + " -- "
- + mAdapter.getItem(position).media.getUri());
+ + mAdapter.getItem(position).media.getDescription().getIconUri());
final BrowserListFragment fragment = new BrowserListFragment();
final Bundle args = new Bundle();
args.putParcelable(BrowserListFragment.ARG_COMPONENT, mComponent);
- args.putParcelable(BrowserListFragment.ARG_URI, item.media.getUri());
+ args.putParcelable(BrowserListFragment.ARG_URI, item.media.getDescription().getIconUri());
fragment.setArguments(args);
getFragmentManager().beginTransaction()
@@ -130,7 +129,8 @@
}
mBrowser.subscribe(mUri, new MediaBrowser.SubscriptionCallback() {
@Override
- public void onChildrenLoaded(Uri parentUri, List<MediaBrowserItem> children) {
+ public void onChildrenLoaded(Uri parentUri,
+ List<MediaBrowser.MediaItem> children) {
Log.d(TAG, "onChildrenLoaded parentUri=" + parentUri
+ " children= " + children);
mItems.clear();
@@ -197,7 +197,7 @@
final TextView tv = (TextView)convertView;
final Item item = mItems.get(position);
- tv.setText(item.media.getTitle());
+ tv.setText(item.media.getDescription().getTitle());
return convertView;
}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
index 937f1e6..845db6c 100644
--- a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
@@ -25,13 +25,14 @@
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.media.MediaDescription;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
-import android.media.browse.MediaBrowserItem;
-import android.media.browse.MediaBrowserService;
-import android.media.browse.MediaBrowserService.BrowserRoot;
+import android.media.browse.MediaBrowser;
+import android.service.media.MediaBrowserService;
+import android.service.media.MediaBrowserService.BrowserRoot;
import android.media.session.MediaSession;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -122,17 +123,19 @@
@Override
public void onLoadChildren(final Uri parentUri,
- final Result<List<MediaBrowserItem>> result) {
+ final Result<List<MediaBrowser.MediaItem>> result) {
new Handler().postDelayed(new Runnable() {
public void run() {
- final ArrayList<MediaBrowserItem> list = new ArrayList();
+ final ArrayList<MediaBrowser.MediaItem> list = new ArrayList();
for (int i=0; i<10; i++) {
- list.add(new MediaBrowserItem.Builder(
- Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
- MediaBrowserItem.FLAG_BROWSABLE, "Title " + i)
- .setSummary("Summary " + i)
- .build());
+ MediaDescription.Builder bob = new MediaDescription.Builder();
+ bob.setTitle("Title " + i);
+ bob.setSubtitle("Summary " + i);
+ bob.setMediaId(Uri.withAppendedPath(BASE_URI,
+ Integer.toString(i)).toString());
+ list.add(new MediaBrowser.MediaItem(MediaBrowser.MediaItem.FLAG_BROWSABLE,
+ bob.build()));
}
result.sendResult(list);
@@ -141,11 +144,6 @@
result.detach();
}
- @Override
- public void onLoadIcon(Uri uri, int width, int height, Result<Bitmap> result) {
- result.sendResult(null);
- }
-
/*
@Override
public void query(final Query query, final IMetadataResultHandler metadataResultHandler,
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
index a5bcda5..d1172ac 100644
--- a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -12,6 +12,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
+import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -185,10 +186,10 @@
text = "Empty metadata!";
art = null;
} else {
- MediaMetadata.Description description = mMetadata.getDescription();
+ MediaDescription description = mMetadata.getDescription();
title = description.getTitle();
text = description.getSubtitle();
- art = description.getIcon();
+ art = description.getIconBitmap();
}
String playPauseLabel = "";
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index b6591bd..c08c1a3 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.os.Bundle;
import android.text.format.DateUtils;
-import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -36,6 +35,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Map;
public class UsageStatsActivity extends ListActivity {
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
@@ -84,7 +84,7 @@
private void updateAdapter() {
long now = System.currentTimeMillis();
long beginTime = now - USAGE_STATS_PERIOD;
- ArrayMap<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
+ Map<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
beginTime, now);
mAdapter.update(stats);
}
@@ -92,17 +92,13 @@
private class Adapter extends BaseAdapter {
private ArrayList<UsageStats> mStats = new ArrayList<>();
- public void update(ArrayMap<String, UsageStats> stats) {
+ public void update(Map<String, UsageStats> stats) {
mStats.clear();
if (stats == null) {
return;
}
- final int packageCount = stats.size();
- for (int i = 0; i < packageCount; i++) {
- mStats.add(stats.valueAt(i));
- }
-
+ mStats.addAll(stats.values());
Collections.sort(mStats, mComparator);
notifyDataSetChanged();
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index e1a579c..1f01461 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -92,8 +92,7 @@
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
- Intent enroll = mHotwordDetector.getManageIntent(
- AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
+ Intent enroll = mHotwordDetector.createIntentToEnroll();
Log.i(TAG, "Need to enroll with " + enroll);
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index b44e2d1..117fc24 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1594,6 +1594,11 @@
return mIncludedAssets.getResources(false);
}
+AssetManager& AaptAssets::getAssetManager()
+{
+ return mIncludedAssets;
+}
+
void AaptAssets::print(const String8& prefix) const
{
String8 innerPrefix(prefix);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 0c2576a..3fc9f81 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -561,6 +561,7 @@
status_t buildIncludedResources(Bundle* bundle);
status_t addIncludedResources(const sp<AaptFile>& file);
const ResTable& getIncludedResources() const;
+ AssetManager& getAssetManager();
void print(const String8& prefix) const;
diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp
new file mode 100644
index 0000000..708e405
--- /dev/null
+++ b/tools/aapt/AaptXml.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include "AaptXml.h"
+
+using namespace android;
+
+namespace AaptXml {
+
+static String8 getStringAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+ String8* outError) {
+ Res_value value;
+ if (tree.getAttributeValue(attrIndex, &value) < 0) {
+ if (outError != NULL) {
+ *outError = "could not find attribute at index";
+ }
+ return String8();
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
+ return String8();
+ }
+
+ size_t len;
+ const uint16_t* str = tree.getAttributeStringValue(attrIndex, &len);
+ return str ? String8(str, len) : String8();
+}
+
+static int32_t getIntegerAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+ int32_t defValue, String8* outError) {
+ Res_value value;
+ if (tree.getAttributeValue(attrIndex, &value) < 0) {
+ if (outError != NULL) {
+ *outError = "could not find attribute at index";
+ }
+ return defValue;
+ }
+
+ if (value.dataType < Res_value::TYPE_FIRST_INT
+ || value.dataType > Res_value::TYPE_LAST_INT) {
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
+ return defValue;
+ }
+ return value.data;
+}
+
+
+ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) {
+ size_t attrCount = tree.getAttributeCount();
+ for (size_t i = 0; i < attrCount; i++) {
+ if (tree.getAttributeNameResID(i) == attrRes) {
+ return (ssize_t)i;
+ }
+ }
+ return -1;
+}
+
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
+ const char* attr, String8* outError) {
+ ssize_t idx = tree.indexOfAttribute(ns, attr);
+ if (idx < 0) {
+ return String8();
+ }
+ return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return String8();
+ }
+ return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return String8();
+ }
+ Res_value value;
+ if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+ if (value.dataType == Res_value::TYPE_STRING) {
+ size_t len;
+ const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+ return str ? String8(str, len) : String8();
+ }
+ resTable.resolveReference(&value, 0);
+ if (value.dataType != Res_value::TYPE_STRING) {
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
+ return String8();
+ }
+ }
+ size_t len;
+ const Res_value* value2 = &value;
+ const char16_t* str = resTable.valueToString(value2, 0, NULL, &len);
+ return str ? String8(str, len) : String8();
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, const char* ns,
+ const char* attr, int32_t defValue, String8* outError) {
+ ssize_t idx = tree.indexOfAttribute(ns, attr);
+ if (idx < 0) {
+ return defValue;
+ }
+ return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, int32_t defValue,
+ String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return defValue;
+ }
+ return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getResolvedIntegerAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, int32_t defValue, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return defValue;
+ }
+ Res_value value;
+ if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ resTable.resolveReference(&value, 0);
+ }
+ if (value.dataType < Res_value::TYPE_FIRST_INT
+ || value.dataType > Res_value::TYPE_LAST_INT) {
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
+ return defValue;
+ }
+ }
+ return value.data;
+}
+
+void getResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, Res_value* outValue, String8* outError) {
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ if (outError != NULL) {
+ *outError = "attribute could not be found";
+ }
+ return;
+ }
+ if (tree.getAttributeValue(idx, outValue) != NO_ERROR) {
+ if (outValue->dataType == Res_value::TYPE_REFERENCE) {
+ resTable.resolveReference(outValue, 0);
+ }
+ // The attribute was found and was resolved if need be.
+ return;
+ }
+ if (outError != NULL) {
+ *outError = "error getting resolved resource attribute";
+ }
+}
+
+} // namespace AaptXml
diff --git a/tools/aapt/AaptXml.h b/tools/aapt/AaptXml.h
new file mode 100644
index 0000000..16977f3
--- /dev/null
+++ b/tools/aapt/AaptXml.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAPT_XML_H
+#define __AAPT_XML_H
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+/**
+ * Utility methods for dealing with ResXMLTree.
+ */
+namespace AaptXml {
+
+/**
+ * Returns the index of the attribute, or < 0 if it was not found.
+ */
+ssize_t indexOfAttribute(const android::ResXMLTree& tree, uint32_t attrRes);
+
+/**
+ * Returns the string value for the specified attribute.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, android::String8* outError = NULL);
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+ const char* attr, android::String8* outError) {
+ return getIntegerAttribute(tree, ns, attr, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError) {
+ return getIntegerAttribute(tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes, int32_t defValue = -1,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+inline int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError) {
+ return getResolvedIntegerAttribute(resTable, tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string may be a resource in the supplied ResTable.
+ */
+android::String8 getResolvedAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes,
+ android::String8* outError = NULL);
+
+/**
+ * Returns the resource for the specified attribute in the outValue parameter.
+ * The resource may be a resource in the supplied ResTable.
+ */
+void getResolvedResourceAttribute(const android::ResTable& resTable,
+ const android::ResXMLTree& tree, uint32_t attrRes, android::Res_value* outValue,
+ android::String8* outError = NULL);
+
+} // namespace AaptXml
+
+#endif // __AAPT_XML_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 4ce5045..2cbabe1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -28,6 +28,7 @@
AaptAssets.cpp \
AaptConfig.cpp \
AaptUtil.cpp \
+ AaptXml.cpp \
ApkBuilder.cpp \
Command.cpp \
CrunchCache.cpp \
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index af49461..9bed899 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -130,6 +130,10 @@
void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+ const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
+ void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
+ const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
+ void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; }
bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -323,6 +327,8 @@
const char* mSingleCrunchInputFile;
const char* mSingleCrunchOutputFile;
bool mBuildSharedLibrary;
+ android::String8 mPlatformVersionCode;
+ android::String8 mPlatformVersionName;
/* file specification */
int mArgc;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index a0f0a08..27e60f3 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
//
// Android Asset Packaging Tool main entry point.
//
+#include "AaptXml.h"
#include "ApkBuilder.h"
#include "Bundle.h"
#include "Images.h"
@@ -241,162 +242,17 @@
return result;
}
-static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
-{
- size_t N = tree.getAttributeCount();
- for (size_t i=0; i<N; i++) {
- if (tree.getAttributeNameResID(i) == attrRes) {
- return (ssize_t)i;
- }
- }
- return -1;
-}
-
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
- const char* attr, String8* outError)
-{
- ssize_t idx = tree.indexOfAttribute(ns, attr);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
- String8* outError, int32_t defValue = -1)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType < Res_value::TYPE_FIRST_INT
- || value.dataType > Res_value::TYPE_LAST_INT) {
- if (outError != NULL) {
- *outError = "attribute is not an integer value";
- }
- return defValue;
- }
- }
- return value.data;
-}
-
-static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError, int32_t defValue = -1)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- resTable->resolveReference(&value, 0);
- }
- if (value.dataType < Res_value::TYPE_FIRST_INT
- || value.dataType > Res_value::TYPE_LAST_INT) {
- if (outError != NULL) {
- *outError = "attribute is not an integer value";
- }
- return defValue;
- }
- }
- return value.data;
-}
-
-static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_STRING) {
- size_t len;
- const uint16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
- }
- resTable->resolveReference(&value, 0);
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) {
- *outError = "attribute is not a string value";
- }
- return String8();
- }
- }
- size_t len;
- const Res_value* value2 = &value;
- const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
- return str ? String8(str, len) : String8();
-}
-
-static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
- const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- if (outError != NULL) {
- *outError = "attribute could not be found";
- }
- return;
- }
- if (tree.getAttributeValue(idx, value) != NO_ERROR) {
- if (value->dataType == Res_value::TYPE_REFERENCE) {
- resTable->resolveReference(value, 0);
- }
- // The attribute was found and was resolved if need be.
- return;
- }
- if (outError != NULL) {
- *outError = "error getting resolved resource attribute";
- }
-}
-
-static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
+static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
uint32_t attrRes, String8 attrLabel, String8* outError)
{
Res_value value;
- getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
+ AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
if (*outError != "") {
*outError = "error print resolved resource attribute";
return;
}
if (value.dataType == Res_value::TYPE_STRING) {
- String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
+ String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
printf("%s='%s'", attrLabel.string(),
ResTable::normalizeForOutput(result.string()).string());
} else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
@@ -488,10 +344,10 @@
}
String8 tag(ctag16);
if (tag == "screen") {
- int32_t screenSize = getIntegerAttribute(tree,
- SCREEN_SIZE_ATTR, NULL, -1);
- int32_t screenDensity = getIntegerAttribute(tree,
- SCREEN_DENSITY_ATTR, NULL, -1);
+ int32_t screenSize = AaptXml::getIntegerAttribute(tree,
+ SCREEN_SIZE_ATTR);
+ int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
+ SCREEN_DENSITY_ATTR);
if (screenSize > 0 && screenDensity > 0) {
if (!first) {
printf(",");
@@ -577,7 +433,7 @@
}
} else if (depth == 2 && withinApduService) {
if (tag == "aid-group") {
- String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+ String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
if (error != "") {
if (outError != NULL) *outError = error;
return Vector<String8>();
@@ -876,11 +732,11 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- String8 pkg = getAttribute(tree, NULL, "package", NULL);
+ String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
} else if (depth == 2 && tag == "permission") {
String8 error;
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
@@ -889,14 +745,14 @@
ResTable::normalizeForOutput(name.string()).string());
} else if (depth == 2 && tag == "uses-permission") {
String8 error;
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
}
printUsesPermission(name,
- getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
- getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
}
}
} else if (strcmp("badging", option) == 0) {
@@ -1151,12 +1007,14 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- pkg = getAttribute(tree, NULL, "package", NULL);
+ pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: name='%s' ",
ResTable::normalizeForOutput(pkg.string()).string());
- int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+ int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
+ error.string());
goto bail;
}
if (versionCode > 0) {
@@ -1164,23 +1022,29 @@
} else {
printf("versionCode='' ");
}
- String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
+ String8 versionName = AaptXml::getResolvedAttribute(res, tree,
+ VERSION_NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
+ error.string());
goto bail;
}
printf("versionName='%s'",
ResTable::normalizeForOutput(versionName.string()).string());
- String8 splitName = getAttribute(tree, NULL, "split", NULL);
+ String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
if (!splitName.isEmpty()) {
printf(" split='%s'", ResTable::normalizeForOutput(
splitName.string()).string());
}
+
+ String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+ "platformBuildVersionName");
+ printf(" platformBuildVersionName='%s'", platformVersionName.string());
printf("\n");
- int32_t installLocation = getResolvedIntegerAttribute(&res, tree,
- INSTALL_LOCATION_ATTR, &error, -1);
+ int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
+ INSTALL_LOCATION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
error.string());
@@ -1215,7 +1079,8 @@
for (size_t i=0; i<NL; i++) {
const char* localeStr = locales[i].string();
assets.setLocale(localeStr != NULL ? localeStr : "");
- String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
if (llabel != "") {
if (localeStr == NULL || strlen(localeStr) == 0) {
label = llabel;
@@ -1236,7 +1101,8 @@
for (size_t i=0; i<ND; i++) {
tmpConfig.density = densities[i];
assets.setConfiguration(tmpConfig);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+ &error);
if (icon != "") {
printf("application-icon-%d:'%s'\n", densities[i],
ResTable::normalizeForOutput(icon.string()).string());
@@ -1244,14 +1110,17 @@
}
assets.setConfiguration(config);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
+ error.string());
goto bail;
}
- int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
+ int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
+ error.string());
goto bail;
}
printf("application: label='%s' ",
@@ -1261,9 +1130,11 @@
printf("testOnly='%d'\n", testOnly);
}
- int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
+ int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
+ DEBUGGABLE_ATTR, 0, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
+ error.string());
goto bail;
}
if (debuggable != 0) {
@@ -1284,10 +1155,11 @@
}
}
} else if (tag == "uses-sdk") {
- int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+ int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree,
+ MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
error.string());
@@ -1300,14 +1172,15 @@
targetSdk = code;
printf("sdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
+ code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
if (code != -1) {
printf("maxSdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
+ code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree,
+ TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
error.string());
@@ -1323,16 +1196,16 @@
printf("targetSdkVersion:'%d'\n", code);
}
} else if (tag == "uses-configuration") {
- int32_t reqTouchScreen = getIntegerAttribute(tree,
- REQ_TOUCH_SCREEN_ATTR, NULL, 0);
- int32_t reqKeyboardType = getIntegerAttribute(tree,
- REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
- int32_t reqHardKeyboard = getIntegerAttribute(tree,
- REQ_HARD_KEYBOARD_ATTR, NULL, 0);
- int32_t reqNavigation = getIntegerAttribute(tree,
- REQ_NAVIGATION_ATTR, NULL, 0);
- int32_t reqFiveWayNav = getIntegerAttribute(tree,
- REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
+ int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
+ REQ_TOUCH_SCREEN_ATTR, 0);
+ int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
+ REQ_KEYBOARD_TYPE_ATTR, 0);
+ int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
+ REQ_HARD_KEYBOARD_ATTR, 0);
+ int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
+ REQ_NAVIGATION_ATTR, 0);
+ int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
+ REQ_FIVE_WAY_NAV_ATTR, 0);
printf("uses-configuration:");
if (reqTouchScreen != 0) {
printf(" reqTouchScreen='%d'", reqTouchScreen);
@@ -1353,26 +1226,26 @@
} else if (tag == "supports-input") {
withinSupportsInput = true;
} else if (tag == "supports-screens") {
- smallScreen = getIntegerAttribute(tree,
- SMALL_SCREEN_ATTR, NULL, 1);
- normalScreen = getIntegerAttribute(tree,
- NORMAL_SCREEN_ATTR, NULL, 1);
- largeScreen = getIntegerAttribute(tree,
- LARGE_SCREEN_ATTR, NULL, 1);
- xlargeScreen = getIntegerAttribute(tree,
- XLARGE_SCREEN_ATTR, NULL, 1);
- anyDensity = getIntegerAttribute(tree,
- ANY_DENSITY_ATTR, NULL, 1);
- requiresSmallestWidthDp = getIntegerAttribute(tree,
- REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
- compatibleWidthLimitDp = getIntegerAttribute(tree,
- COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
- largestWidthLimitDp = getIntegerAttribute(tree,
- LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+ smallScreen = AaptXml::getIntegerAttribute(tree,
+ SMALL_SCREEN_ATTR, 1);
+ normalScreen = AaptXml::getIntegerAttribute(tree,
+ NORMAL_SCREEN_ATTR, 1);
+ largeScreen = AaptXml::getIntegerAttribute(tree,
+ LARGE_SCREEN_ATTR, 1);
+ xlargeScreen = AaptXml::getIntegerAttribute(tree,
+ XLARGE_SCREEN_ATTR, 1);
+ anyDensity = AaptXml::getIntegerAttribute(tree,
+ ANY_DENSITY_ATTR, 1);
+ requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
+ compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
+ largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
} else if (tag == "feature-group") {
withinFeatureGroup = true;
FeatureGroup group;
- group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:label' attribute:"
" %s\n", error.string());
@@ -1381,17 +1254,17 @@
featureGroups.add(group);
} else if (tag == "uses-feature") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 1);
commonFeatures.features.add(name, req);
if (req) {
addParentFeatures(&commonFeatures, name);
}
} else {
- int vers = getIntegerAttribute(tree,
+ int vers = AaptXml::getIntegerAttribute(tree,
GL_ES_VERSION_ATTR, &error);
if (error == "") {
if (vers > commonFeatures.openGLESVersion) {
@@ -1400,7 +1273,7 @@
}
}
} else if (tag == "uses-permission") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
if (name == "android.permission.CAMERA") {
addImpliedFeature(&impliedFeatures, "android.hardware.camera",
@@ -1478,15 +1351,15 @@
}
printUsesPermission(name,
- getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
- getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
} else if (tag == "uses-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("uses-package:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1496,7 +1369,7 @@
goto bail;
}
} else if (tag == "original-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("original-package:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1506,7 +1379,7 @@
goto bail;
}
} else if (tag == "supports-gl-texture") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("supports-gl-texture:'%s'\n",
ResTable::normalizeForOutput(name.string()).string());
@@ -1524,9 +1397,9 @@
}
depth--;
} else if (tag == "package-verifier") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+ String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
if (publicKey != "" && error == "") {
printf("package-verifier: name='%s' publicKey='%s'\n",
ResTable::normalizeForOutput(name.string()).string(),
@@ -1553,35 +1426,38 @@
if (withinApplication) {
if(tag == "activity") {
withinActivity = true;
- activityName = getAttribute(tree, NAME_ATTR, &error);
+ activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
- activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
error.string());
goto bail;
}
- activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
error.string());
goto bail;
}
- activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
+ activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
error.string());
goto bail;
}
- int32_t orien = getResolvedIntegerAttribute(&res, tree,
+ int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
SCREEN_ORIENTATION_ATTR, &error);
if (error == "") {
if (orien == 0 || orien == 6 || orien == 8) {
@@ -1595,21 +1471,21 @@
}
}
} else if (tag == "uses-library") {
- String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
+ String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
"ERROR getting 'android:name' attribute for uses-library"
" %s\n", error.string());
goto bail;
}
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 1);
printf("uses-library%s:'%s'\n",
req ? "" : "-not-required", ResTable::normalizeForOutput(
libraryName.string()).string());
} else if (tag == "receiver") {
withinReceiver = true;
- receiverName = getAttribute(tree, NAME_ATTR, &error);
+ receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
@@ -1618,7 +1494,8 @@
goto bail;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_DEVICE_ADMIN") {
hasBindDeviceAdminPermission = true;
@@ -1629,7 +1506,7 @@
}
} else if (tag == "service") {
withinService = true;
- serviceName = getAttribute(tree, NAME_ATTR, &error);
+ serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for "
@@ -1637,7 +1514,8 @@
goto bail;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_INPUT_METHOD") {
hasBindInputMethodPermission = true;
@@ -1659,22 +1537,24 @@
} else if (tag == "provider") {
withinProvider = true;
- bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
+ bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
+ EXPORTED_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
" %s\n", error.string());
goto bail;
}
- bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
- GRANT_URI_PERMISSIONS_ATTR, &error);
+ bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
+ res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
" %s\n", error.string());
goto bail;
}
- String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getResolvedAttribute(res, tree,
+ PERMISSION_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
" %s\n", error.string());
@@ -1685,7 +1565,8 @@
permission == "android.permission.MANAGE_DOCUMENTS";
} else if (bundle->getIncludeMetaData() && tag == "meta-data") {
- String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
+ NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for "
"meta-data:%s\n", error.string());
@@ -1693,12 +1574,12 @@
}
printf("meta-data: name='%s' ",
ResTable::normalizeForOutput(metaDataName.string()).string());
- printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
+ printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
&error);
if (error != "") {
// Try looking for a RESOURCE_ATTR
error = "";
- printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
+ printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
String8("resource"), &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:value' or "
@@ -1709,7 +1590,7 @@
}
printf("\n");
} else if (withinSupportsInput && tag == "input-type") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
supportedInput.add(name);
} else {
@@ -1721,12 +1602,13 @@
} else if (withinFeatureGroup && tag == "uses-feature") {
FeatureGroup& top = featureGroups.editTop();
- String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
if (name != "" && error == "") {
top.features.add(name, true);
addParentFeatures(&top, name);
} else {
- int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
+ int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
+ &error);
if (error == "") {
if (vers > top.openGLESVersion) {
top.openGLESVersion = vers;
@@ -1754,7 +1636,7 @@
actCameraSecure = false;
catLauncher = false;
} else if (withinService && tag == "meta-data") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1768,7 +1650,8 @@
offHost = false;
}
- String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+ String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
+ RESOURCE_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:resource' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1797,7 +1680,7 @@
} else if ((depth == 5) && withinIntentFilter) {
String8 action;
if (tag == "action") {
- action = getAttribute(tree, NAME_ATTR, &error);
+ action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
@@ -1849,7 +1732,7 @@
}
if (tag == "category") {
- String8 category = getAttribute(tree, NAME_ATTR, &error);
+ String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
error.string());
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index dd40b20..f24a023b 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -60,9 +60,6 @@
int dumpResources(Bundle* bundle);
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
- const char* attr, String8* outError);
-
status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
FILE* fp, bool includeRaw);
#endif // __MAIN_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7979a1d..5deeca2 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
// Build resource files from raw assets.
//
#include "AaptAssets.h"
+#include "AaptXml.h"
#include "CacheUpdater.h"
#include "CrunchCache.h"
#include "FileFinder.h"
@@ -805,6 +806,20 @@
}
}
+ if (bundle->getPlatformBuildVersionCode() != "") {
+ if (!addTagAttribute(root, "", "platformBuildVersionCode",
+ bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ if (bundle->getPlatformBuildVersionName() != "") {
+ if (!addTagAttribute(root, "", "platformBuildVersionName",
+ bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
if (bundle->getDebugMode()) {
sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
if (application != NULL) {
@@ -881,6 +896,106 @@
return NO_ERROR;
}
+static int32_t getPlatformAssetCookie(const AssetManager& assets) {
+ // Find the system package (0x01). AAPT always generates attributes
+ // with the type 0x01, so we're looking for the first attribute
+ // resource in the system package.
+ const ResTable& table = assets.getResources(true);
+ Res_value val;
+ ssize_t idx = table.getResource(0x01010000, &val, true);
+ if (idx != NO_ERROR) {
+ // Try as a bag.
+ const ResTable::bag_entry* entry;
+ ssize_t cnt = table.lockBag(0x01010000, &entry);
+ if (cnt >= 0) {
+ idx = entry->stringBlock;
+ }
+ table.unlockBag(entry);
+ }
+
+ if (idx < 0) {
+ return 0;
+ }
+ return table.getTableCookie(idx);
+}
+
+enum {
+ VERSION_CODE_ATTR = 0x0101021b,
+ VERSION_NAME_ATTR = 0x0101021c,
+};
+
+static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+ size_t len;
+ ResXMLTree::event_code_t code;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code != ResXMLTree::START_TAG) {
+ continue;
+ }
+
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+ return UNKNOWN_ERROR;
+ }
+
+ String8 tag(ctag16, len);
+ if (tag != "manifest") {
+ continue;
+ }
+
+ String8 error;
+ int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: failed to get platform version code\n");
+ return UNKNOWN_ERROR;
+ }
+
+ if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") {
+ bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
+ }
+
+ String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: failed to get platform version name\n");
+ return UNKNOWN_ERROR;
+ }
+
+ if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
+ bundle->setPlatformBuildVersionName(versionName);
+ }
+ return NO_ERROR;
+ }
+
+ fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n");
+ return UNKNOWN_ERROR;
+}
+
+static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
+ int32_t cookie = getPlatformAssetCookie(assets);
+ if (cookie == 0) {
+ fprintf(stderr, "ERROR: Platform package not found\n");
+ return UNKNOWN_ERROR;
+ }
+
+ ResXMLTree tree;
+ Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
+ if (asset == NULL) {
+ fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
+ return UNKNOWN_ERROR;
+ }
+
+ ssize_t result = NO_ERROR;
+ if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
+ fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
+ result = UNKNOWN_ERROR;
+ } else {
+ result = extractPlatformBuildVersion(tree, bundle);
+ }
+
+ delete asset;
+ return result;
+}
+
#define ASSIGN_IT(n) \
do { \
ssize_t index = resources->indexOfKey(String8(#n)); \
@@ -1356,6 +1471,17 @@
return UNKNOWN_ERROR;
}
+ // If we're not overriding the platform build versions,
+ // extract them from the platform APK.
+ if (packageType != ResourceTable::System &&
+ (bundle->getPlatformBuildVersionCode() == "" ||
+ bundle->getPlatformBuildVersionName() == "")) {
+ err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
+ if (err != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
String8 manifestPath(manifestFile->getPrintableSource());
@@ -2636,13 +2762,14 @@
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
return -1;
}
- pkg = getAttribute(tree, NULL, "package", NULL);
+ pkg = AaptXml::getAttribute(tree, NULL, "package");
} else if (depth == 2) {
if (tag == "application") {
inApplication = true;
keepTag = true;
- String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+ String8 agent = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android",
"backupAgent", &error);
if (agent.length() > 0) {
addProguardKeepRule(keep, agent, pkg.string(),
@@ -2658,8 +2785,8 @@
}
}
if (keepTag) {
- String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
- "name", &error);
+ String8 name = AaptXml::getAttribute(tree,
+ "http://schemas.android.com/apk/res/android", "name", &error);
if (error != "") {
fprintf(stderr, "ERROR: %s\n", error.string());
return -1;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
new file mode 100644
index 0000000..fce4323
--- /dev/null
+++ b/tools/apilint/apilint.py
@@ -0,0 +1,540 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Enforces common Android public API design patterns. It ignores lint messages from
+a previous API level, if provided.
+
+Usage: apilint.py current.txt
+Usage: apilint.py current.txt previous.txt
+"""
+
+import re, sys
+
+
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+
+def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
+ # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
+ codes = []
+ if reset: codes.append("0")
+ else:
+ if not fg is None: codes.append("3%d" % (fg))
+ if not bg is None:
+ if not bright: codes.append("4%d" % (bg))
+ else: codes.append("10%d" % (bg))
+ if bold: codes.append("1")
+ elif dim: codes.append("2")
+ else: codes.append("22")
+ return "\033[%sm" % (";".join(codes))
+
+
+class Field():
+ def __init__(self, clazz, raw):
+ self.clazz = clazz
+ self.raw = raw.strip(" {;")
+
+ raw = raw.split()
+ self.split = list(raw)
+
+ for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
+ while r in raw: raw.remove(r)
+
+ self.typ = raw[0]
+ self.name = raw[1].strip(";")
+ if len(raw) >= 4 and raw[2] == "=":
+ self.value = raw[3].strip(';"')
+ else:
+ self.value = None
+
+ def __repr__(self):
+ return self.raw
+
+
+class Method():
+ def __init__(self, clazz, raw):
+ self.clazz = clazz
+ self.raw = raw.strip(" {;")
+
+ raw = re.split("[\s(),;]+", raw)
+ for r in ["", ";"]:
+ while r in raw: raw.remove(r)
+ self.split = list(raw)
+
+ for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]:
+ while r in raw: raw.remove(r)
+
+ self.typ = raw[0]
+ self.name = raw[1]
+ self.args = []
+ for r in raw[2:]:
+ if r == "throws": break
+ self.args.append(r)
+
+ def __repr__(self):
+ return self.raw
+
+
+class Class():
+ def __init__(self, pkg, raw):
+ self.pkg = pkg
+ self.raw = raw.strip(" {;")
+ self.ctors = []
+ self.fields = []
+ self.methods = []
+
+ raw = raw.split()
+ self.split = list(raw)
+ if "class" in raw:
+ self.fullname = raw[raw.index("class")+1]
+ elif "interface" in raw:
+ self.fullname = raw[raw.index("interface")+1]
+
+ if "." in self.fullname:
+ self.name = self.fullname[self.fullname.rindex(".")+1:]
+ else:
+ self.name = self.fullname
+
+ def __repr__(self):
+ return self.raw
+
+
+class Package():
+ def __init__(self, raw):
+ self.raw = raw.strip(" {;")
+
+ raw = raw.split()
+ self.name = raw[raw.index("package")+1]
+
+ def __repr__(self):
+ return self.raw
+
+
+def parse_api(fn):
+ api = []
+ pkg = None
+ clazz = None
+
+ with open(fn) as f:
+ for raw in f.readlines():
+ raw = raw.rstrip()
+
+ if raw.startswith("package"):
+ pkg = Package(raw)
+ elif raw.startswith(" ") and raw.endswith("{"):
+ clazz = Class(pkg, raw)
+ api.append(clazz)
+ elif raw.startswith(" ctor"):
+ clazz.ctors.append(Method(clazz, raw))
+ elif raw.startswith(" method"):
+ clazz.methods.append(Method(clazz, raw))
+ elif raw.startswith(" field"):
+ clazz.fields.append(Field(clazz, raw))
+
+ return api
+
+
+failures = []
+
+def _fail(clazz, detail, msg):
+ """Records an API failure to be processed later."""
+ global failures
+
+ res = msg
+ if detail is not None:
+ res += "\n in " + repr(detail)
+ res += "\n in " + repr(clazz)
+ res += "\n in " + repr(clazz.pkg)
+ failures.append(res)
+
+def warn(clazz, detail, msg):
+ _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg))
+
+def error(clazz, detail, msg):
+ _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK), format(reset=True), msg))
+
+
+def verify_constants(clazz):
+ """All static final constants must be FOO_NAME style."""
+ if re.match("R\.[a-z]+", clazz.fullname): return
+
+ for f in clazz.fields:
+ if "static" in f.split and "final" in f.split:
+ if re.match("[A-Z0-9_]+", f.name) is None:
+ error(clazz, f, "Constant field names should be FOO_NAME")
+
+
+def verify_enums(clazz):
+ """Enums are bad, mmkay?"""
+ if "extends java.lang.Enum" in clazz.raw:
+ error(clazz, None, "Enums are not allowed")
+
+
+def verify_class_names(clazz):
+ """Try catching malformed class names like myMtp or MTPUser."""
+ if re.search("[A-Z]{2,}", clazz.name) is not None:
+ warn(clazz, None, "Class name style should be Mtp not MTP")
+ if re.match("[^A-Z]", clazz.name):
+ error(clazz, None, "Class must start with uppercase char")
+
+
+def verify_method_names(clazz):
+ """Try catching malformed method names, like Foo() or getMTU()."""
+ if clazz.pkg.name == "android.opengl": return
+
+ for m in clazz.methods:
+ if re.search("[A-Z]{2,}", m.name) is not None:
+ warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
+ if re.match("[^a-z]", m.name):
+ error(clazz, None, "Method name must start with lowercase char")
+
+
+def verify_callbacks(clazz):
+ """Verify Callback classes.
+ All callback classes must be abstract.
+ All methods must follow onFoo() naming style."""
+
+ if clazz.name.endswith("Callbacks"):
+ error(clazz, None, "Class must be named exactly Callback")
+ if clazz.name.endswith("Observer"):
+ warn(clazz, None, "Class should be named Callback")
+
+ if clazz.name.endswith("Callback"):
+ if "interface" in clazz.split:
+ error(clazz, None, "Callback must be abstract class")
+
+ for m in clazz.methods:
+ if not re.match("on[A-Z][a-z]*", m.name):
+ error(clazz, m, "Callback method names must be onFoo style")
+
+
+def verify_listeners(clazz):
+ """Verify Listener classes.
+ All Listener classes must be interface.
+ All methods must follow onFoo() naming style.
+ If only a single method, it must match class name:
+ interface OnFooListener { void onFoo() }"""
+
+ if clazz.name.endswith("Listener"):
+ if " abstract class " in clazz.raw:
+ error(clazz, None, "Listener should be interface")
+
+ for m in clazz.methods:
+ if not re.match("on[A-Z][a-z]*", m.name):
+ error(clazz, m, "Listener method names must be onFoo style")
+
+ if len(clazz.methods) == 1 and clazz.name.startswith("On"):
+ m = clazz.methods[0]
+ if (m.name + "Listener").lower() != clazz.name.lower():
+ error(clazz, m, "Single method name should match class name")
+
+
+def verify_actions(clazz):
+ """Verify intent actions.
+ All action names must be named ACTION_FOO.
+ All action values must be scoped by package and match name:
+ package android.foo {
+ String ACTION_BAR = "android.foo.action.BAR";
+ }"""
+ for f in clazz.fields:
+ if f.value is None: continue
+ if f.name.startswith("EXTRA_"): continue
+
+ if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
+ if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
+ if not f.name.startswith("ACTION_"):
+ error(clazz, f, "Intent action must be ACTION_FOO")
+ else:
+ if clazz.name == "Intent":
+ prefix = "android.intent.action"
+ elif clazz.name == "Settings":
+ prefix = "android.settings"
+ else:
+ prefix = clazz.pkg.name + ".action"
+ expected = prefix + "." + f.name[7:]
+ if f.value != expected:
+ error(clazz, f, "Inconsistent action value")
+
+
+def verify_extras(clazz):
+ """Verify intent extras.
+ All extra names must be named EXTRA_FOO.
+ All extra values must be scoped by package and match name:
+ package android.foo {
+ String EXTRA_BAR = "android.foo.extra.BAR";
+ }"""
+ for f in clazz.fields:
+ if f.value is None: continue
+ if f.name.startswith("ACTION_"): continue
+
+ if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
+ if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
+ if not f.name.startswith("EXTRA_"):
+ error(clazz, f, "Intent extra must be EXTRA_FOO")
+ else:
+ if clazz.name == "Intent":
+ prefix = "android.intent.extra"
+ else:
+ prefix = clazz.pkg.name + ".extra"
+ expected = prefix + "." + f.name[6:]
+ if f.value != expected:
+ error(clazz, f, "Inconsistent extra value")
+
+
+def verify_equals(clazz):
+ """Verify that equals() and hashCode() must be overridden together."""
+ methods = [ m.name for m in clazz.methods ]
+ eq = "equals" in methods
+ hc = "hashCode" in methods
+ if eq != hc:
+ error(clazz, None, "Must override both equals and hashCode")
+
+
+def verify_parcelable(clazz):
+ """Verify that Parcelable objects aren't hiding required bits."""
+ if "implements android.os.Parcelable" in clazz.raw:
+ creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
+ write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
+ describe = [ i for i in clazz.methods if i.name == "describeContents" ]
+
+ if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
+ error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents")
+
+
+def verify_protected(clazz):
+ """Verify that no protected methods are allowed."""
+ for m in clazz.methods:
+ if "protected" in m.split:
+ error(clazz, m, "Protected method")
+ for f in clazz.fields:
+ if "protected" in f.split:
+ error(clazz, f, "Protected field")
+
+
+def verify_fields(clazz):
+ """Verify that all exposed fields are final.
+ Exposed fields must follow myName style.
+ Catch internal mFoo objects being exposed."""
+ for f in clazz.fields:
+ if not "final" in f.split:
+ error(clazz, f, "Bare fields must be final; consider adding accessors")
+
+ if not "static" in f.split:
+ if not re.match("[a-z]([a-zA-Z]+)?", f.name):
+ error(clazz, f, "Non-static fields must be myName")
+
+ if re.match("[m][A-Z]", f.name):
+ error(clazz, f, "Don't expose your internal objects")
+
+
+def verify_register(clazz):
+ """Verify parity of registration methods.
+ Callback objects use register/unregister methods.
+ Listener objects use add/remove methods."""
+ methods = [ m.name for m in clazz.methods ]
+ for m in clazz.methods:
+ if "Callback" in m.raw:
+ if m.name.startswith("register"):
+ other = "unregister" + m.name[8:]
+ if other not in methods:
+ error(clazz, m, "Missing unregister")
+ if m.name.startswith("unregister"):
+ other = "register" + m.name[10:]
+ if other not in methods:
+ error(clazz, m, "Missing register")
+
+ if m.name.startswith("add") or m.name.startswith("remove"):
+ error(clazz, m, "Callback should be register/unregister")
+
+ if "Listener" in m.raw:
+ if m.name.startswith("add"):
+ other = "remove" + m.name[3:]
+ if other not in methods:
+ error(clazz, m, "Missing remove")
+ if m.name.startswith("remove") and not m.name.startswith("removeAll"):
+ other = "add" + m.name[6:]
+ if other not in methods:
+ error(clazz, m, "Missing add")
+
+ if m.name.startswith("register") or m.name.startswith("unregister"):
+ error(clazz, m, "Listener should be add/remove")
+
+
+def verify_sync(clazz):
+ """Verify synchronized methods aren't exposed."""
+ for m in clazz.methods:
+ if "synchronized" in m.split:
+ error(clazz, m, "Lock exposed")
+
+
+def verify_intent_builder(clazz):
+ """Verify that Intent builders are createFooIntent() style."""
+ if clazz.name == "Intent": return
+
+ for m in clazz.methods:
+ if m.typ == "android.content.Intent":
+ if m.name.startswith("create") and m.name.endswith("Intent"):
+ pass
+ else:
+ warn(clazz, m, "Should be createFooIntent()")
+
+
+def verify_helper_classes(clazz):
+ """Verify that helper classes are named consistently with what they extend."""
+ if "extends android.app.Service" in clazz.raw:
+ if not clazz.name.endswith("Service"):
+ error(clazz, None, "Inconsistent class name")
+ if "extends android.content.ContentProvider" in clazz.raw:
+ if not clazz.name.endswith("Provider"):
+ error(clazz, None, "Inconsistent class name")
+ if "extends android.content.BroadcastReceiver" in clazz.raw:
+ if not clazz.name.endswith("Receiver"):
+ error(clazz, None, "Inconsistent class name")
+
+
+def verify_builder(clazz):
+ """Verify builder classes.
+ Methods should return the builder to enable chaining."""
+ if " extends " in clazz.raw: return
+ if not clazz.name.endswith("Builder"): return
+
+ if clazz.name != "Builder":
+ warn(clazz, None, "Should be standalone Builder class")
+
+ has_build = False
+ for m in clazz.methods:
+ if m.name == "build":
+ has_build = True
+ continue
+
+ if m.name.startswith("get"): continue
+ if m.name.startswith("clear"): continue
+
+ if not m.typ.endswith(clazz.fullname):
+ warn(clazz, m, "Should return the builder")
+
+ if not has_build:
+ warn(clazz, None, "Missing build() method")
+
+
+def verify_aidl(clazz):
+ """Catch people exposing raw AIDL."""
+ if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
+ error(clazz, None, "Exposing raw AIDL interface")
+
+
+def verify_internal(clazz):
+ """Catch people exposing internal classes."""
+ if clazz.pkg.name.startswith("com.android"):
+ error(clazz, None, "Exposing internal class")
+
+
+def verify_layering(clazz):
+ """Catch package layering violations.
+ For example, something in android.os depending on android.app."""
+ ranking = [
+ ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
+ "android.app",
+ "android.widget",
+ "android.view",
+ "android.animation",
+ "android.provider",
+ "android.content",
+ "android.database",
+ "android.graphics",
+ "android.text",
+ "android.os",
+ "android.util"
+ ]
+
+ def rank(p):
+ for i in range(len(ranking)):
+ if isinstance(ranking[i], list):
+ for j in ranking[i]:
+ if p.startswith(j): return i
+ else:
+ if p.startswith(ranking[i]): return i
+
+ cr = rank(clazz.pkg.name)
+ if cr is None: return
+
+ for f in clazz.fields:
+ ir = rank(f.typ)
+ if ir and ir < cr:
+ warn(clazz, f, "Field type violates package layering")
+
+ for m in clazz.methods:
+ ir = rank(m.typ)
+ if ir and ir < cr:
+ warn(clazz, m, "Method return type violates package layering")
+ for arg in m.args:
+ ir = rank(arg)
+ if ir and ir < cr:
+ warn(clazz, m, "Method argument type violates package layering")
+
+
+def verify_all(api):
+ global failures
+
+ failures = []
+ for clazz in api:
+ if clazz.pkg.name.startswith("java"): continue
+ if clazz.pkg.name.startswith("junit"): continue
+ if clazz.pkg.name.startswith("org.apache"): continue
+ if clazz.pkg.name.startswith("org.xml"): continue
+ if clazz.pkg.name.startswith("org.json"): continue
+ if clazz.pkg.name.startswith("org.w3c"): continue
+
+ verify_constants(clazz)
+ verify_enums(clazz)
+ verify_class_names(clazz)
+ verify_method_names(clazz)
+ verify_callbacks(clazz)
+ verify_listeners(clazz)
+ verify_actions(clazz)
+ verify_extras(clazz)
+ verify_equals(clazz)
+ verify_parcelable(clazz)
+ verify_protected(clazz)
+ verify_fields(clazz)
+ verify_register(clazz)
+ verify_sync(clazz)
+ verify_intent_builder(clazz)
+ verify_helper_classes(clazz)
+ verify_builder(clazz)
+ verify_aidl(clazz)
+ verify_internal(clazz)
+ verify_layering(clazz)
+
+ return failures
+
+
+cur = parse_api(sys.argv[1])
+cur_fail = verify_all(cur)
+
+if len(sys.argv) > 2:
+ prev = parse_api(sys.argv[2])
+ prev_fail = verify_all(prev)
+
+ # ignore errors from previous API level
+ for p in prev_fail:
+ if p in cur_fail:
+ cur_fail.remove(p)
+
+
+for f in cur_fail:
+ print f
+ print