Merge "Don't add title margins during to Toolbar layout if titles don't fit" 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 43b5d9b..84c175c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3609,8 +3609,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();
@@ -14922,6 +14921,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[]);
@@ -15100,7 +15124,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);
@@ -15128,6 +15152,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";
@@ -15148,14 +15173,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();
@@ -15706,6 +15723,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();
@@ -16241,7 +16259,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);
}
@@ -16253,27 +16270,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);
@@ -16282,37 +16284,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);
}
}
@@ -16407,7 +16382,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();
@@ -16424,7 +16399,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);
@@ -16444,16 +16419,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();
}
@@ -16474,7 +16449,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
@@ -16489,34 +16464,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);
@@ -16537,6 +16505,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();
@@ -16548,15 +16517,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
@@ -16570,6 +16539,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
}
@@ -16580,7 +16550,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);
@@ -27096,6 +27066,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 {
@@ -27106,9 +27103,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);
@@ -27116,13 +27115,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";
}
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/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/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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 07e9a94..214f50c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,6 +61,8 @@
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
+ void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
+ int getInterruptionFilterFromListener(in INotificationListener token);
ComponentName getEffectsSuppressor();
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/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 513d222..d75dfe6 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -721,6 +721,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);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 95d1351..ae11f47 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2113,6 +2113,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
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..a544b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -58,26 +58,28 @@
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;
private INotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
@@ -197,6 +199,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(
@@ -345,15 +358,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 +401,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 +418,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 +575,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/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/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/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/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 add0de3..3b16aba 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -84,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;
@@ -129,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) {
@@ -140,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
@@ -166,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,
@@ -372,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);
@@ -381,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();
@@ -390,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);
@@ -399,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();
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/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/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/values/attrs.xml b/core/res/res/values/attrs.xml
index cf1794b..9edcca2 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
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/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 2bb52c9..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>
@@ -744,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>
@@ -827,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">
@@ -835,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">
@@ -845,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/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..90879dd 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -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/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 72786eb0..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) {
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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d9fa0bc..ecfedf6 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) {
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 3fea688..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() */
@@ -3834,7 +3840,6 @@
@Override
public void handleMessage(Message msg) {
-
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
@@ -3866,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:
@@ -4061,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/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..711831b 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) {
@@ -744,9 +754,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 +770,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 +835,6 @@
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
-
- private void setSession(MediaSession session) {
- mSession = session;
- }
}
/**
@@ -872,10 +879,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 +997,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 +1057,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 +1093,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 +1139,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/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/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/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/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/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/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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 107bb06..0919f77 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3559,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.");
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..1d2f7a9 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();
+ targetStack.moveTaskToFrontLocked(inTask, r, options);
mWindowManager.moveTaskToTop(targetStack.topTask().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/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/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..fc1b746 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -57,7 +57,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 +126,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 +178,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 +807,7 @@
@Override
void onZenModeChanged() {
synchronized(mNotificationList) {
- updateListenerHintsLocked();
+ updateInterruptionFilterLocked();
}
}
});
@@ -938,8 +939,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 +954,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
// ============================================================================
@@ -1318,7 +1325,6 @@
} else {
mListenersDisablingEffects.remove(info);
}
- mZenModeHelper.requestFromListener(hints);
updateListenerHintsLocked();
updateEffectsSuppressorLocked();
}
@@ -1335,6 +1341,29 @@
}
@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 ZenModeConfig getZenModeConfig() {
enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
@@ -2058,12 +2087,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 +2126,9 @@
case MESSAGE_LISTENER_HINTS_CHANGED:
handleListenerHintsChanged(msg.arg1);
break;
+ case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
+ handleListenerInterruptionFilterChanged(msg.arg1);
+ break;
}
}
@@ -2701,6 +2747,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 +2803,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/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0b93690..7a5336b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -115,35 +115,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");
}
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..2cb9077 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
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/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 28cc99f..564a3df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4600,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");
@@ -4611,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(),
@@ -4648,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/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/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/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..fd660bb 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());
}
+
+ int32_t platformVersionCode = AaptXml::getIntegerAttribute(tree, NULL,
+ "platformBuildVersionCode");
+ printf(" platformBuildVersionCode='%d'", platformVersionCode);
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