Merge "Revert "Relaunch activity resumed if it got resumed before relaunching.""
diff --git a/api/current.txt b/api/current.txt
index b7f63bc..da45517 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13375,6 +13375,7 @@
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+ field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13397,6 +13398,7 @@
field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+ field public static final int TYPE_POSE_6DOF = 28; // 0x1c
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -22214,6 +22216,7 @@
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+ method public static final android.net.Uri buildRecordedProgramUri(long);
field public static final java.lang.String AUTHORITY = "android.media.tv";
}
@@ -22350,13 +22353,49 @@
field public static final java.lang.String TRAVEL = "TRAVEL";
}
+ public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+ field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+ field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+ field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+ field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+ field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+ field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+ field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+ field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+ field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+ field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+ field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+ field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public final class TvInputInfo implements android.os.Parcelable {
+ method public boolean canRecord();
method public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
method public java.lang.String getId();
method public java.lang.String getParentId();
method public android.content.pm.ServiceInfo getServiceInfo();
+ method public int getTunerCount();
method public int getType();
method public boolean isPassthroughInput();
method public android.graphics.drawable.Drawable loadIcon(android.content.Context);
@@ -22391,6 +22430,10 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+ field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+ field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -22408,12 +22451,15 @@
method public void onInputAdded(java.lang.String);
method public void onInputRemoved(java.lang.String);
method public void onInputStateChanged(java.lang.String, int);
+ method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
}
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
+ method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
+ method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
}
@@ -22426,6 +22472,18 @@
method public final boolean onSetSurface(android.view.Surface);
}
+ public static abstract class TvInputService.RecordingSession {
+ ctor public TvInputService.RecordingSession(android.content.Context);
+ method public void notifyConnected();
+ method public void notifyError(int);
+ method public void notifyRecordingStarted();
+ method public void notifyRecordingStopped(android.net.Uri);
+ method public abstract void onConnect(android.net.Uri);
+ method public abstract void onDisconnect();
+ method public abstract void onStartRecording();
+ method public abstract void onStopRecording();
+ }
+
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session(android.content.Context);
method public void layoutSurface(int, int, int, int);
@@ -22453,6 +22511,7 @@
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
method public void onTimeShiftPause();
+ method public void onTimeShiftPlay(android.net.Uri);
method public void onTimeShiftResume();
method public void onTimeShiftSeekTo(long);
method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -22463,6 +22522,23 @@
method public void setOverlayViewEnabled(boolean);
}
+ public class TvRecordingClient {
+ ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+ method public void connect(java.lang.String, android.net.Uri);
+ method public void disconnect();
+ method public void startRecording();
+ method public void stopRecording();
+ }
+
+ public class TvRecordingClient.RecordingCallback {
+ ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnected();
+ method public void onDisconnected();
+ method public void onError(int);
+ method public void onRecordingStarted();
+ method public void onRecordingStopped(android.net.Uri);
+ }
+
public final class TvTrackInfo implements android.os.Parcelable {
method public int describeContents();
method public final int getAudioChannelCount();
@@ -22514,6 +22590,7 @@
method public void setStreamVolume(float);
method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void timeShiftPause();
+ method public void timeShiftPlay(java.lang.String, android.net.Uri);
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -31021,6 +31098,7 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
method public static java.lang.String getDocumentId(android.net.Uri);
@@ -31029,6 +31107,7 @@
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -32855,6 +32934,7 @@
method public void getVarV(int, android.renderscript.FieldPacker);
method protected void invoke(int);
method protected void invoke(int, android.renderscript.FieldPacker);
+ method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
method public void setTimeZone(java.lang.String);
method public void setVar(int, float);
method public void setVar(int, double);
diff --git a/api/system-current.txt b/api/system-current.txt
index 55f08c8..302ebe1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13772,6 +13772,7 @@
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+ field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13795,6 +13796,7 @@
field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+ field public static final int TYPE_POSE_6DOF = 28; // 0x1c
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -23641,6 +23643,7 @@
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+ method public static final android.net.Uri buildRecordedProgramUri(long);
method public static final boolean isChannelUriForPassthroughInput(android.net.Uri);
field public static final java.lang.String AUTHORITY = "android.media.tv";
}
@@ -23781,6 +23784,40 @@
field public static final java.lang.String TRAVEL = "TRAVEL";
}
+ public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+ field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+ field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+ field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+ field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+ field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+ field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+ field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+ field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+ field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+ field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+ field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+ field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public static final class TvContract.WatchedPrograms implements android.media.tv.TvContract.BaseTvColumns {
field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
@@ -23829,6 +23866,7 @@
}
public final class TvInputInfo implements android.os.Parcelable {
+ method public boolean canRecord();
method public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -23840,6 +23878,7 @@
method public java.lang.String getId();
method public java.lang.String getParentId();
method public android.content.pm.ServiceInfo getServiceInfo();
+ method public int getTunerCount();
method public int getType();
method public boolean isConnectedToHdmiSwitch();
method public boolean isHardwareInput();
@@ -23874,6 +23913,7 @@
method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
method public void addBlockedRating(android.media.tv.TvContentRating);
method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
+ method public void createRecordingSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
method public void createSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
method public java.util.List<android.media.tv.TvContentRating> getBlockedRatings();
@@ -23897,6 +23937,10 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+ field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+ field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -23961,16 +24005,19 @@
method public void onInputRemoved(java.lang.String);
method public void onInputStateChanged(java.lang.String, int);
method public void onInputUpdated(java.lang.String);
+ method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
}
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
+ method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
method public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
method public java.lang.String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
method public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo);
method public java.lang.String onHdmiDeviceRemoved(android.hardware.hdmi.HdmiDeviceInfo);
+ method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
}
@@ -23983,6 +24030,21 @@
method public final boolean onSetSurface(android.view.Surface);
}
+ public static abstract class TvInputService.RecordingSession {
+ ctor public TvInputService.RecordingSession(android.content.Context);
+ method public void notifyConnected();
+ method public void notifyError(int);
+ method public void notifyRecordingStarted();
+ method public void notifyRecordingStopped(android.net.Uri);
+ method public void notifySessionEvent(java.lang.String, android.os.Bundle);
+ method public void onAppPrivateCommand(java.lang.String, android.os.Bundle);
+ method public abstract void onConnect(android.net.Uri);
+ method public void onConnect(android.net.Uri, android.os.Bundle);
+ method public abstract void onDisconnect();
+ method public abstract void onStartRecording();
+ method public abstract void onStopRecording();
+ }
+
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session(android.content.Context);
method public void layoutSurface(int, int, int, int);
@@ -24013,6 +24075,7 @@
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
method public void onTimeShiftPause();
+ method public void onTimeShiftPlay(android.net.Uri);
method public void onTimeShiftResume();
method public void onTimeShiftSeekTo(long);
method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -24024,6 +24087,26 @@
method public void setOverlayViewEnabled(boolean);
}
+ public class TvRecordingClient {
+ ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+ method public void connect(java.lang.String, android.net.Uri);
+ method public void connect(java.lang.String, android.net.Uri, android.os.Bundle);
+ method public void disconnect();
+ method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
+ method public void startRecording();
+ method public void stopRecording();
+ }
+
+ public class TvRecordingClient.RecordingCallback {
+ ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnected();
+ method public void onDisconnected();
+ method public void onError(int);
+ method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
+ method public void onRecordingStarted();
+ method public void onRecordingStopped(android.net.Uri);
+ }
+
public class TvStreamConfig implements android.os.Parcelable {
method public int describeContents();
method public int getFlags();
@@ -24106,6 +24189,7 @@
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
+ method public void timeShiftPlay(java.lang.String, android.net.Uri);
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -24401,6 +24485,7 @@
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public boolean isTetheringSupported();
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -24413,7 +24498,10 @@
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
+ method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+ method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public void stopTethering(int);
method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void unregisterNetworkCallback(android.app.PendingIntent);
@@ -24435,6 +24523,9 @@
field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TYPE_BLUETOOTH = 7; // 0x7
field public static final int TYPE_DUMMY = 8; // 0x8
field public static final int TYPE_ETHERNET = 9; // 0x9
@@ -24461,6 +24552,12 @@
method public abstract void onNetworkActive();
}
+ public static abstract class ConnectivityManager.OnStartTetheringCallback {
+ ctor public ConnectivityManager.OnStartTetheringCallback();
+ method public void onTetheringFailed();
+ method public void onTetheringStarted();
+ }
+
public class Credentials {
ctor public Credentials(int, int, int);
method public int getGid();
@@ -33115,6 +33212,7 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
method public static java.lang.String getDocumentId(android.net.Uri);
@@ -33123,6 +33221,7 @@
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -35052,6 +35151,7 @@
method public void getVarV(int, android.renderscript.FieldPacker);
method protected void invoke(int);
method protected void invoke(int, android.renderscript.FieldPacker);
+ method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
method public void setTimeZone(java.lang.String);
method public void setVar(int, float);
method public void setVar(int, double);
diff --git a/api/test-current.txt b/api/test-current.txt
index e320b67..050caa0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -13383,6 +13383,7 @@
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+ field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13405,6 +13406,7 @@
field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+ field public static final int TYPE_POSE_6DOF = 28; // 0x1c
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -22222,6 +22224,7 @@
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+ method public static final android.net.Uri buildRecordedProgramUri(long);
field public static final java.lang.String AUTHORITY = "android.media.tv";
}
@@ -22358,13 +22361,49 @@
field public static final java.lang.String TRAVEL = "TRAVEL";
}
+ public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+ field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+ field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+ field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+ field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+ field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+ field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+ field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+ field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+ field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+ field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+ field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+ field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+ field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+ field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public final class TvInputInfo implements android.os.Parcelable {
+ method public boolean canRecord();
method public android.content.Intent createSettingsIntent();
method public android.content.Intent createSetupIntent();
method public int describeContents();
method public java.lang.String getId();
method public java.lang.String getParentId();
method public android.content.pm.ServiceInfo getServiceInfo();
+ method public int getTunerCount();
method public int getType();
method public boolean isPassthroughInput();
method public android.graphics.drawable.Drawable loadIcon(android.content.Context);
@@ -22399,6 +22438,10 @@
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+ field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+ field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -22416,12 +22459,15 @@
method public void onInputAdded(java.lang.String);
method public void onInputRemoved(java.lang.String);
method public void onInputStateChanged(java.lang.String, int);
+ method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
}
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
+ method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
+ method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
}
@@ -22434,6 +22480,18 @@
method public final boolean onSetSurface(android.view.Surface);
}
+ public static abstract class TvInputService.RecordingSession {
+ ctor public TvInputService.RecordingSession(android.content.Context);
+ method public void notifyConnected();
+ method public void notifyError(int);
+ method public void notifyRecordingStarted();
+ method public void notifyRecordingStopped(android.net.Uri);
+ method public abstract void onConnect(android.net.Uri);
+ method public abstract void onDisconnect();
+ method public abstract void onStartRecording();
+ method public abstract void onStopRecording();
+ }
+
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session(android.content.Context);
method public void layoutSurface(int, int, int, int);
@@ -22461,6 +22519,7 @@
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
method public void onTimeShiftPause();
+ method public void onTimeShiftPlay(android.net.Uri);
method public void onTimeShiftResume();
method public void onTimeShiftSeekTo(long);
method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -22471,6 +22530,23 @@
method public void setOverlayViewEnabled(boolean);
}
+ public class TvRecordingClient {
+ ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+ method public void connect(java.lang.String, android.net.Uri);
+ method public void disconnect();
+ method public void startRecording();
+ method public void stopRecording();
+ }
+
+ public class TvRecordingClient.RecordingCallback {
+ ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnected();
+ method public void onDisconnected();
+ method public void onError(int);
+ method public void onRecordingStarted();
+ method public void onRecordingStopped(android.net.Uri);
+ }
+
public final class TvTrackInfo implements android.os.Parcelable {
method public int describeContents();
method public final int getAudioChannelCount();
@@ -22522,6 +22598,7 @@
method public void setStreamVolume(float);
method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void timeShiftPause();
+ method public void timeShiftPlay(java.lang.String, android.net.Uri);
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -31033,6 +31110,7 @@
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+ method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
method public static java.lang.String getDocumentId(android.net.Uri);
@@ -31041,6 +31119,7 @@
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+ method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -32869,6 +32948,7 @@
method public void getVarV(int, android.renderscript.FieldPacker);
method protected void invoke(int);
method protected void invoke(int, android.renderscript.FieldPacker);
+ method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
method public void setTimeZone(java.lang.String);
method public void setVar(int, float);
method public void setVar(int, double);
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 6dc3cd1..b83484d 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -18,6 +18,7 @@
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -143,6 +144,10 @@
mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
throw e;
}
+
+ mDevicePolicyManager.setUserProvisioningState(
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
System.out.println("Success: Device owner set to package " + mComponent);
System.out.println("Active admin set to component " + mComponent.toShortString());
}
@@ -161,6 +166,10 @@
mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
throw e;
}
+
+ mDevicePolicyManager.setUserProvisioningState(
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
System.out.println("Success: Active admin and profile owner set to "
+ mComponent.toShortString() + " for user " + mUserId);
}
@@ -180,4 +189,4 @@
throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 560e22a..fa11234 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -23,4 +23,11 @@
/** Called whenever an Activity is moved to the pinned stack from another stack. */
void onActivityPinned();
+
+ /**
+ * Called whenever IActivityManager.startActivity is called on an activity that is already
+ * running in the pinned stack and the activity is not actually started, but the task is either
+ * brought to the front or a new Intent is delivered to it.
+ */
+ void onPinnedActivityRestartAttempt();
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7007afe..879d663 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -53,6 +54,8 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyFactory;
@@ -280,6 +283,21 @@
= "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
/**
+ * Activity action: Finalizes management provisioning, should be used after user-setup
+ * has been completed and {@link #getUserProvisioningState()} returns one of:
+ * <ul>
+ * <li>{@link #STATE_USER_SETUP_INCOMPLETE}</li>
+ * <li>{@link #STATE_USER_SETUP_COMPLETE}</li>
+ * <li>{@link #STATE_USER_PROFILE_COMPLETE}</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROVISION_FINALIZATION
+ = "android.app.action.PROVISION_FINALIZATION";
+
+ /**
* A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
* allows a mobile device management application or NFC programmer application which starts
* managed provisioning to pass data to the management application instance after provisioning.
@@ -861,6 +879,44 @@
public static final int PERMISSION_GRANT_STATE_DENIED = 2;
/**
+ * No management for current user in-effect. This is the default.
+ * @hide
+ */
+ public static final int STATE_USER_UNMANAGED = 0;
+
+ /**
+ * Management partially setup, user setup needs to be completed.
+ * @hide
+ */
+ public static final int STATE_USER_SETUP_INCOMPLETE = 1;
+
+ /**
+ * Management partially setup, user setup completed.
+ * @hide
+ */
+ public static final int STATE_USER_SETUP_COMPLETE = 2;
+
+ /**
+ * Management setup and active on current user.
+ * @hide
+ */
+ public static final int STATE_USER_SETUP_FINALIZED = 3;
+
+ /**
+ * Management partially setup on a managed profile.
+ * @hide
+ */
+ public static final int STATE_USER_PROFILE_COMPLETE = 4;
+
+ /**
+ * @hide
+ */
+ @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE,
+ STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserProvisioningState {}
+
+ /**
* Return true if the given administrator component is currently
* active (enabled) in the system.
*/
@@ -3736,14 +3792,15 @@
/**
* Start Quick Contact on the managed profile for the user, if the policy allows.
+ *
* @hide
*/
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
- long directoryId, Intent originalIntent) {
+ boolean isContactIdIgnored, long directoryId, Intent originalIntent) {
if (mService != null) {
try {
- mService.startManagedQuickContact(
- actualLookupKey, actualContactId, directoryId, originalIntent);
+ mService.startManagedQuickContact(actualLookupKey, actualContactId,
+ isContactIdIgnored, directoryId, originalIntent);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -3756,7 +3813,7 @@
*/
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
Intent originalIntent) {
- startManagedQuickContact(actualLookupKey, actualContactId, Directory.DEFAULT,
+ startManagedQuickContact(actualLookupKey, actualContactId, false, Directory.DEFAULT,
originalIntent);
}
@@ -5247,6 +5304,40 @@
}
/**
+ * @return the {@link UserProvisioningState} for the current user - for unmanaged users will
+ * return {@link #STATE_USER_UNMANAGED}
+ * @hide
+ */
+ @UserProvisioningState
+ public int getUserProvisioningState() {
+ if (mService != null) {
+ try {
+ return mService.getUserProvisioningState();
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return STATE_USER_UNMANAGED;
+ }
+
+ /**
+ * Set the {@link UserProvisioningState} for the supplied user, if they are managed.
+ *
+ * @param state to store
+ * @param userHandle for user
+ * @hide
+ */
+ public void setUserProvisioningState(@UserProvisioningState int state, int userHandle) {
+ if (mService != null) {
+ try {
+ mService.setUserProvisioningState(state, userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
* @hide
* Indicates the entity that controls the device or profile owner. A user/profile is considered
* affiliated if it is managed by the same entity as the device.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e97bdf2b..b34e678 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -211,7 +211,7 @@
void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled);
boolean getCrossProfileContactsSearchDisabled(in ComponentName who);
boolean getCrossProfileContactsSearchDisabledForUser(int userId);
- void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
+ void startManagedQuickContact(String lookupKey, long contactId, boolean isContactIdIgnored, long directoryId, in Intent originalIntent);
void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
boolean getBluetoothContactSharingDisabled(in ComponentName who);
@@ -269,6 +269,9 @@
int getOrganizationColor(in ComponentName admin);
int getOrganizationColorForUser(int userHandle);
+ int getUserProvisioningState();
+ void setUserProvisioningState(int state, int userHandle);
+
void setAffiliationIds(in ComponentName admin, in List<String> ids);
boolean isAffiliatedUser();
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0168908..654396b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -176,7 +176,7 @@
/**
* Value for {@link #flags}: this is set if this application has been
- * install as an update to a built-in system application.
+ * installed as an update to a built-in system application.
*/
public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 6935174..e5efd56 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -551,6 +551,29 @@
*/
public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
+ /**
+ * A constant describing a pose sensor with 6 degrees of freedom.
+ *
+ * Similar to {@link #TYPE_ROTATION_VECTOR}, with additional delta
+ * translation from an arbitrary reference point.
+ *
+ * Can use camera, depth sensor etc to compute output value.
+ *
+ * This is expected to be a high power sensor and expected only to be
+ * used when the screen is on.
+ *
+ * Expected to be more accurate than the rotation vector alone.
+ *
+ */
+ public static final int TYPE_POSE_6DOF = 28;
+
+ /**
+ * A constant string describing a pose sensor with 6 degrees of freedom.
+ *
+ * @see #TYPE_POSE_6DOF
+ */
+ public static final String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
+
/**
* A constant describing all sensor types.
*/
@@ -637,6 +660,7 @@
1, // SENSOR_TYPE_PICK_UP_GESTURE
1, // SENSOR_TYPE_WRIST_TILT_GESTURE
1, // SENSOR_TYPE_DEVICE_ORIENTATION
+ 16, // SENSOR_TYPE_POSE_6DOF
};
/**
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9937b2c..416c74c 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -497,6 +497,40 @@
* <li> 3: device is rotated 90 degrees clockwise from default orientation (X axis
* is vertical and points down)
* </ul>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_POSE_6DOF
+ * Sensor.TYPE_POSE_6DOF}:</h4>
+ *
+ * A TYPE_POSE_6DOF event consists of a rotation expressed as a quaternion and a translation
+ * expressed in SI units. The event also contains a delta rotation and translation that show
+ * how the device?s pose has changed since the previous sequence numbered pose.
+ * The event uses the cannonical Android Sensor axes.
+ *
+ *
+ * <ul>
+ * <li> values[0]: x*sin(θ/2) </li>
+ * <li> values[1]: y*sin(θ/2) </li>
+ * <li> values[2]: z*sin(θ/2) </li>
+ * <li> values[3]: cos(θ/2) </li>
+ *
+ *
+ * <li> values[4]: Translation along x axis from an arbitrary origin. </li>
+ * li> values[5]: Translation along y axis from an arbitrary origin. </li>
+ * <li> values[6]: Translation along z axis from an arbitrary origin. </li>
+ *
+ * <li> values[7]: Delta quaternion rotation x*sin(θ/2) </li>
+ * <li> values[8]: Delta quaternion rotation y*sin(θ/2) </li>
+ * <li> values[9]: Delta quaternion rotation z*sin(θ/2) </li>
+ * <li> values[10]: Delta quaternion rotation cos(θ/2) </li>
+ *
+ * <li> values[11]: Delta translation along x axis. </li>
+ * <li> values[12]: Delta translation along y axis. </li>
+ * <li> values[13]: Delta translation along z axis. </li>
+ *
+ * <li> values[14]: Sequence number </li>
+ *
+ * </ul>
+ *
*/
public final float[] values;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 04caa8f..c96c9be 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -496,7 +496,10 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] token, CancellationSignal cancel, int flags,
- EnrollmentCallback callback) {
+ EnrollmentCallback callback, int userId) {
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = getCurrentUserId();
+ }
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -512,7 +515,7 @@
if (mService != null) try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags);
+ mService.enroll(mToken, token, userId, mServiceReceiver, flags);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
@@ -556,6 +559,21 @@
}
/**
+ * Sets the active user. This is meant to be used to select the current profile for enrollment
+ * to allow separate enrolled fingers for a work profile
+ * @param userId
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FINGERPRINT)
+ public void setActiveUser(int userId) {
+ if (mService != null) try {
+ mService.setActiveUser(userId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in setActiveUser: ", e);
+ }
+ }
+
+ /**
* Remove given fingerprint template from fingerprint hardware and/or protected storage.
* @param fp the fingerprint item to remove
* @param callback an optional callback to verify that fingerprint templates have been
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 690a751..43d5577 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -75,4 +75,7 @@
// Add a callback which gets notified when the fingerprint lockout period expired.
void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback);
+
+ // Explicitly set the active user (for enrolling work profile)
+ void setActiveUser(int uid);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 08c0c09..7cb086f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -27,6 +28,7 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -37,6 +39,7 @@
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -338,6 +341,71 @@
public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
/**
+ * Invalid tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ public static final int TETHERING_INVALID = -1;
+
+ /**
+ * Wifi tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_WIFI = 0;
+
+ /**
+ * USB tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_USB = 1;
+
+ /**
+ * Bluetooth tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_BLUETOOTH = 2;
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering to
+ * enable if any.
+ * @hide
+ */
+ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering for
+ * which to cancel provisioning.
+ * @hide
+ */
+ public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+ * provisioning.
+ * @hide
+ */
+ public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+
+ /**
+ * Tells the TetherService to run a provision check now.
+ * @hide
+ */
+ public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+ /**
+ * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+ * which will receive provisioning results. Can be left empty.
+ * @hide
+ */
+ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -1789,6 +1857,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1810,6 +1883,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1834,6 +1912,7 @@
*
* {@hide}
*/
+ @SystemApi
public boolean isTetheringSupported() {
try {
return mService.isTetheringSupported();
@@ -1843,6 +1922,94 @@
}
/**
+ * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ * @hide
+ */
+ @SystemApi
+ public static abstract class OnStartTetheringCallback {
+ /**
+ * Called when tethering has been successfully started.
+ */
+ public void onTetheringStarted() {};
+
+ /**
+ * Called when starting tethering failed.
+ */
+ public void onTetheringFailed() {};
+ }
+
+ /**
+ * Convenient overload for
+ * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
+ * handler to run on the current thread's {@link Looper}.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback) {
+ startTethering(type, showProvisioningUi, callback, null);
+ }
+
+ /**
+ * Runs tether provisioning for the given type if needed and then starts tethering if
+ * the check succeeds. If no carrier provisioning is required for tethering, tethering is
+ * enabled immediately. If provisioning fails, tethering will not be enabled. It also
+ * schedules tether provisioning re-checks if appropriate.
+ *
+ * @param type The type of tethering to start. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
+ * is one. This should be true the first time this function is called and also any time
+ * the user can see this UI. It gives users information from their carrier about the
+ * check failing and how they can sign up for tethering if possible.
+ * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
+ * of the result of trying to tether.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback, Handler handler) {
+ ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ callback.onTetheringStarted();
+ } else {
+ callback.onTetheringFailed();
+ }
+ }
+ };
+ try {
+ mService.startTethering(type, wrappedCallback, showProvisioningUi);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception trying to start tethering.", e);
+ wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
+ }
+ }
+
+ /**
+ * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+ * applicable.
+ *
+ * @param type The type of tethering to stop. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @hide
+ */
+ @SystemApi
+ public void stopTethering(int type) {
+ try {
+ mService.stopTethering(type);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception trying to stop tethering.", e);
+ }
+ }
+
+ /**
* Get the list of regular expressions that define any tetherable
* USB network interfaces. If USB tethering is not supported by the
* device, this list should be empty.
@@ -1949,6 +2116,8 @@
public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
/** {@hide} */
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+ /** {@hide} */
+ public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/**
* Get a more detailed error code after a Tethering or Untethering
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 569468e..1a9c9ea 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -76,6 +76,10 @@
boolean isTetheringSupported();
+ void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+
+ void stopTethering(int type);
+
String[] getTetherableIfaces();
String[] getTetheredIfaces();
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b547432..1bfb3c0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8401,10 +8401,15 @@
* @hide
*/
public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
- long directoryId, Intent originalIntent) {
+ boolean isContactIdIgnored, long directoryId, Intent originalIntent) {
final Intent intent = new Intent(ACTION_QUICK_CONTACT);
// Rebuild the URI from a lookup key and a contact ID.
- Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ Uri uri = null;
+ if (!TextUtils.isEmpty(lookupKey)) {
+ uri = isContactIdIgnored
+ ? Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey)
+ : Contacts.getLookupUri(contactId, lookupKey);
+ }
if (uri != null && directoryId != Directory.DEFAULT) {
uri = uri.buildUpon().appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 36ef52d..2cd1d48 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -42,10 +42,12 @@
private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int CONTACTS_URI_LOOKUP_ID = 1000;
+ private static final int CONTACTS_URI_LOOKUP = 1001;
static {
// Contacts URI matching table
final UriMatcher matcher = sContactsUriMatcher;
+ matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_URI_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID);
}
@@ -57,6 +59,7 @@
final int match = sContactsUriMatcher.match(uri);
switch (match) {
+ case CONTACTS_URI_LOOKUP:
case CONTACTS_URI_LOOKUP_ID: {
if (maybeStartManagedQuickContact(context, intent)) {
return; // Request handled by DPM. Just return here.
@@ -89,7 +92,10 @@
// Decompose into an ID and a lookup key.
final List<String> pathSegments = uri.getPathSegments();
- final long contactId = ContentUris.parseId(uri);
+ final boolean isContactIdIgnored = pathSegments.size() < 4;
+ final long contactId = isContactIdIgnored
+ ? ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE //contact id will be ignored
+ : ContentUris.parseId(uri);
final String lookupKey = pathSegments.get(2);
final String directoryIdStr = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
final long directoryId = (directoryIdStr == null)
@@ -119,8 +125,8 @@
final long actualDirectoryId = (directoryId
- ContactsContract.Directory.ENTERPRISE_DIRECTORY_ID_BASE);
- dpm.startManagedQuickContact(actualLookupKey, actualContactId, actualDirectoryId,
- originalIntent);
+ dpm.startManagedQuickContact(actualLookupKey, actualContactId, isContactIdIgnored,
+ actualDirectoryId, originalIntent);
return true;
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 88cc8a2..cdd88f6 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -342,8 +342,8 @@
* within the same document provider.
*
* @see #COLUMN_FLAGS
- * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri)
- * @see DocumentsProvider#moveDocument(String, String)
+ * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri)
+ * @see DocumentsProvider#moveDocument(String, String, String)
*/
public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -614,6 +614,8 @@
public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
/** {@hide} */
+ public static final String EXTRA_PARENT_URI = "parentUri";
+ /** {@hide} */
public static final String EXTRA_URI = "uri";
private static final String PATH_ROOT = "root";
@@ -1139,7 +1141,6 @@
* @param targetParentDocumentUri document which will become a parent of the source
* document's copy.
* @return the copied document, or {@code null} if failed.
- * @hide
*/
public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
Uri targetParentDocumentUri) {
@@ -1170,17 +1171,18 @@
* Moves the given document under a new parent.
*
* @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}
+ * @param sourceParentDocumentUri parent document of the document to move.
* @param targetParentDocumentUri document which will become a new parent of the source
* document.
* @return the moved document, or {@code null} if failed.
- * @hide
*/
public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
- Uri targetParentDocumentUri) {
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
- return moveDocument(client, sourceDocumentUri, targetParentDocumentUri);
+ return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+ targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
return null;
@@ -1191,9 +1193,10 @@
/** {@hide} */
public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
- Uri targetParentDocumentUri) throws RemoteException {
+ Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
final Bundle in = new Bundle();
in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+ in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 94b4157..bae928d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -289,12 +289,14 @@
* be returned.
*
* @param sourceDocumentId the document to move.
+ * @param sourceParentDocumentId the parent of the document to move.
* @param targetParentDocumentId the target document to be a new parent of the
* source document.
* @hide
*/
@SuppressWarnings("unused")
- public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+ public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+ String targetParentDocumentId)
throws FileNotFoundException {
throw new UnsupportedOperationException("Move not supported");
}
@@ -759,7 +761,7 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
- // Original document no longer exists, clean up any grants
+ // Original document no longer exists, clean up any grants.
revokeDocumentPermission(documentId);
}
@@ -767,7 +769,7 @@
enforceWritePermissionInner(documentUri, getCallingPackage(), null);
deleteDocument(documentId);
- // Document no longer exists, clean up any grants
+ // Document no longer exists, clean up any grants.
revokeDocumentPermission(documentId);
} else if (METHOD_COPY_DOCUMENT.equals(method)) {
@@ -793,13 +795,16 @@
}
} else if (METHOD_MOVE_DOCUMENT.equals(method)) {
+ final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+ final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
enforceWritePermissionInner(targetUri, getCallingPackage(), null);
- final String newDocumentId = moveDocument(documentId, targetId);
+ final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
if (newDocumentId != null) {
final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
@@ -814,7 +819,7 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
}
- // Original document no longer exists, clean up any grants
+ // Original document no longer exists, clean up any grants.
revokeDocumentPermission(documentId);
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 80cf4bb..a9fd3c8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -129,6 +129,27 @@
"android.settings.WIRELESS_SETTINGS";
/**
+ * Activity Action: Show tether provisioning activity.
+ *
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: {@link ConnectivityManager.EXTRA_TETHER_TYPE} should be included to specify which type
+ * of tethering should be checked. {@link ConnectivityManager.EXTRA_PROVISION_CALLBACK} should
+ * contain a {@link ResultReceiver} which will be called back with a tether result code.
+ * <p>
+ * Output: The result of the provisioning check.
+ * {@link ConnectivityManager.TETHER_ERROR_NO_ERROR} if successful,
+ * {@link ConnectivityManager.TETHER_ERROR_PROVISION_FAILED} for failure.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TETHER_PROVISIONING =
+ "android.settings.TETHER_PROVISIONING_UI";
+
+ /**
* Activity Action: Show settings to allow entering/exiting airplane mode.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dc50e17..dd0887f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -100,13 +100,13 @@
import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
-
import static android.os.Build.VERSION_CODES.*;
import static java.lang.Math.max;
import com.android.internal.R;
import com.android.internal.util.Predicate;
import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.ScrollBarUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -5128,6 +5128,88 @@
return mVerticalScrollbarPosition;
}
+ boolean isOnScrollbar(float x, float y) {
+ if (mScrollCache == null) {
+ return false;
+ }
+ x += getScrollX();
+ y += getScrollY();
+ if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getVerticalScrollBarBounds(bounds);
+ if (bounds.contains((int)x, (int)y)) {
+ return true;
+ }
+ }
+ if (isHorizontalScrollBarEnabled()) {
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getHorizontalScrollBarBounds(bounds);
+ if (bounds.contains((int)x, (int)y)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isOnScrollbarThumb(float x, float y) {
+ return isOnVerticalScrollbarThumb(x, y) || isOnHorizontalScrollbarThumb(x, y);
+ }
+
+ private boolean isOnVerticalScrollbarThumb(float x, float y) {
+ if (mScrollCache == null) {
+ return false;
+ }
+ if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+ x += getScrollX();
+ y += getScrollY();
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getVerticalScrollBarBounds(bounds);
+ final int range = computeVerticalScrollRange();
+ final int offset = computeVerticalScrollOffset();
+ final int extent = computeVerticalScrollExtent();
+ final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(),
+ extent, range);
+ final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength,
+ extent, range, offset);
+ final int thumbTop = bounds.top + thumbOffset;
+ if (x >= bounds.left && x <= bounds.right && y >= thumbTop
+ && y <= thumbTop + thumbLength) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isOnHorizontalScrollbarThumb(float x, float y) {
+ if (mScrollCache == null) {
+ return false;
+ }
+ if (isHorizontalScrollBarEnabled()) {
+ x += getScrollX();
+ y += getScrollY();
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getHorizontalScrollBarBounds(bounds);
+ final int range = computeHorizontalScrollRange();
+ final int offset = computeHorizontalScrollOffset();
+ final int extent = computeHorizontalScrollExtent();
+ final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(),
+ extent, range);
+ final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength,
+ extent, range, offset);
+ final int thumbLeft = bounds.left + thumbOffset;
+ if (x >= thumbLeft && x <= thumbLeft + thumbLength && y >= bounds.top
+ && y <= bounds.bottom) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isDraggingScrollBar() {
+ return mScrollCache != null
+ && mScrollCache.mScrollBarDraggingState != ScrollabilityCache.NOT_DRAGGING;
+ }
+
/**
* Sets the state of all scroll indicators.
* <p>
@@ -9715,6 +9797,9 @@
}
if (onFilterTouchEventForSecurity(event)) {
+ if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
+ result = true;
+ }
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
@@ -10579,6 +10664,11 @@
}
}
+ if ((action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE)
+ && event.isFromSource(InputDevice.SOURCE_MOUSE)
+ && isOnScrollbar(event.getX(), event.getY())) {
+ awakenScrollBars();
+ }
if (isHoverable()) {
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
@@ -10682,6 +10772,110 @@
}
/**
+ * Handles scroll bar dragging by mouse input.
+ *
+ * @hide
+ * @param event The motion event.
+ *
+ * @return true if the event was handled as a scroll bar dragging, false otherwise.
+ */
+ protected boolean handleScrollBarDragging(MotionEvent event) {
+ if (mScrollCache == null) {
+ return false;
+ }
+ final float x = event.getX();
+ final float y = event.getY();
+ final int action = event.getAction();
+ if ((mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING
+ && action != MotionEvent.ACTION_DOWN)
+ || !event.isFromSource(InputDevice.SOURCE_MOUSE)
+ || !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING;
+ return false;
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ if (mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING) {
+ return false;
+ }
+ if (mScrollCache.mScrollBarDraggingState
+ == ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR) {
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getVerticalScrollBarBounds(bounds);
+ final int range = computeVerticalScrollRange();
+ final int offset = computeVerticalScrollOffset();
+ final int extent = computeVerticalScrollExtent();
+
+ final int thumbLength = ScrollBarUtils.getThumbLength(
+ bounds.height(), bounds.width(), extent, range);
+ final int thumbOffset = ScrollBarUtils.getThumbOffset(
+ bounds.height(), thumbLength, extent, range, offset);
+
+ final float diff = y - mScrollCache.mScrollBarDraggingPos;
+ final float maxThumbOffset = bounds.height() - thumbLength;
+ final float newThumbOffset =
+ Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset);
+ final int height = getHeight();
+ if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0
+ && height > 0 && extent > 0) {
+ final int newY = Math.round((range - extent)
+ / ((float)extent / height) * (newThumbOffset / maxThumbOffset));
+ if (newY != getScrollY()) {
+ mScrollCache.mScrollBarDraggingPos = y;
+ setScrollY(newY);
+ }
+ }
+ return true;
+ }
+ if (mScrollCache.mScrollBarDraggingState
+ == ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR) {
+ final Rect bounds = mScrollCache.mScrollBarBounds;
+ getHorizontalScrollBarBounds(bounds);
+ final int range = computeHorizontalScrollRange();
+ final int offset = computeHorizontalScrollOffset();
+ final int extent = computeHorizontalScrollExtent();
+
+ final int thumbLength = ScrollBarUtils.getThumbLength(
+ bounds.width(), bounds.height(), extent, range);
+ final int thumbOffset = ScrollBarUtils.getThumbOffset(
+ bounds.width(), thumbLength, extent, range, offset);
+
+ final float diff = x - mScrollCache.mScrollBarDraggingPos;
+ final float maxThumbOffset = bounds.width() - thumbLength;
+ final float newThumbOffset =
+ Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset);
+ final int width = getWidth();
+ if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0
+ && width > 0 && extent > 0) {
+ final int newX = Math.round((range - extent)
+ / ((float)extent / width) * (newThumbOffset / maxThumbOffset));
+ if (newX != getScrollX()) {
+ mScrollCache.mScrollBarDraggingPos = x;
+ setScrollX(newX);
+ }
+ }
+ return true;
+ }
+ case MotionEvent.ACTION_DOWN:
+ if (isOnVerticalScrollbarThumb(x, y)) {
+ mScrollCache.mScrollBarDraggingState =
+ ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR;
+ mScrollCache.mScrollBarDraggingPos = y;
+ return true;
+ }
+ if (isOnHorizontalScrollbarThumb(x, y)) {
+ mScrollCache.mScrollBarDraggingState =
+ ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR;
+ mScrollCache.mScrollBarDraggingPos = x;
+ return true;
+ }
+ }
+ mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING;
+ return false;
+ }
+
+ /**
* Implement this method to handle touch screen motion events.
* <p>
* If this method is used to detect click actions, it is recommended that
@@ -10714,7 +10908,6 @@
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
-
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
@@ -14275,6 +14468,45 @@
}
}
+ private void getHorizontalScrollBarBounds(Rect bounds) {
+ final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+ final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
+ && !isVerticalScrollBarHidden();
+ final int size = getHorizontalScrollbarHeight();
+ final int verticalScrollBarGap = drawVerticalScrollBar ?
+ getVerticalScrollbarWidth() : 0;
+ final int width = mRight - mLeft;
+ final int height = mBottom - mTop;
+ bounds.top = mScrollY + height - size - (mUserPaddingBottom & inside);
+ bounds.left = mScrollX + (mPaddingLeft & inside);
+ bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
+ bounds.bottom = bounds.top + size;
+ }
+
+ private void getVerticalScrollBarBounds(Rect bounds) {
+ final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+ final int size = getVerticalScrollbarWidth();
+ int verticalScrollbarPosition = mVerticalScrollbarPosition;
+ if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
+ verticalScrollbarPosition = isLayoutRtl() ?
+ SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
+ }
+ final int width = mRight - mLeft;
+ final int height = mBottom - mTop;
+ switch (verticalScrollbarPosition) {
+ default:
+ case SCROLLBAR_POSITION_RIGHT:
+ bounds.left = mScrollX + width - size - (mUserPaddingRight & inside);
+ break;
+ case SCROLLBAR_POSITION_LEFT:
+ bounds.left = mScrollX + (mUserPaddingLeft & inside);
+ break;
+ }
+ bounds.top = mScrollY + (mPaddingTop & inside);
+ bounds.right = bounds.left + size;
+ bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside);
+ }
+
/**
* <p>Request the drawing of the horizontal and the vertical scrollbar. The
* scrollbars are painted only if they have been awakened first.</p>
@@ -14322,80 +14554,36 @@
cache.scrollBar.mutate().setAlpha(255);
}
-
- final int viewFlags = mViewFlags;
-
- final boolean drawHorizontalScrollBar =
- (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
- final boolean drawVerticalScrollBar =
- (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
- && !isVerticalScrollBarHidden();
+ final boolean drawHorizontalScrollBar = isHorizontalScrollBarEnabled();
+ final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
+ && !isVerticalScrollBarHidden();
if (drawVerticalScrollBar || drawHorizontalScrollBar) {
- final int width = mRight - mLeft;
- final int height = mBottom - mTop;
-
final ScrollBarDrawable scrollBar = cache.scrollBar;
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
-
- int left;
- int top;
- int right;
- int bottom;
-
if (drawHorizontalScrollBar) {
- int size = scrollBar.getSize(false);
- if (size <= 0) {
- size = cache.scrollBarSize;
- }
-
scrollBar.setParameters(computeHorizontalScrollRange(),
computeHorizontalScrollOffset(),
computeHorizontalScrollExtent(), false);
- final int verticalScrollBarGap = drawVerticalScrollBar ?
- getVerticalScrollbarWidth() : 0;
- top = scrollY + height - size - (mUserPaddingBottom & inside);
- left = scrollX + (mPaddingLeft & inside);
- right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
- bottom = top + size;
- onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom);
+ final Rect bounds = cache.mScrollBarBounds;
+ getHorizontalScrollBarBounds(bounds);
+ onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
+ bounds.right, bounds.bottom);
if (invalidate) {
- invalidate(left, top, right, bottom);
+ invalidate(bounds);
}
}
if (drawVerticalScrollBar) {
- int size = scrollBar.getSize(true);
- if (size <= 0) {
- size = cache.scrollBarSize;
- }
-
scrollBar.setParameters(computeVerticalScrollRange(),
computeVerticalScrollOffset(),
computeVerticalScrollExtent(), true);
- int verticalScrollbarPosition = mVerticalScrollbarPosition;
- if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
- verticalScrollbarPosition = isLayoutRtl() ?
- SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
- }
- switch (verticalScrollbarPosition) {
- default:
- case SCROLLBAR_POSITION_RIGHT:
- left = scrollX + width - size - (mUserPaddingRight & inside);
- break;
- case SCROLLBAR_POSITION_LEFT:
- left = scrollX + (mUserPaddingLeft & inside);
- break;
- }
- top = scrollY + (mPaddingTop & inside);
- right = left + size;
- bottom = scrollY + height - (mUserPaddingBottom & inside);
- onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom);
+ final Rect bounds = cache.mScrollBarBounds;
+ getVerticalScrollBarBounds(bounds);
+ onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
+ bounds.right, bounds.bottom);
if (invalidate) {
- invalidate(left, top, right, bottom);
+ invalidate(bounds);
}
}
}
@@ -21335,6 +21523,9 @@
* @see PointerIcon
*/
public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+ if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) {
+ return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW);
+ }
return mPointerIcon;
}
@@ -22606,6 +22797,15 @@
private int mLastColor;
+ public final Rect mScrollBarBounds = new Rect();
+
+ public static final int NOT_DRAGGING = 0;
+ public static final int DRAGGING_VERTICAL_SCROLL_BAR = 1;
+ public static final int DRAGGING_HORIZONTAL_SCROLL_BAR = 2;
+ public int mScrollBarDraggingState = NOT_DRAGGING;
+
+ public float mScrollBarDraggingPos = 0;
+
public ScrollabilityCache(ViewConfiguration configuration, View host) {
fadingEdgeLength = configuration.getScaledFadingEdgeLength();
scrollBarSize = configuration.getScaledScrollBarSize();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 27e2ea3..3fe6b8e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -59,6 +59,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
/**
@@ -1651,6 +1652,9 @@
@Override
public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+ if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) {
+ return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW);
+ }
// Check what the child under the pointer says about the pointer.
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
@@ -1974,7 +1978,7 @@
* hover exit event in {@link #onHoverEvent} and then the hovered child will
* receive a hover enter event.
* </p><p>
- * The default implementation always returns false.
+ * The default implementation handles mouse hover on the scroll bars.
* </p>
*
* @param event The motion event that describes the hover.
@@ -1982,6 +1986,15 @@
* and prevent its children from receiving it.
*/
public boolean onInterceptHoverEvent(MotionEvent event) {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ final int action = event.getAction();
+ final float x = event.getX();
+ final float y = event.getY();
+ if ((action == MotionEvent.ACTION_HOVER_MOVE
+ || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) {
+ return true;
+ }
+ }
return false;
}
@@ -2716,6 +2729,12 @@
* messages will be delivered here.
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
+ && ev.getAction() == MotionEvent.ACTION_DOWN
+ && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
+ && isOnScrollbarThumb(ev.getX(), ev.getY())) {
+ return true;
+ }
return false;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 6c2c956..9561f08 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3701,6 +3701,13 @@
}
}
+ /** @hide */
+ @Override
+ protected boolean handleScrollBarDragging(MotionEvent event) {
+ // Doesn't support normal scroll bar dragging. Use FastScroller.
+ return false;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!isEnabled()) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4355eb3..67473c6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1082,6 +1082,20 @@
return true;
}
+ private void startDragAndDrop() {
+ final int start = mTextView.getSelectionStart();
+ final int end = mTextView.getSelectionEnd();
+ CharSequence selectedText = mTextView.getTransformedText(start, end);
+ ClipData data = ClipData.newPlainText(null, selectedText);
+ DragLocalState localState = new DragLocalState(mTextView, start, end);
+ mTextView.startDragAndDrop(data, getTextThumbnailBuilder(selectedText), localState,
+ View.DRAG_FLAG_GLOBAL);
+ stopTextActionMode();
+ if (hasSelectionController()) {
+ getSelectionController().resetTouchOffsets();
+ }
+ }
+
public boolean performLongClick(boolean handled) {
// Long press in empty space moves cursor and starts the insertion action mode.
if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
@@ -1097,15 +1111,7 @@
if (!handled && mTextActionMode != null) {
if (touchPositionIsInSelection()) {
- // Start a drag
- final int start = mTextView.getSelectionStart();
- final int end = mTextView.getSelectionEnd();
- CharSequence selectedText = mTextView.getTransformedText(start, end);
- ClipData data = ClipData.newPlainText(null, selectedText);
- DragLocalState localState = new DragLocalState(mTextView, start, end);
- mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState,
- View.DRAG_FLAG_GLOBAL);
- stopTextActionMode();
+ startDragAndDrop();
} else {
stopTextActionMode();
selectCurrentWordAndStartDrag();
@@ -4925,6 +4931,14 @@
if (isMouse && !isDragAcceleratorActive()) {
final int offset = mTextView.getOffsetForPosition(eventX, eventY);
+ if (mTextView.hasSelection()
+ && (!mHaventMovedEnoughToStartDrag || mStartOffset != offset)
+ && offset >= mTextView.getSelectionStart()
+ && offset <= mTextView.getSelectionEnd()) {
+ startDragAndDrop();
+ break;
+ }
+
if (mStartOffset != offset) {
// Start character based drag accelerator.
if (mTextActionMode != null) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ebc7eb3..f16fdd6 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -454,6 +454,10 @@
return true;
}
+ if (super.onInterceptTouchEvent(ev)) {
+ return true;
+ }
+
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
/*
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 91d6232..8880217 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -16,6 +16,8 @@
package android.widget;
+import com.android.internal.widget.ScrollBarUtils;
+
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
@@ -135,23 +137,15 @@
}
if (drawThumb) {
- final int size = vertical ? r.height() : r.width();
+ final int scrollBarLength = vertical ? r.height() : r.width();
final int thickness = vertical ? r.width() : r.height();
- final int minLength = thickness * 2;
+ final int thumbLength =
+ ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range);
+ final int thumbOffset =
+ ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range,
+ mOffset);
- // Avoid the tiny thumb.
- int length = Math.round((float) size * extent / range);
- if (length < minLength) {
- length = minLength;
- }
-
- // Avoid the too-big thumb.
- int offset = Math.round((float) (size - length) * mOffset / (range - extent));
- if (offset > size - length) {
- offset = size - length;
- }
-
- drawThumb(canvas, r, offset, length, vertical);
+ drawThumb(canvas, r, thumbOffset, thumbLength, vertical);
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 78b931d..3f7a07bb 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -489,6 +489,10 @@
return true;
}
+ if (super.onInterceptTouchEvent(ev)) {
+ return true;
+ }
+
/*
* Don't try to intercept touch if we can't scroll anyway.
*/
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 5b40bc0..a572486 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -237,6 +237,22 @@
return mMiddleTarget;
}
+ public SnapTarget getNextTarget(SnapTarget snapTarget) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1 && index < mTargets.size() - 1) {
+ return mTargets.get(index + 1);
+ }
+ return snapTarget;
+ }
+
+ public SnapTarget getPreviousTarget(SnapTarget snapTarget) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1 && index > 0) {
+ return mTargets.get(index - 1);
+ }
+ return snapTarget;
+ }
+
/**
* Represents a snap target for the divider.
*/
diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java
new file mode 100644
index 0000000..0ae9f74
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollBarUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+public class ScrollBarUtils {
+
+ public static int getThumbLength(int size, int thickness, int extent, int range) {
+ // Avoid the tiny thumb.
+ final int minLength = thickness * 2;
+ int length = Math.round((float) size * extent / range);
+ if (length < minLength) {
+ length = minLength;
+ }
+ return length;
+ }
+
+ public static int getThumbOffset(int size, int thumbLength, int extent, int range, int offset) {
+ // Avoid the too-big thumb.
+ int thumbOffset = Math.round((float) (size - thumbLength) * offset / (range - extent));
+ if (thumbOffset > size - thumbLength) {
+ thumbOffset = size - thumbLength;
+ }
+ return thumbOffset;
+ }
+}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 34877e0..35b5016 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -86,20 +86,20 @@
}
static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
}
static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
}
static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jint alpha, jint flagsHandle) {
- SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+ SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
}
@@ -351,7 +351,7 @@
if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
canvas->drawNinePatch(skiaBitmap, *chunk, left, top, right, bottom, paint);
} else {
- canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas->save(SaveFlags::MatrixClip);
SkScalar scale = dstDensity / (float)srcDensity;
canvas->translate(left, top);
@@ -390,7 +390,7 @@
canvas->drawBitmap(bitmap, left, top, paint);
}
} else {
- canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas->save(SaveFlags::MatrixClip);
SkScalar scale = canvasDensity / (float)bitmapDensity;
canvas->translate(left, top);
canvas->scale(scale, scale);
diff --git a/core/res/res/drawable-xhdpi/pointer_alias_large.png b/core/res/res/drawable-xhdpi/pointer_alias_large.png
new file mode 100644
index 0000000..813cd2a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_alias_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png
new file mode 100644
index 0000000..f101077
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_arrow_large.png
new file mode 100644
index 0000000..0f9165b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_cell_large.png b/core/res/res/drawable-xhdpi/pointer_cell_large.png
new file mode 100644
index 0000000..b41f855
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_cell_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_context_menu_large.png b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png
new file mode 100644
index 0000000..6264e0b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_copy_large.png b/core/res/res/drawable-xhdpi/pointer_copy_large.png
new file mode 100644
index 0000000..5e7f375
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_copy_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_crosshair_large.png b/core/res/res/drawable-xhdpi/pointer_crosshair_large.png
new file mode 100644
index 0000000..d5f4502
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_crosshair_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grab_large.png b/core/res/res/drawable-xhdpi/pointer_grab_large.png
new file mode 100644
index 0000000..bf99b79
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grab_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
new file mode 100644
index 0000000..e576bcd
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_hand_large.png b/core/res/res/drawable-xhdpi/pointer_hand_large.png
new file mode 100644
index 0000000..76fa09b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_hand_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_help_large.png b/core/res/res/drawable-xhdpi/pointer_help_large.png
new file mode 100644
index 0000000..a45f039
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_help_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png
new file mode 100644
index 0000000..9b2a312
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
new file mode 100644
index 0000000..f140532
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_text_large.png b/core/res/res/drawable-xhdpi/pointer_text_large.png
new file mode 100644
index 0000000..56be154
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_text_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png
new file mode 100644
index 0000000..9e6a8b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png
new file mode 100644
index 0000000..2631094c8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png
new file mode 100644
index 0000000..5ec65e01
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png b/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png
new file mode 100644
index 0000000..26c46de
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png b/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png
new file mode 100644
index 0000000..c2b845f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png b/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png
new file mode 100644
index 0000000..148ed25
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png
Binary files differ
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c0453f8..eb0075b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -53,6 +53,7 @@
<uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index afd0bc4..00df87d 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -37,6 +37,7 @@
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
import com.android.frameworks.coretests.R;
@@ -145,6 +146,40 @@
}
@SmallTest
+ public void testDragAndDrop() throws Exception {
+ final String text = "abc def ghi.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(text.indexOf("d"), text.indexOf("f") + 1));
+
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(text.indexOf("e"), text.length()));
+
+ onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+ onView(withId(R.id.textview)).check(hasSelection(""));
+ assertNoSelectionHandles();
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+ }
+
+ @SmallTest
+ public void testDragAndDrop_longClick() throws Exception {
+ final String text = "abc def ghi.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(text.indexOf("d"), text.indexOf("f") + 1));
+
+ onView(withId(R.id.textview)).perform(
+ mouseLongClickAndDragOnText(text.indexOf("e"), text.length()));
+
+ onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+ onView(withId(R.id.textview)).check(hasSelection(""));
+ assertNoSelectionHandles();
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+ }
+
+ @SmallTest
public void testSelectTextByLongClick() throws Exception {
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index edbfef9..5dae4a8 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -159,6 +159,22 @@
}
@SmallTest
+ public void testDragAndDrop() throws Exception {
+ final String text = "abc def ghi.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
+
+ onView(withId(R.id.textview)).perform(
+ longPressAndDragOnText(text.indexOf("e"), text.length()));
+
+ onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+ onView(withId(R.id.textview)).check(hasSelection(""));
+ assertNoSelectionHandles();
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+ }
+
+ @SmallTest
public void testDoubleTapToSelect() throws Exception {
final String helloWorld = "Hello SuetYi!";
onView(withId(R.id.textview)).perform(click());
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 483ccf7..fc40554 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -4,6 +4,11 @@
HWUI_NEW_OPS := true
+# Enables fine-grained GLES error checking
+# If set to true, every GLES call is wrapped & error checked
+# Has moderate overhead
+HWUI_ENABLE_OPENGL_VALIDATION := false
+
hwui_src_files := \
font/CacheTexture.cpp \
font/Font.cpp \
@@ -157,6 +162,13 @@
frameworks/rs
endif
+ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION))
+ hwui_cflags += -include debug/wrap_gles.h
+ hwui_src_files += debug/wrap_gles.cpp
+ hwui_c_includes += frameworks/native/opengl/libs/GLES2
+ hwui_cflags += -DDEBUG_OPENGL=3
+endif
+
# ------------------------
# static library
@@ -188,8 +200,8 @@
-DHWUI_NULL_GPU
LOCAL_SRC_FILES := \
$(hwui_src_files) \
- tests/common/nullegl.cpp \
- tests/common/nullgles.cpp
+ debug/nullegl.cpp \
+ debug/nullgles.cpp
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 00381ee..7ecc743 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -784,9 +784,7 @@
.build();
renderer.renderGlop(state, glop);
}
- GL_CHECKPOINT();
renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
- GL_CHECKPOINT();
}
} // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index a808b88..5736c70 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -49,7 +49,8 @@
// attach the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
offscreenBuffer->texture.id(), 0);
- LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
+ GL_CHECKPOINT(LOW);
+
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
@@ -63,7 +64,7 @@
if (mRenderTarget.stencil) {
// if stencil was used for clipping, detach it and return it to pool
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed");
+ GL_CHECKPOINT(MODERATE);
mCaches.renderBufferCache.put(mRenderTarget.stencil);
mRenderTarget.stencil = nullptr;
}
@@ -74,8 +75,7 @@
// Detach the texture from the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED, bound fbo = %u",
- mRenderState.getFramebuffer());
+ GL_CHECKPOINT(LOW);
mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
mRenderTarget.frameBufferId = 0;
}
@@ -139,8 +139,6 @@
mCaches.pathCache.trim();
mCaches.tessellationCache.trim();
- GL_CHECKPOINT();
-
#if DEBUG_MEMORY_USAGE
mCaches.dumpMemoryUsage();
#else
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 0643a54..9dfe454 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -27,6 +27,22 @@
namespace android {
+namespace SaveFlags {
+
+// These must match the corresponding Canvas API constants.
+enum {
+ Matrix = 0x01,
+ Clip = 0x02,
+ HasAlphaLayer = 0x04,
+ ClipToLayer = 0x10,
+
+ // Helper constant
+ MatrixClip = Matrix | Clip,
+};
+typedef uint32_t Flags;
+
+} // namespace SaveFlags
+
class ANDROID_API Canvas {
public:
virtual ~Canvas() {};
@@ -70,16 +86,17 @@
// ----------------------------------------------------------------------------
// Canvas state operations
// ----------------------------------------------------------------------------
+
// Save (layer)
virtual int getSaveCount() const = 0;
- virtual int save(SkCanvas::SaveFlags flags) = 0;
+ virtual int save(SaveFlags::Flags flags) = 0;
virtual void restore() = 0;
virtual void restoreToCount(int saveCount) = 0;
virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+ const SkPaint* paint, SaveFlags::Flags flags) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags) = 0;
+ int alpha, SaveFlags::Flags flags) = 0;
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index cf2726b5..43ff33f 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include <SkCanvas.h>
-
+#include "Canvas.h"
#include "CanvasState.h"
#include "utils/MathUtils.h"
@@ -54,8 +53,7 @@
}
freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
mSnapshot->setRelativeLightCenter(Vector3());
mSaveCount = 1;
}
@@ -72,8 +70,7 @@
}
freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = mCanvas.getTargetFbo();
mSnapshot->setRelativeLightCenter(lightCenter);
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index e98fa04..748edef 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -17,8 +17,18 @@
#ifndef ANDROID_HWUI_DEBUG_H
#define ANDROID_HWUI_DEBUG_H
+#define DEBUG_LEVEL_HIGH 3
+#define DEBUG_LEVEL_MODERATE 2
+#define DEBUG_LEVEL_LOW 1
+#define DEBUG_LEVEL_NONE 0
+
// Turn on to check for OpenGL errors on each frame
-#define DEBUG_OPENGL 1
+// Note DEBUG_LEVEL_HIGH for DEBUG_OPENGL is only setable by enabling
+// HWUI_ENABLE_OPENGL_VALIDATION when building HWUI. Similarly if
+// HWUI_ENABLE_OPENGL_VALIDATION is set then this is always DEBUG_LEVEL_HIGH
+#ifndef DEBUG_OPENGL
+#define DEBUG_OPENGL DEBUG_LEVEL_LOW
+#endif
// Turn on to enable initialization information
#define DEBUG_INIT 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a1825c5..1b0f424 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <SkCanvas.h>
-
#include <utils/Trace.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -419,7 +417,7 @@
* beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
*
* saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
- * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set.
+ * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set.
*/
void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
SaveLayerOp* op, int newSaveCount) {
@@ -438,7 +436,7 @@
int saveFlags = op->getFlags();
DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);
- if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) {
+ if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) {
// store and replay the save operation, as it may be needed to correctly playback the clip
DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount);
storeStateOpBarrier(renderer, op);
@@ -621,7 +619,7 @@
this, newSaveCount, mBatches.size());
// store displayState for the restore operation, as it may be associated with a saveLayer that
- // doesn't have kClip_SaveFlag set
+ // doesn't have SaveFlags::Clip set
DeferredDisplayState* state = createState();
renderer.storeDisplayState(*state, getStateOpDeferFlags());
mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount));
@@ -654,7 +652,7 @@
renderer.eventMark("Flush");
// save and restore so that reordering doesn't affect final state
- renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ renderer.save(SaveFlags::MatrixClip);
if (CC_LIKELY(avoidOverdraw())) {
for (unsigned int i = 1; i < mBatches.size(); i++) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 759c12a..384e64d 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -102,7 +102,7 @@
return mSkiaCanvasProxy.get();
}
-int DisplayListCanvas::save(SkCanvas::SaveFlags flags) {
+int DisplayListCanvas::save(SaveFlags::Flags flags) {
addStateOp(new (alloc()) SaveOp((int) flags));
return mState.save((int) flags);
}
@@ -125,9 +125,9 @@
}
int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags) {
+ const SkPaint* paint, SaveFlags::Flags flags) {
// force matrix/clip isolation for layer
- flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+ flags |= SaveFlags::MatrixClip;
paint = refPaint(paint);
addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags));
@@ -232,7 +232,7 @@
void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top,
const SkPaint* paint) {
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
translate(left, top);
drawBitmap(&bitmap, paint);
restore();
@@ -253,7 +253,7 @@
drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
} else {
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
concat(matrix);
drawBitmap(&bitmap, paint);
restore();
@@ -269,7 +269,7 @@
&& (srcBottom - srcTop == dstBottom - dstTop)
&& (srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
drawBitmap(&bitmap, paint);
restore();
@@ -283,7 +283,7 @@
// Apply the scale transform on the canvas, so that the shader
// effectively calculates positions relative to src rect space
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
scale(scaleX, scaleY);
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 72fc100..f1cfa08 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -128,14 +128,14 @@
// ----------------------------------------------------------------------------
// Save (layer)
virtual int getSaveCount() const override { return mState.getSaveCount(); }
- virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SkCanvas::SaveFlags flags) override;
+ SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags) override {
+ int alpha, SaveFlags::Flags flags) override {
SkPaint paint;
paint.setAlpha(alpha);
return saveLayer(left, top, right, bottom, &paint, flags);
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index c4c655b..a3d6401 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -16,6 +16,7 @@
#include "FrameBuilder.h"
+#include "Canvas.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
#include "renderstate/OffscreenBufferPool.h"
@@ -23,7 +24,6 @@
#include "utils/PaintUtils.h"
#include "utils/TraceUtils.h"
-#include <SkCanvas.h>
#include <SkPathOps.h>
#include <utils/TypeHelpers.h>
@@ -77,7 +77,7 @@
if (node->nothingToDraw()) continue;
node->computeOrdering();
- int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ int count = mCanvasState.save(SaveFlags::MatrixClip);
deferNodePropsAndOps(*node);
mCanvasState.restoreToCount(count);
}
@@ -327,7 +327,7 @@
void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
- int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ int count = mCanvasState.save(SaveFlags::MatrixClip);
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
const DisplayList& displayList = *(renderNode.getDisplayList());
@@ -348,7 +348,7 @@
for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
RenderNodeOp* childOp = renderNode.mProjectedNodes[i];
- int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
+ int restoreTo = mCanvasState.save(SaveFlags::Matrix);
mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
deferRenderNodeOpImpl(*childOp);
mCanvasState.restoreToCount(restoreTo);
@@ -392,7 +392,7 @@
void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
- int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ int count = mCanvasState.save(SaveFlags::MatrixClip);
// apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip,
@@ -597,7 +597,7 @@
const Rect& repaintRect,
const Vector3& lightCenter,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
- mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ mCanvasState.save(SaveFlags::MatrixClip);
mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index bd01850..4fd39be 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -100,30 +100,30 @@
// Relay through layers in reverse order, since layers
// later in the list will be drawn by earlier ones
for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
LayerBuilder& layer = *(mLayerBuilders[i]);
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
renderer.endLayer();
} else if (!layer.empty()) { // save layer - skip entire layer if empty
layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
renderer.endLayer();
}
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
renderer.endFrame(fbo0.repaintRect);
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 0f219e4..3123e8e 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -407,7 +407,6 @@
renderState.bindFramebuffer(fbo);
glGenTextures(1, &texture);
- GL_CHECKPOINT();
caches.textureState().activateTexture(0);
caches.textureState().bindTexture(texture);
@@ -422,11 +421,9 @@
glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
0, format, type, nullptr);
- GL_CHECKPOINT();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
- GL_CHECKPOINT();
{
LayerRenderer renderer(renderState, layer);
@@ -437,8 +434,6 @@
renderer.translate(0.0f, bitmap->height());
renderer.scale(1.0f, -1.0f);
- GL_CHECKPOINT();
-
{
Rect bounds;
bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
@@ -447,7 +442,6 @@
glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
type, bitmap->getPixels());
- GL_CHECKPOINT();
}
status = true;
@@ -460,6 +454,8 @@
renderState.deleteFramebuffer(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
+ GL_CHECKPOINT(MODERATE);
+
return status;
}
return false;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index db017fe..6c2e244 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -17,6 +17,7 @@
#include <GpuMemoryTracker.h>
#include "OpenGLRenderer.h"
+#include "Canvas.h"
#include "DeferredDisplayList.h"
#include "GammaFontRenderer.h"
#include "Glop.h"
@@ -39,7 +40,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaintDefaults.h>
#include <SkPathOps.h>
@@ -195,7 +195,7 @@
}
if (!suppressErrorChecks()) {
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
#if DEBUG_MEMORY_USAGE
mCaches.dumpMemoryUsage();
@@ -472,7 +472,7 @@
int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags, const SkPath* convexMask) {
// force matrix/clip isolation for layer
- flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+ flags |= SaveFlags::MatrixClip;
const int count = mState.saveSnapshot(flags);
@@ -531,7 +531,7 @@
const SkPaint* paint, int flags) {
const int count = mState.saveSnapshot(flags);
- if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ if (!mState.currentlyIgnored() && (flags & SaveFlags::ClipToLayer)) {
// initialize the snapshot as though it almost represents an FBO layer so deferred draw
// operations will be able to store and restore the current clip and transform info, and
// quick rejection will be correct (for display lists)
@@ -558,7 +558,7 @@
* and the frame buffer still receive every drawing command. For instance, if a
* layer is created and a shape intersecting the bounds of the layers and the
* framebuffer is draw, the shape will be drawn on both (unless the layer was
- * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ * created with the SaveFlags::ClipToLayer flag.)
*
* A way to implement layers is to create an FBO for each layer, backed by an RGBA
* texture. Unfortunately, this is inefficient as it requires every primitive to
@@ -608,7 +608,7 @@
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
- const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
+ const bool fboLayer = flags & SaveFlags::ClipToLayer;
// Window coordinates of the layer
Rect clip;
@@ -890,7 +890,7 @@
if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
if (layer->getConvexMask()) {
- save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::MatrixClip);
// clip to the area of the layer the mask can be larger
clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
@@ -2233,7 +2233,7 @@
if (layer->isTextureLayer()) {
transform = &layer->getTransform();
if (!transform->isIdentity()) {
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
concatMatrix(*transform);
}
}
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 78855e5..328e291 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -92,7 +92,7 @@
// android/graphics/Canvas state operations
// ----------------------------------------------------------------------------
// Save (layer)
-int RecordingCanvas::save(SkCanvas::SaveFlags flags) {
+int RecordingCanvas::save(SaveFlags::Flags flags) {
return mState.save((int) flags);
}
@@ -105,10 +105,10 @@
}
int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags) {
+ const SkPaint* paint, SaveFlags::Flags flags) {
// force matrix/clip isolation for layer
- flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
- bool clippedLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
+ flags |= SaveFlags::MatrixClip;
+ bool clippedLayer = flags & SaveFlags::ClipToLayer;
const Snapshot& previous = *mState.currentSnapshot();
@@ -128,7 +128,7 @@
// unlikely case where an unclipped savelayer is recorded with a clip it can use,
// as none of its unaffected/unclipped area is visible
clippedLayer = true;
- flags |= SkCanvas::kClipToLayer_SaveFlag;
+ flags |= SaveFlags::ClipToLayer;
}
visibleBounds.doIntersect(previous.getRenderTargetClip());
@@ -424,7 +424,7 @@
// Bitmap-based
void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
translate(left, top);
drawBitmap(&bitmap, paint);
restore();
@@ -445,7 +445,7 @@
drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
} else {
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
concat(matrix);
drawBitmap(&bitmap, paint);
restore();
@@ -461,7 +461,7 @@
&& (srcBottom - srcTop == dstBottom - dstTop)
&& (srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
- save(SkCanvas::kMatrix_SaveFlag);
+ save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
drawBitmap(&bitmap, paint);
restore();
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 8aa7506..786f96e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -108,14 +108,14 @@
// ----------------------------------------------------------------------------
// Save (layer)
virtual int getSaveCount() const override { return mState.getSaveCount(); }
- virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SkCanvas::SaveFlags flags) override;
+ SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags) override {
+ int alpha, SaveFlags::Flags flags) override {
SkPaint paint;
paint.setAlpha(alpha);
return saveLayer(left, top, right, bottom, &paint, flags);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ae690fd..d4588ed 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -33,8 +33,6 @@
#include "protos/hwui.pb.h"
#include "protos/ProtoHelpers.h"
-#include <SkCanvas.h>
-
#include <algorithm>
#include <sstream>
#include <string>
@@ -105,8 +103,7 @@
(isRenderable() ? "" : ", empty"),
(properties().getProjectBackwards() ? ", projected" : ""),
(mLayer != nullptr ? ", on HW Layer" : ""));
- ALOGD("%*s%s %d", level * 2, "", "Save",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip);
properties().debugOutputProperties(level);
@@ -574,7 +571,7 @@
layerBounds.left, layerBounds.top,
layerBounds.right, layerBounds.bottom,
(int) (properties().getAlpha() * 255),
- SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag);
+ SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
@@ -875,7 +872,7 @@
// Apply the base transform of the parent of the 3d children. This isolates
// 3d children of the current chunk from transformations made in previous chunks.
- int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ int rootRestoreTo = renderer.save(SaveFlags::Matrix);
renderer.setGlobalMatrix(initialTransform);
/**
@@ -919,7 +916,7 @@
// only the actual child DL draw needs to be in save/restore,
// since it modifies the renderer's matrix
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ int restoreTo = renderer.save(SaveFlags::Matrix);
DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
@@ -941,7 +938,7 @@
int restoreTo = renderer.getSaveCount();
LinearAllocator& alloc = handler.allocator();
- handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
// Transform renderer to match background we're projecting onto
@@ -966,7 +963,7 @@
renderNodeOp_t* childOp = mProjectedNodes[i];
// matrix save, concat, and restore can be done safely without allocating operations
- int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+ int restoreTo = renderer.save(SaveFlags::Matrix);
renderer.concatMatrix(childOp->transformFromCompositingAncestor);
childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
@@ -1027,11 +1024,11 @@
LinearAllocator& alloc = handler.allocator();
int restoreTo = renderer.getSaveCount();
- handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "",
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+ SaveFlags::MatrixClip, restoreTo);
if (useViewProperties) {
setViewProperties<T>(renderer, handler);
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ce1bd6a..b848af4 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -18,12 +18,12 @@
#include <utils/Trace.h>
-#include <SkCanvas.h>
#include <SkColorFilter.h>
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkPathOps.h>
+#include "Canvas.h"
#include "Matrix.h"
#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
@@ -144,7 +144,7 @@
(int)layerBounds.left, (int)layerBounds.top,
(int)layerBounds.right, (int)layerBounds.bottom,
(int)(mPrimitiveFields.mAlpha * 255),
- SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag);
+ SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 96c1a7c..20e7c71 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
#include <SkGraphics.h>
#include <SkShader.h>
#include <SkTArray.h>
+#include <SkTLazy.h>
#include <SkTemplates.h>
#include <memory>
@@ -63,14 +64,14 @@
virtual bool isHighContrastText() override { return mHighContrastText; }
virtual int getSaveCount() const override;
- virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags) override;
+ const SkPaint* paint, SaveFlags::Flags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags) override;
+ int alpha, SaveFlags::Flags flags) override;
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
@@ -138,13 +139,13 @@
private:
struct SaveRec {
- int saveCount;
- SkCanvas::SaveFlags saveFlags;
+ int saveCount;
+ SaveFlags::Flags saveFlags;
};
bool mHighContrastText = false;
- void recordPartialSave(SkCanvas::SaveFlags flags);
+ void recordPartialSave(SaveFlags::Flags flags);
void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
void applyClips(const SkTArray<SkClipStack::Element>& clips);
@@ -231,7 +232,7 @@
return mCanvas->getSaveCount();
}
-int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+int SkiaCanvas::save(SaveFlags::Flags flags) {
int count = mCanvas->save();
recordPartialSave(flags);
return count;
@@ -254,8 +255,8 @@
return;
}
- bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
- bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+ bool preserveMatrix = !(rec->saveFlags & SaveFlags::Matrix);
+ bool preserveClip = !(rec->saveFlags & SaveFlags::Clip);
SkMatrix savedMatrix;
if (preserveMatrix) {
@@ -291,34 +292,53 @@
}
}
+static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
+ SkCanvas::SaveLayerFlags layerFlags = 0;
+
+ if (!(flags & SaveFlags::HasAlphaLayer)) {
+ layerFlags |= SkCanvas::kIsOpaque_SaveLayerFlag;
+ }
+
+ if (!(flags & SaveFlags::ClipToLayer)) {
+ layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag;
+ }
+
+ return layerFlags;
+}
+
int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags) {
- SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
- int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+ const SkPaint* paint, SaveFlags::Flags flags) {
+ const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
+
+ int count = mCanvas->saveLayer(rec);
recordPartialSave(flags);
return count;
}
int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags) {
- SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
- int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
- recordPartialSave(flags);
- return count;
+ int alpha, SaveFlags::Flags flags) {
+ SkTLazy<SkPaint> alphaPaint;
+ if (static_cast<unsigned>(alpha) < 0xFF) {
+ alphaPaint.init()->setAlpha(alpha);
+ }
+
+ return this->saveLayer(left, top, right, bottom, alphaPaint.getMaybeNull(),
+ flags);
}
// ----------------------------------------------------------------------------
// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
// ----------------------------------------------------------------------------
-void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+void SkiaCanvas::recordPartialSave(SaveFlags::Flags flags) {
// A partial save is a save operation which doesn't capture the full canvas state.
- // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+ // (either SaveFlags::Matrix or SaveFlags::Clip is missing).
// Mask-out non canvas state bits.
- flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+ flags &= SaveFlags::MatrixClip;
- if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+ if (flags == SaveFlags::MatrixClip) {
// not a partial save.
return;
}
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 976f775..6530d4ed8 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -159,7 +159,21 @@
}
void SkiaCanvasProxy::willSave() {
- mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ mCanvas->save(android::SaveFlags::MatrixClip);
+}
+
+static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
+ SaveFlags::Flags saveFlags = 0;
+
+ if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
+ saveFlags |= SaveFlags::ClipToLayer;
+ }
+
+ if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) {
+ saveFlags |= SaveFlags::HasAlphaLayer;
+ }
+
+ return saveFlags;
}
SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
@@ -170,7 +184,7 @@
rect = SkRect::MakeEmpty();
}
mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
- (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags));
+ saveFlags(saveLayerRec.fSaveLayerFlags));
return SkCanvas::kNoLayer_SaveLayerStrategy;
}
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index c6d8977..27fea1f 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -16,7 +16,7 @@
#include "Snapshot.h"
-#include <SkCanvas.h>
+#include "Canvas.h"
namespace android {
namespace uirenderer {
@@ -57,14 +57,14 @@
, mClipArea(nullptr)
, mViewportData(s->mViewportData)
, mRelativeLightCenter(s->mRelativeLightCenter) {
- if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
+ if (saveFlags & SaveFlags::Matrix) {
mTransformRoot = *s->transform;
transform = &mTransformRoot;
} else {
transform = s->transform;
}
- if (saveFlags & SkCanvas::kClip_SaveFlag) {
+ if (saveFlags & SaveFlags::Clip) {
mClipAreaRoot = s->getClipArea();
mClipArea = &mClipAreaRoot;
} else {
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5046d37..c09b6dd 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -97,7 +97,7 @@
void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
GLenum format, GLenum type, const void* pixels) {
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
bool needsAlloc = updateSize(width, height, internalformat);
if (!mId) {
glGenTextures(1, &mId);
@@ -112,7 +112,7 @@
glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
format, type, pixels);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
}
static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 3e20608..1d31c9e 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -420,7 +420,7 @@
return;
}
- int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
+ int saveCount = outCanvas->save(SaveFlags::MatrixClip);
outCanvas->translate(mBounds.fLeft, mBounds.fTop);
// Handle RTL mirroring.
diff --git a/libs/hwui/tests/common/nullegl.cpp b/libs/hwui/debug/nullegl.cpp
similarity index 100%
rename from libs/hwui/tests/common/nullegl.cpp
rename to libs/hwui/debug/nullegl.cpp
diff --git a/libs/hwui/tests/common/nullgles.cpp b/libs/hwui/debug/nullgles.cpp
similarity index 99%
rename from libs/hwui/tests/common/nullgles.cpp
rename to libs/hwui/debug/nullgles.cpp
index f8e8c98..ffb0649 100644
--- a/libs/hwui/tests/common/nullgles.cpp
+++ b/libs/hwui/debug/nullgles.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "unwrap_gles.h"
+
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
diff --git a/libs/hwui/debug/unwrap_gles.h b/libs/hwui/debug/unwrap_gles.h
new file mode 100644
index 0000000..7716a73
--- /dev/null
+++ b/libs/hwui/debug/unwrap_gles.h
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+#undef HWUI_GLES_WRAP_ENABLED
+
+#undef glActiveShaderProgram
+#undef glActiveShaderProgramEXT
+#undef glActiveTexture
+#undef glAlphaFunc
+#undef glAlphaFuncQCOM
+#undef glAlphaFuncx
+#undef glAlphaFuncxOES
+#undef glApplyFramebufferAttachmentCMAAINTEL
+#undef glAttachShader
+#undef glBeginConditionalRenderNV
+#undef glBeginPerfMonitorAMD
+#undef glBeginPerfQueryINTEL
+#undef glBeginQuery
+#undef glBeginQueryEXT
+#undef glBeginTransformFeedback
+#undef glBindAttribLocation
+#undef glBindBuffer
+#undef glBindBufferBase
+#undef glBindBufferRange
+#undef glBindFragDataLocationEXT
+#undef glBindFragDataLocationIndexedEXT
+#undef glBindFramebuffer
+#undef glBindFramebufferOES
+#undef glBindImageTexture
+#undef glBindProgramPipeline
+#undef glBindProgramPipelineEXT
+#undef glBindRenderbuffer
+#undef glBindRenderbufferOES
+#undef glBindSampler
+#undef glBindTexture
+#undef glBindTransformFeedback
+#undef glBindVertexArray
+#undef glBindVertexArrayOES
+#undef glBindVertexBuffer
+#undef glBlendBarrier
+#undef glBlendBarrierKHR
+#undef glBlendBarrierNV
+#undef glBlendColor
+#undef glBlendEquation
+#undef glBlendEquationOES
+#undef glBlendEquationSeparate
+#undef glBlendEquationSeparateOES
+#undef glBlendEquationSeparatei
+#undef glBlendEquationSeparateiEXT
+#undef glBlendEquationSeparateiOES
+#undef glBlendEquationi
+#undef glBlendEquationiEXT
+#undef glBlendEquationiOES
+#undef glBlendFunc
+#undef glBlendFuncSeparate
+#undef glBlendFuncSeparateOES
+#undef glBlendFuncSeparatei
+#undef glBlendFuncSeparateiEXT
+#undef glBlendFuncSeparateiOES
+#undef glBlendFunci
+#undef glBlendFunciEXT
+#undef glBlendFunciOES
+#undef glBlendParameteriNV
+#undef glBlitFramebuffer
+#undef glBlitFramebufferANGLE
+#undef glBlitFramebufferNV
+#undef glBufferData
+#undef glBufferStorageEXT
+#undef glBufferSubData
+#undef glCheckFramebufferStatus
+#undef glCheckFramebufferStatusOES
+#undef glClear
+#undef glClearBufferfi
+#undef glClearBufferfv
+#undef glClearBufferiv
+#undef glClearBufferuiv
+#undef glClearColor
+#undef glClearColorx
+#undef glClearColorxOES
+#undef glClearDepthf
+#undef glClearDepthfOES
+#undef glClearDepthx
+#undef glClearDepthxOES
+#undef glClearStencil
+#undef glClientActiveTexture
+#undef glClientWaitSync
+#undef glClientWaitSyncAPPLE
+#undef glClipPlanef
+#undef glClipPlanefIMG
+#undef glClipPlanefOES
+#undef glClipPlanex
+#undef glClipPlanexIMG
+#undef glClipPlanexOES
+#undef glColor4f
+#undef glColor4ub
+#undef glColor4x
+#undef glColor4xOES
+#undef glColorMask
+#undef glColorMaski
+#undef glColorMaskiEXT
+#undef glColorMaskiOES
+#undef glColorPointer
+#undef glCompileShader
+#undef glCompressedTexImage2D
+#undef glCompressedTexImage3D
+#undef glCompressedTexImage3DOES
+#undef glCompressedTexSubImage2D
+#undef glCompressedTexSubImage3D
+#undef glCompressedTexSubImage3DOES
+#undef glCopyBufferSubData
+#undef glCopyBufferSubDataNV
+#undef glCopyImageSubData
+#undef glCopyImageSubDataEXT
+#undef glCopyImageSubDataOES
+#undef glCopyPathNV
+#undef glCopyTexImage2D
+#undef glCopyTexSubImage2D
+#undef glCopyTexSubImage3D
+#undef glCopyTexSubImage3DOES
+#undef glCopyTextureLevelsAPPLE
+#undef glCoverFillPathInstancedNV
+#undef glCoverFillPathNV
+#undef glCoverStrokePathInstancedNV
+#undef glCoverStrokePathNV
+#undef glCoverageMaskNV
+#undef glCoverageModulationNV
+#undef glCoverageModulationTableNV
+#undef glCoverageOperationNV
+#undef glCreatePerfQueryINTEL
+#undef glCreateProgram
+#undef glCreateShader
+#undef glCreateShaderProgramv
+#undef glCreateShaderProgramvEXT
+#undef glCullFace
+#undef glCurrentPaletteMatrixOES
+#undef glDebugMessageCallback
+#undef glDebugMessageCallbackKHR
+#undef glDebugMessageControl
+#undef glDebugMessageControlKHR
+#undef glDebugMessageInsert
+#undef glDebugMessageInsertKHR
+#undef glDeleteBuffers
+#undef glDeleteFencesNV
+#undef glDeleteFramebuffers
+#undef glDeleteFramebuffersOES
+#undef glDeletePathsNV
+#undef glDeletePerfMonitorsAMD
+#undef glDeletePerfQueryINTEL
+#undef glDeleteProgram
+#undef glDeleteProgramPipelines
+#undef glDeleteProgramPipelinesEXT
+#undef glDeleteQueries
+#undef glDeleteQueriesEXT
+#undef glDeleteRenderbuffers
+#undef glDeleteRenderbuffersOES
+#undef glDeleteSamplers
+#undef glDeleteShader
+#undef glDeleteSync
+#undef glDeleteSyncAPPLE
+#undef glDeleteTextures
+#undef glDeleteTransformFeedbacks
+#undef glDeleteVertexArrays
+#undef glDeleteVertexArraysOES
+#undef glDepthFunc
+#undef glDepthMask
+#undef glDepthRangeArrayfvNV
+#undef glDepthRangeIndexedfNV
+#undef glDepthRangef
+#undef glDepthRangefOES
+#undef glDepthRangex
+#undef glDepthRangexOES
+#undef glDetachShader
+#undef glDisable
+#undef glDisableClientState
+#undef glDisableDriverControlQCOM
+#undef glDisableVertexAttribArray
+#undef glDisablei
+#undef glDisableiEXT
+#undef glDisableiNV
+#undef glDisableiOES
+#undef glDiscardFramebufferEXT
+#undef glDispatchCompute
+#undef glDispatchComputeIndirect
+#undef glDrawArrays
+#undef glDrawArraysIndirect
+#undef glDrawArraysInstanced
+#undef glDrawArraysInstancedANGLE
+#undef glDrawArraysInstancedBaseInstanceEXT
+#undef glDrawArraysInstancedEXT
+#undef glDrawArraysInstancedNV
+#undef glDrawBuffers
+#undef glDrawBuffersEXT
+#undef glDrawBuffersIndexedEXT
+#undef glDrawBuffersNV
+#undef glDrawElements
+#undef glDrawElementsBaseVertex
+#undef glDrawElementsBaseVertexEXT
+#undef glDrawElementsBaseVertexOES
+#undef glDrawElementsIndirect
+#undef glDrawElementsInstanced
+#undef glDrawElementsInstancedANGLE
+#undef glDrawElementsInstancedBaseInstanceEXT
+#undef glDrawElementsInstancedBaseVertex
+#undef glDrawElementsInstancedBaseVertexBaseInstanceEXT
+#undef glDrawElementsInstancedBaseVertexEXT
+#undef glDrawElementsInstancedBaseVertexOES
+#undef glDrawElementsInstancedEXT
+#undef glDrawElementsInstancedNV
+#undef glDrawRangeElements
+#undef glDrawRangeElementsBaseVertex
+#undef glDrawRangeElementsBaseVertexEXT
+#undef glDrawRangeElementsBaseVertexOES
+#undef glDrawTexfOES
+#undef glDrawTexfvOES
+#undef glDrawTexiOES
+#undef glDrawTexivOES
+#undef glDrawTexsOES
+#undef glDrawTexsvOES
+#undef glDrawTexxOES
+#undef glDrawTexxvOES
+#undef glEGLImageTargetRenderbufferStorageOES
+#undef glEGLImageTargetTexture2DOES
+#undef glEnable
+#undef glEnableClientState
+#undef glEnableDriverControlQCOM
+#undef glEnableVertexAttribArray
+#undef glEnablei
+#undef glEnableiEXT
+#undef glEnableiNV
+#undef glEnableiOES
+#undef glEndConditionalRenderNV
+#undef glEndPerfMonitorAMD
+#undef glEndPerfQueryINTEL
+#undef glEndQuery
+#undef glEndQueryEXT
+#undef glEndTilingQCOM
+#undef glEndTransformFeedback
+#undef glExtGetBufferPointervQCOM
+#undef glExtGetBuffersQCOM
+#undef glExtGetFramebuffersQCOM
+#undef glExtGetProgramBinarySourceQCOM
+#undef glExtGetProgramsQCOM
+#undef glExtGetRenderbuffersQCOM
+#undef glExtGetShadersQCOM
+#undef glExtGetTexLevelParameterivQCOM
+#undef glExtGetTexSubImageQCOM
+#undef glExtGetTexturesQCOM
+#undef glExtIsProgramBinaryQCOM
+#undef glExtTexObjectStateOverrideiQCOM
+#undef glFenceSync
+#undef glFenceSyncAPPLE
+#undef glFinish
+#undef glFinishFenceNV
+#undef glFlush
+#undef glFlushMappedBufferRange
+#undef glFlushMappedBufferRangeEXT
+#undef glFogf
+#undef glFogfv
+#undef glFogx
+#undef glFogxOES
+#undef glFogxv
+#undef glFogxvOES
+#undef glFragmentCoverageColorNV
+#undef glFramebufferParameteri
+#undef glFramebufferRenderbuffer
+#undef glFramebufferRenderbufferOES
+#undef glFramebufferSampleLocationsfvNV
+#undef glFramebufferTexture
+#undef glFramebufferTexture2D
+#undef glFramebufferTexture2DMultisampleEXT
+#undef glFramebufferTexture2DMultisampleIMG
+#undef glFramebufferTexture2DOES
+#undef glFramebufferTexture3DOES
+#undef glFramebufferTextureEXT
+#undef glFramebufferTextureLayer
+#undef glFramebufferTextureMultisampleMultiviewOVR
+#undef glFramebufferTextureMultiviewOVR
+#undef glFramebufferTextureOES
+#undef glFrontFace
+#undef glFrustumf
+#undef glFrustumfOES
+#undef glFrustumx
+#undef glFrustumxOES
+#undef glGenBuffers
+#undef glGenFencesNV
+#undef glGenFramebuffers
+#undef glGenFramebuffersOES
+#undef glGenPathsNV
+#undef glGenPerfMonitorsAMD
+#undef glGenProgramPipelines
+#undef glGenProgramPipelinesEXT
+#undef glGenQueries
+#undef glGenQueriesEXT
+#undef glGenRenderbuffers
+#undef glGenRenderbuffersOES
+#undef glGenSamplers
+#undef glGenTextures
+#undef glGenTransformFeedbacks
+#undef glGenVertexArrays
+#undef glGenVertexArraysOES
+#undef glGenerateMipmap
+#undef glGenerateMipmapOES
+#undef glGetActiveAttrib
+#undef glGetActiveUniform
+#undef glGetActiveUniformBlockName
+#undef glGetActiveUniformBlockiv
+#undef glGetActiveUniformsiv
+#undef glGetAttachedShaders
+#undef glGetAttribLocation
+#undef glGetBooleani_v
+#undef glGetBooleanv
+#undef glGetBufferParameteri64v
+#undef glGetBufferParameteriv
+#undef glGetBufferPointerv
+#undef glGetBufferPointervOES
+#undef glGetClipPlanef
+#undef glGetClipPlanefOES
+#undef glGetClipPlanex
+#undef glGetClipPlanexOES
+#undef glGetCoverageModulationTableNV
+#undef glGetDebugMessageLog
+#undef glGetDebugMessageLogKHR
+#undef glGetDriverControlStringQCOM
+#undef glGetDriverControlsQCOM
+#undef glGetError
+#undef glGetFenceivNV
+#undef glGetFirstPerfQueryIdINTEL
+#undef glGetFixedv
+#undef glGetFixedvOES
+#undef glGetFloati_vNV
+#undef glGetFloatv
+#undef glGetFragDataIndexEXT
+#undef glGetFragDataLocation
+#undef glGetFramebufferAttachmentParameteriv
+#undef glGetFramebufferAttachmentParameterivOES
+#undef glGetFramebufferParameteriv
+#undef glGetGraphicsResetStatus
+#undef glGetGraphicsResetStatusEXT
+#undef glGetGraphicsResetStatusKHR
+#undef glGetImageHandleNV
+#undef glGetInteger64i_v
+#undef glGetInteger64v
+#undef glGetInteger64vAPPLE
+#undef glGetIntegeri_v
+#undef glGetIntegeri_vEXT
+#undef glGetIntegerv
+#undef glGetInternalformatSampleivNV
+#undef glGetInternalformativ
+#undef glGetLightfv
+#undef glGetLightxv
+#undef glGetLightxvOES
+#undef glGetMaterialfv
+#undef glGetMaterialxv
+#undef glGetMaterialxvOES
+#undef glGetMultisamplefv
+#undef glGetNextPerfQueryIdINTEL
+#undef glGetObjectLabel
+#undef glGetObjectLabelEXT
+#undef glGetObjectLabelKHR
+#undef glGetObjectPtrLabel
+#undef glGetObjectPtrLabelKHR
+#undef glGetPathCommandsNV
+#undef glGetPathCoordsNV
+#undef glGetPathDashArrayNV
+#undef glGetPathLengthNV
+#undef glGetPathMetricRangeNV
+#undef glGetPathMetricsNV
+#undef glGetPathParameterfvNV
+#undef glGetPathParameterivNV
+#undef glGetPathSpacingNV
+#undef glGetPerfCounterInfoINTEL
+#undef glGetPerfMonitorCounterDataAMD
+#undef glGetPerfMonitorCounterInfoAMD
+#undef glGetPerfMonitorCounterStringAMD
+#undef glGetPerfMonitorCountersAMD
+#undef glGetPerfMonitorGroupStringAMD
+#undef glGetPerfMonitorGroupsAMD
+#undef glGetPerfQueryDataINTEL
+#undef glGetPerfQueryIdByNameINTEL
+#undef glGetPerfQueryInfoINTEL
+#undef glGetPointerv
+#undef glGetPointervKHR
+#undef glGetProgramBinary
+#undef glGetProgramBinaryOES
+#undef glGetProgramInfoLog
+#undef glGetProgramInterfaceiv
+#undef glGetProgramPipelineInfoLog
+#undef glGetProgramPipelineInfoLogEXT
+#undef glGetProgramPipelineiv
+#undef glGetProgramPipelineivEXT
+#undef glGetProgramResourceIndex
+#undef glGetProgramResourceLocation
+#undef glGetProgramResourceLocationIndexEXT
+#undef glGetProgramResourceName
+#undef glGetProgramResourcefvNV
+#undef glGetProgramResourceiv
+#undef glGetProgramiv
+#undef glGetQueryObjecti64vEXT
+#undef glGetQueryObjectivEXT
+#undef glGetQueryObjectui64vEXT
+#undef glGetQueryObjectuiv
+#undef glGetQueryObjectuivEXT
+#undef glGetQueryiv
+#undef glGetQueryivEXT
+#undef glGetRenderbufferParameteriv
+#undef glGetRenderbufferParameterivOES
+#undef glGetSamplerParameterIiv
+#undef glGetSamplerParameterIivEXT
+#undef glGetSamplerParameterIivOES
+#undef glGetSamplerParameterIuiv
+#undef glGetSamplerParameterIuivEXT
+#undef glGetSamplerParameterIuivOES
+#undef glGetSamplerParameterfv
+#undef glGetSamplerParameteriv
+#undef glGetShaderInfoLog
+#undef glGetShaderPrecisionFormat
+#undef glGetShaderSource
+#undef glGetShaderiv
+#undef glGetString
+#undef glGetStringi
+#undef glGetSynciv
+#undef glGetSyncivAPPLE
+#undef glGetTexEnvfv
+#undef glGetTexEnviv
+#undef glGetTexEnvxv
+#undef glGetTexEnvxvOES
+#undef glGetTexGenfvOES
+#undef glGetTexGenivOES
+#undef glGetTexGenxvOES
+#undef glGetTexLevelParameterfv
+#undef glGetTexLevelParameteriv
+#undef glGetTexParameterIiv
+#undef glGetTexParameterIivEXT
+#undef glGetTexParameterIivOES
+#undef glGetTexParameterIuiv
+#undef glGetTexParameterIuivEXT
+#undef glGetTexParameterIuivOES
+#undef glGetTexParameterfv
+#undef glGetTexParameteriv
+#undef glGetTexParameterxv
+#undef glGetTexParameterxvOES
+#undef glGetTextureHandleNV
+#undef glGetTextureSamplerHandleNV
+#undef glGetTransformFeedbackVarying
+#undef glGetTranslatedShaderSourceANGLE
+#undef glGetUniformBlockIndex
+#undef glGetUniformIndices
+#undef glGetUniformLocation
+#undef glGetUniformfv
+#undef glGetUniformiv
+#undef glGetUniformuiv
+#undef glGetVertexAttribIiv
+#undef glGetVertexAttribIuiv
+#undef glGetVertexAttribPointerv
+#undef glGetVertexAttribfv
+#undef glGetVertexAttribiv
+#undef glGetnUniformfv
+#undef glGetnUniformfvEXT
+#undef glGetnUniformfvKHR
+#undef glGetnUniformiv
+#undef glGetnUniformivEXT
+#undef glGetnUniformivKHR
+#undef glGetnUniformuiv
+#undef glGetnUniformuivKHR
+#undef glHint
+#undef glInsertEventMarkerEXT
+#undef glInterpolatePathsNV
+#undef glInvalidateFramebuffer
+#undef glInvalidateSubFramebuffer
+#undef glIsBuffer
+#undef glIsEnabled
+#undef glIsEnabledi
+#undef glIsEnablediEXT
+#undef glIsEnablediNV
+#undef glIsEnablediOES
+#undef glIsFenceNV
+#undef glIsFramebuffer
+#undef glIsFramebufferOES
+#undef glIsImageHandleResidentNV
+#undef glIsPathNV
+#undef glIsPointInFillPathNV
+#undef glIsPointInStrokePathNV
+#undef glIsProgram
+#undef glIsProgramPipeline
+#undef glIsProgramPipelineEXT
+#undef glIsQuery
+#undef glIsQueryEXT
+#undef glIsRenderbuffer
+#undef glIsRenderbufferOES
+#undef glIsSampler
+#undef glIsShader
+#undef glIsSync
+#undef glIsSyncAPPLE
+#undef glIsTexture
+#undef glIsTextureHandleResidentNV
+#undef glIsTransformFeedback
+#undef glIsVertexArray
+#undef glIsVertexArrayOES
+#undef glLabelObjectEXT
+#undef glLightModelf
+#undef glLightModelfv
+#undef glLightModelx
+#undef glLightModelxOES
+#undef glLightModelxv
+#undef glLightModelxvOES
+#undef glLightf
+#undef glLightfv
+#undef glLightx
+#undef glLightxOES
+#undef glLightxv
+#undef glLightxvOES
+#undef glLineWidth
+#undef glLineWidthx
+#undef glLineWidthxOES
+#undef glLinkProgram
+#undef glLoadIdentity
+#undef glLoadMatrixf
+#undef glLoadMatrixx
+#undef glLoadMatrixxOES
+#undef glLoadPaletteFromModelViewMatrixOES
+#undef glLogicOp
+#undef glMakeImageHandleNonResidentNV
+#undef glMakeImageHandleResidentNV
+#undef glMakeTextureHandleNonResidentNV
+#undef glMakeTextureHandleResidentNV
+#undef glMapBufferOES
+#undef glMapBufferRange
+#undef glMapBufferRangeEXT
+#undef glMaterialf
+#undef glMaterialfv
+#undef glMaterialx
+#undef glMaterialxOES
+#undef glMaterialxv
+#undef glMaterialxvOES
+#undef glMatrixIndexPointerOES
+#undef glMatrixLoad3x2fNV
+#undef glMatrixLoad3x3fNV
+#undef glMatrixLoadTranspose3x3fNV
+#undef glMatrixMode
+#undef glMatrixMult3x2fNV
+#undef glMatrixMult3x3fNV
+#undef glMatrixMultTranspose3x3fNV
+#undef glMemoryBarrier
+#undef glMemoryBarrierByRegion
+#undef glMinSampleShading
+#undef glMinSampleShadingOES
+#undef glMultMatrixf
+#undef glMultMatrixx
+#undef glMultMatrixxOES
+#undef glMultiDrawArraysEXT
+#undef glMultiDrawArraysIndirectEXT
+#undef glMultiDrawElementsBaseVertexEXT
+#undef glMultiDrawElementsBaseVertexOES
+#undef glMultiDrawElementsEXT
+#undef glMultiDrawElementsIndirectEXT
+#undef glMultiTexCoord4f
+#undef glMultiTexCoord4x
+#undef glMultiTexCoord4xOES
+#undef glNamedFramebufferSampleLocationsfvNV
+#undef glNormal3f
+#undef glNormal3x
+#undef glNormal3xOES
+#undef glNormalPointer
+#undef glObjectLabel
+#undef glObjectLabelKHR
+#undef glObjectPtrLabel
+#undef glObjectPtrLabelKHR
+#undef glOrthof
+#undef glOrthofOES
+#undef glOrthox
+#undef glOrthoxOES
+#undef glPatchParameteri
+#undef glPatchParameteriEXT
+#undef glPatchParameteriOES
+#undef glPathCommandsNV
+#undef glPathCoordsNV
+#undef glPathCoverDepthFuncNV
+#undef glPathDashArrayNV
+#undef glPathGlyphIndexArrayNV
+#undef glPathGlyphIndexRangeNV
+#undef glPathGlyphRangeNV
+#undef glPathGlyphsNV
+#undef glPathMemoryGlyphIndexArrayNV
+#undef glPathParameterfNV
+#undef glPathParameterfvNV
+#undef glPathParameteriNV
+#undef glPathParameterivNV
+#undef glPathStencilDepthOffsetNV
+#undef glPathStencilFuncNV
+#undef glPathStringNV
+#undef glPathSubCommandsNV
+#undef glPathSubCoordsNV
+#undef glPauseTransformFeedback
+#undef glPixelStorei
+#undef glPointAlongPathNV
+#undef glPointParameterf
+#undef glPointParameterfv
+#undef glPointParameterx
+#undef glPointParameterxOES
+#undef glPointParameterxv
+#undef glPointParameterxvOES
+#undef glPointSize
+#undef glPointSizePointerOES
+#undef glPointSizex
+#undef glPointSizexOES
+#undef glPolygonModeNV
+#undef glPolygonOffset
+#undef glPolygonOffsetx
+#undef glPolygonOffsetxOES
+#undef glPopDebugGroup
+#undef glPopDebugGroupKHR
+#undef glPopGroupMarkerEXT
+#undef glPopMatrix
+#undef glPrimitiveBoundingBox
+#undef glPrimitiveBoundingBoxEXT
+#undef glPrimitiveBoundingBoxOES
+#undef glProgramBinary
+#undef glProgramBinaryOES
+#undef glProgramParameteri
+#undef glProgramParameteriEXT
+#undef glProgramPathFragmentInputGenNV
+#undef glProgramUniform1f
+#undef glProgramUniform1fEXT
+#undef glProgramUniform1fv
+#undef glProgramUniform1fvEXT
+#undef glProgramUniform1i
+#undef glProgramUniform1iEXT
+#undef glProgramUniform1iv
+#undef glProgramUniform1ivEXT
+#undef glProgramUniform1ui
+#undef glProgramUniform1uiEXT
+#undef glProgramUniform1uiv
+#undef glProgramUniform1uivEXT
+#undef glProgramUniform2f
+#undef glProgramUniform2fEXT
+#undef glProgramUniform2fv
+#undef glProgramUniform2fvEXT
+#undef glProgramUniform2i
+#undef glProgramUniform2iEXT
+#undef glProgramUniform2iv
+#undef glProgramUniform2ivEXT
+#undef glProgramUniform2ui
+#undef glProgramUniform2uiEXT
+#undef glProgramUniform2uiv
+#undef glProgramUniform2uivEXT
+#undef glProgramUniform3f
+#undef glProgramUniform3fEXT
+#undef glProgramUniform3fv
+#undef glProgramUniform3fvEXT
+#undef glProgramUniform3i
+#undef glProgramUniform3iEXT
+#undef glProgramUniform3iv
+#undef glProgramUniform3ivEXT
+#undef glProgramUniform3ui
+#undef glProgramUniform3uiEXT
+#undef glProgramUniform3uiv
+#undef glProgramUniform3uivEXT
+#undef glProgramUniform4f
+#undef glProgramUniform4fEXT
+#undef glProgramUniform4fv
+#undef glProgramUniform4fvEXT
+#undef glProgramUniform4i
+#undef glProgramUniform4iEXT
+#undef glProgramUniform4iv
+#undef glProgramUniform4ivEXT
+#undef glProgramUniform4ui
+#undef glProgramUniform4uiEXT
+#undef glProgramUniform4uiv
+#undef glProgramUniform4uivEXT
+#undef glProgramUniformHandleui64NV
+#undef glProgramUniformHandleui64vNV
+#undef glProgramUniformMatrix2fv
+#undef glProgramUniformMatrix2fvEXT
+#undef glProgramUniformMatrix2x3fv
+#undef glProgramUniformMatrix2x3fvEXT
+#undef glProgramUniformMatrix2x4fv
+#undef glProgramUniformMatrix2x4fvEXT
+#undef glProgramUniformMatrix3fv
+#undef glProgramUniformMatrix3fvEXT
+#undef glProgramUniformMatrix3x2fv
+#undef glProgramUniformMatrix3x2fvEXT
+#undef glProgramUniformMatrix3x4fv
+#undef glProgramUniformMatrix3x4fvEXT
+#undef glProgramUniformMatrix4fv
+#undef glProgramUniformMatrix4fvEXT
+#undef glProgramUniformMatrix4x2fv
+#undef glProgramUniformMatrix4x2fvEXT
+#undef glProgramUniformMatrix4x3fv
+#undef glProgramUniformMatrix4x3fvEXT
+#undef glPushDebugGroup
+#undef glPushDebugGroupKHR
+#undef glPushGroupMarkerEXT
+#undef glPushMatrix
+#undef glQueryCounterEXT
+#undef glQueryMatrixxOES
+#undef glRasterSamplesEXT
+#undef glReadBuffer
+#undef glReadBufferIndexedEXT
+#undef glReadBufferNV
+#undef glReadPixels
+#undef glReadnPixels
+#undef glReadnPixelsEXT
+#undef glReadnPixelsKHR
+#undef glReleaseShaderCompiler
+#undef glRenderbufferStorage
+#undef glRenderbufferStorageMultisample
+#undef glRenderbufferStorageMultisampleANGLE
+#undef glRenderbufferStorageMultisampleAPPLE
+#undef glRenderbufferStorageMultisampleEXT
+#undef glRenderbufferStorageMultisampleIMG
+#undef glRenderbufferStorageMultisampleNV
+#undef glRenderbufferStorageOES
+#undef glResolveDepthValuesNV
+#undef glResolveMultisampleFramebufferAPPLE
+#undef glResumeTransformFeedback
+#undef glRotatef
+#undef glRotatex
+#undef glRotatexOES
+#undef glSampleCoverage
+#undef glSampleCoveragex
+#undef glSampleCoveragexOES
+#undef glSampleMaski
+#undef glSamplerParameterIiv
+#undef glSamplerParameterIivEXT
+#undef glSamplerParameterIivOES
+#undef glSamplerParameterIuiv
+#undef glSamplerParameterIuivEXT
+#undef glSamplerParameterIuivOES
+#undef glSamplerParameterf
+#undef glSamplerParameterfv
+#undef glSamplerParameteri
+#undef glSamplerParameteriv
+#undef glScalef
+#undef glScalex
+#undef glScalexOES
+#undef glScissor
+#undef glScissorArrayvNV
+#undef glScissorIndexedNV
+#undef glScissorIndexedvNV
+#undef glSelectPerfMonitorCountersAMD
+#undef glSetFenceNV
+#undef glShadeModel
+#undef glShaderBinary
+#undef glShaderSource
+#undef glStartTilingQCOM
+#undef glStencilFillPathInstancedNV
+#undef glStencilFillPathNV
+#undef glStencilFunc
+#undef glStencilFuncSeparate
+#undef glStencilMask
+#undef glStencilMaskSeparate
+#undef glStencilOp
+#undef glStencilOpSeparate
+#undef glStencilStrokePathInstancedNV
+#undef glStencilStrokePathNV
+#undef glStencilThenCoverFillPathInstancedNV
+#undef glStencilThenCoverFillPathNV
+#undef glStencilThenCoverStrokePathInstancedNV
+#undef glStencilThenCoverStrokePathNV
+#undef glSubpixelPrecisionBiasNV
+#undef glTestFenceNV
+#undef glTexBuffer
+#undef glTexBufferEXT
+#undef glTexBufferOES
+#undef glTexBufferRange
+#undef glTexBufferRangeEXT
+#undef glTexBufferRangeOES
+#undef glTexCoordPointer
+#undef glTexEnvf
+#undef glTexEnvfv
+#undef glTexEnvi
+#undef glTexEnviv
+#undef glTexEnvx
+#undef glTexEnvxOES
+#undef glTexEnvxv
+#undef glTexEnvxvOES
+#undef glTexGenfOES
+#undef glTexGenfvOES
+#undef glTexGeniOES
+#undef glTexGenivOES
+#undef glTexGenxOES
+#undef glTexGenxvOES
+#undef glTexImage2D
+#undef glTexImage3D
+#undef glTexImage3DOES
+#undef glTexPageCommitmentEXT
+#undef glTexParameterIiv
+#undef glTexParameterIivEXT
+#undef glTexParameterIivOES
+#undef glTexParameterIuiv
+#undef glTexParameterIuivEXT
+#undef glTexParameterIuivOES
+#undef glTexParameterf
+#undef glTexParameterfv
+#undef glTexParameteri
+#undef glTexParameteriv
+#undef glTexParameterx
+#undef glTexParameterxOES
+#undef glTexParameterxv
+#undef glTexParameterxvOES
+#undef glTexStorage1DEXT
+#undef glTexStorage2D
+#undef glTexStorage2DEXT
+#undef glTexStorage2DMultisample
+#undef glTexStorage3D
+#undef glTexStorage3DEXT
+#undef glTexStorage3DMultisample
+#undef glTexStorage3DMultisampleOES
+#undef glTexSubImage2D
+#undef glTexSubImage3D
+#undef glTexSubImage3DOES
+#undef glTextureStorage1DEXT
+#undef glTextureStorage2DEXT
+#undef glTextureStorage3DEXT
+#undef glTextureViewEXT
+#undef glTextureViewOES
+#undef glTransformFeedbackVaryings
+#undef glTransformPathNV
+#undef glTranslatef
+#undef glTranslatex
+#undef glTranslatexOES
+#undef glUniform1f
+#undef glUniform1fv
+#undef glUniform1i
+#undef glUniform1iv
+#undef glUniform1ui
+#undef glUniform1uiv
+#undef glUniform2f
+#undef glUniform2fv
+#undef glUniform2i
+#undef glUniform2iv
+#undef glUniform2ui
+#undef glUniform2uiv
+#undef glUniform3f
+#undef glUniform3fv
+#undef glUniform3i
+#undef glUniform3iv
+#undef glUniform3ui
+#undef glUniform3uiv
+#undef glUniform4f
+#undef glUniform4fv
+#undef glUniform4i
+#undef glUniform4iv
+#undef glUniform4ui
+#undef glUniform4uiv
+#undef glUniformBlockBinding
+#undef glUniformHandleui64NV
+#undef glUniformHandleui64vNV
+#undef glUniformMatrix2fv
+#undef glUniformMatrix2x3fv
+#undef glUniformMatrix2x3fvNV
+#undef glUniformMatrix2x4fv
+#undef glUniformMatrix2x4fvNV
+#undef glUniformMatrix3fv
+#undef glUniformMatrix3x2fv
+#undef glUniformMatrix3x2fvNV
+#undef glUniformMatrix3x4fv
+#undef glUniformMatrix3x4fvNV
+#undef glUniformMatrix4fv
+#undef glUniformMatrix4x2fv
+#undef glUniformMatrix4x2fvNV
+#undef glUniformMatrix4x3fv
+#undef glUniformMatrix4x3fvNV
+#undef glUnmapBuffer
+#undef glUnmapBufferOES
+#undef glUseProgram
+#undef glUseProgramStages
+#undef glUseProgramStagesEXT
+#undef glValidateProgram
+#undef glValidateProgramPipeline
+#undef glValidateProgramPipelineEXT
+#undef glVertexAttrib1f
+#undef glVertexAttrib1fv
+#undef glVertexAttrib2f
+#undef glVertexAttrib2fv
+#undef glVertexAttrib3f
+#undef glVertexAttrib3fv
+#undef glVertexAttrib4f
+#undef glVertexAttrib4fv
+#undef glVertexAttribBinding
+#undef glVertexAttribDivisor
+#undef glVertexAttribDivisorANGLE
+#undef glVertexAttribDivisorEXT
+#undef glVertexAttribDivisorNV
+#undef glVertexAttribFormat
+#undef glVertexAttribI4i
+#undef glVertexAttribI4iv
+#undef glVertexAttribI4ui
+#undef glVertexAttribI4uiv
+#undef glVertexAttribIFormat
+#undef glVertexAttribIPointer
+#undef glVertexAttribPointer
+#undef glVertexBindingDivisor
+#undef glVertexPointer
+#undef glViewport
+#undef glViewportArrayvNV
+#undef glViewportIndexedfNV
+#undef glViewportIndexedfvNV
+#undef glWaitSync
+#undef glWaitSyncAPPLE
+#undef glWeightPathsNV
+#undef glWeightPointerOES
+
+#endif // HWUI_GLES_WRAP_ENABLED
diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp
new file mode 100644
index 0000000..c4f2e35
--- /dev/null
+++ b/libs/hwui/debug/wrap_gles.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unwrap_gles.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl32.h>
+
+#include <cutils/log.h>
+
+void assertNoGlErrors(const char* apicall) {
+ GLenum status = GL_NO_ERROR;
+ GLenum lastError = GL_NO_ERROR;
+ const char* lastErrorName = nullptr;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ lastError = status;
+ switch (status) {
+ case GL_INVALID_ENUM:
+ ALOGE("GL error: GL_INVALID_ENUM");
+ lastErrorName = "GL_INVALID_ENUM";
+ break;
+ case GL_INVALID_VALUE:
+ ALOGE("GL error: GL_INVALID_VALUE");
+ lastErrorName = "GL_INVALID_VALUE";
+ break;
+ case GL_INVALID_OPERATION:
+ ALOGE("GL error: GL_INVALID_OPERATION");
+ lastErrorName = "GL_INVALID_OPERATION";
+ break;
+ case GL_OUT_OF_MEMORY:
+ ALOGE("GL error: Out of memory!");
+ lastErrorName = "GL_OUT_OF_MEMORY";
+ break;
+ default:
+ ALOGE("GL error: 0x%x", status);
+ lastErrorName = "UNKNOWN";
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR,
+ "%s error! %s (0x%x)", apicall, lastErrorName, lastError);
+}
+
+#define API_ENTRY(x) wrap_##x
+#define CALL_GL_API(x, ...) x(__VA_ARGS__); assertNoGlErrors(#x)
+#define CALL_GL_API_RETURN(x, ...) auto ret = x(__VA_ARGS__);\
+ assertNoGlErrors(#x);\
+ return ret
+
+extern "C" {
+#include <gl2_api.in>
+#include <gl2ext_api.in>
+
+// libGLESv2 handles these specially, so they are not in gl2_api.in
+
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) {
+ CALL_GL_API(glGetBooleanv, pname, data);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) {
+ CALL_GL_API(glGetFloatv, pname, data);
+}
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) {
+ CALL_GL_API(glGetIntegerv, pname, data);
+}
+const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+ CALL_GL_API_RETURN(glGetString, name);
+}
+const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) {
+ CALL_GL_API_RETURN(glGetStringi, name, index);
+}
+void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) {
+ CALL_GL_API(glGetInteger64v, pname, data);
+}
+}
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
new file mode 100644
index 0000000..4a35374
--- /dev/null
+++ b/libs/hwui/debug/wrap_gles.h
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HWUI_GLES_WRAP_ENABLED
+#define HWUI_GLES_WRAP_ENABLED
+
+#define glActiveShaderProgram wrap_glActiveShaderProgram
+#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT
+#define glActiveTexture wrap_glActiveTexture
+#define glAlphaFunc wrap_glAlphaFunc
+#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM
+#define glAlphaFuncx wrap_glAlphaFuncx
+#define glAlphaFuncxOES wrap_glAlphaFuncxOES
+#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL
+#define glAttachShader wrap_glAttachShader
+#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV
+#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD
+#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL
+#define glBeginQuery wrap_glBeginQuery
+#define glBeginQueryEXT wrap_glBeginQueryEXT
+#define glBeginTransformFeedback wrap_glBeginTransformFeedback
+#define glBindAttribLocation wrap_glBindAttribLocation
+#define glBindBuffer wrap_glBindBuffer
+#define glBindBufferBase wrap_glBindBufferBase
+#define glBindBufferRange wrap_glBindBufferRange
+#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT
+#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT
+#define glBindFramebuffer wrap_glBindFramebuffer
+#define glBindFramebufferOES wrap_glBindFramebufferOES
+#define glBindImageTexture wrap_glBindImageTexture
+#define glBindProgramPipeline wrap_glBindProgramPipeline
+#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT
+#define glBindRenderbuffer wrap_glBindRenderbuffer
+#define glBindRenderbufferOES wrap_glBindRenderbufferOES
+#define glBindSampler wrap_glBindSampler
+#define glBindTexture wrap_glBindTexture
+#define glBindTransformFeedback wrap_glBindTransformFeedback
+#define glBindVertexArray wrap_glBindVertexArray
+#define glBindVertexArrayOES wrap_glBindVertexArrayOES
+#define glBindVertexBuffer wrap_glBindVertexBuffer
+#define glBlendBarrier wrap_glBlendBarrier
+#define glBlendBarrierKHR wrap_glBlendBarrierKHR
+#define glBlendBarrierNV wrap_glBlendBarrierNV
+#define glBlendColor wrap_glBlendColor
+#define glBlendEquation wrap_glBlendEquation
+#define glBlendEquationOES wrap_glBlendEquationOES
+#define glBlendEquationSeparate wrap_glBlendEquationSeparate
+#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES
+#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei
+#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT
+#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES
+#define glBlendEquationi wrap_glBlendEquationi
+#define glBlendEquationiEXT wrap_glBlendEquationiEXT
+#define glBlendEquationiOES wrap_glBlendEquationiOES
+#define glBlendFunc wrap_glBlendFunc
+#define glBlendFuncSeparate wrap_glBlendFuncSeparate
+#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES
+#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei
+#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT
+#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES
+#define glBlendFunci wrap_glBlendFunci
+#define glBlendFunciEXT wrap_glBlendFunciEXT
+#define glBlendFunciOES wrap_glBlendFunciOES
+#define glBlendParameteriNV wrap_glBlendParameteriNV
+#define glBlitFramebuffer wrap_glBlitFramebuffer
+#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE
+#define glBlitFramebufferNV wrap_glBlitFramebufferNV
+#define glBufferData wrap_glBufferData
+#define glBufferStorageEXT wrap_glBufferStorageEXT
+#define glBufferSubData wrap_glBufferSubData
+#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus
+#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES
+#define glClear wrap_glClear
+#define glClearBufferfi wrap_glClearBufferfi
+#define glClearBufferfv wrap_glClearBufferfv
+#define glClearBufferiv wrap_glClearBufferiv
+#define glClearBufferuiv wrap_glClearBufferuiv
+#define glClearColor wrap_glClearColor
+#define glClearColorx wrap_glClearColorx
+#define glClearColorxOES wrap_glClearColorxOES
+#define glClearDepthf wrap_glClearDepthf
+#define glClearDepthfOES wrap_glClearDepthfOES
+#define glClearDepthx wrap_glClearDepthx
+#define glClearDepthxOES wrap_glClearDepthxOES
+#define glClearStencil wrap_glClearStencil
+#define glClientActiveTexture wrap_glClientActiveTexture
+#define glClientWaitSync wrap_glClientWaitSync
+#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE
+#define glClipPlanef wrap_glClipPlanef
+#define glClipPlanefIMG wrap_glClipPlanefIMG
+#define glClipPlanefOES wrap_glClipPlanefOES
+#define glClipPlanex wrap_glClipPlanex
+#define glClipPlanexIMG wrap_glClipPlanexIMG
+#define glClipPlanexOES wrap_glClipPlanexOES
+#define glColor4f wrap_glColor4f
+#define glColor4ub wrap_glColor4ub
+#define glColor4x wrap_glColor4x
+#define glColor4xOES wrap_glColor4xOES
+#define glColorMask wrap_glColorMask
+#define glColorMaski wrap_glColorMaski
+#define glColorMaskiEXT wrap_glColorMaskiEXT
+#define glColorMaskiOES wrap_glColorMaskiOES
+#define glColorPointer wrap_glColorPointer
+#define glCompileShader wrap_glCompileShader
+#define glCompressedTexImage2D wrap_glCompressedTexImage2D
+#define glCompressedTexImage3D wrap_glCompressedTexImage3D
+#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES
+#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D
+#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D
+#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES
+#define glCopyBufferSubData wrap_glCopyBufferSubData
+#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV
+#define glCopyImageSubData wrap_glCopyImageSubData
+#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT
+#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES
+#define glCopyPathNV wrap_glCopyPathNV
+#define glCopyTexImage2D wrap_glCopyTexImage2D
+#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D
+#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D
+#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES
+#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE
+#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV
+#define glCoverFillPathNV wrap_glCoverFillPathNV
+#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV
+#define glCoverStrokePathNV wrap_glCoverStrokePathNV
+#define glCoverageMaskNV wrap_glCoverageMaskNV
+#define glCoverageModulationNV wrap_glCoverageModulationNV
+#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV
+#define glCoverageOperationNV wrap_glCoverageOperationNV
+#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL
+#define glCreateProgram wrap_glCreateProgram
+#define glCreateShader wrap_glCreateShader
+#define glCreateShaderProgramv wrap_glCreateShaderProgramv
+#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT
+#define glCullFace wrap_glCullFace
+#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES
+#define glDebugMessageCallback wrap_glDebugMessageCallback
+#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR
+#define glDebugMessageControl wrap_glDebugMessageControl
+#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR
+#define glDebugMessageInsert wrap_glDebugMessageInsert
+#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR
+#define glDeleteBuffers wrap_glDeleteBuffers
+#define glDeleteFencesNV wrap_glDeleteFencesNV
+#define glDeleteFramebuffers wrap_glDeleteFramebuffers
+#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES
+#define glDeletePathsNV wrap_glDeletePathsNV
+#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD
+#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL
+#define glDeleteProgram wrap_glDeleteProgram
+#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines
+#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT
+#define glDeleteQueries wrap_glDeleteQueries
+#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT
+#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers
+#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES
+#define glDeleteSamplers wrap_glDeleteSamplers
+#define glDeleteShader wrap_glDeleteShader
+#define glDeleteSync wrap_glDeleteSync
+#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE
+#define glDeleteTextures wrap_glDeleteTextures
+#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks
+#define glDeleteVertexArrays wrap_glDeleteVertexArrays
+#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES
+#define glDepthFunc wrap_glDepthFunc
+#define glDepthMask wrap_glDepthMask
+#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV
+#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV
+#define glDepthRangef wrap_glDepthRangef
+#define glDepthRangefOES wrap_glDepthRangefOES
+#define glDepthRangex wrap_glDepthRangex
+#define glDepthRangexOES wrap_glDepthRangexOES
+#define glDetachShader wrap_glDetachShader
+#define glDisable wrap_glDisable
+#define glDisableClientState wrap_glDisableClientState
+#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM
+#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray
+#define glDisablei wrap_glDisablei
+#define glDisableiEXT wrap_glDisableiEXT
+#define glDisableiNV wrap_glDisableiNV
+#define glDisableiOES wrap_glDisableiOES
+#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT
+#define glDispatchCompute wrap_glDispatchCompute
+#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect
+#define glDrawArrays wrap_glDrawArrays
+#define glDrawArraysIndirect wrap_glDrawArraysIndirect
+#define glDrawArraysInstanced wrap_glDrawArraysInstanced
+#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE
+#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT
+#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT
+#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV
+#define glDrawBuffers wrap_glDrawBuffers
+#define glDrawBuffersEXT wrap_glDrawBuffersEXT
+#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT
+#define glDrawBuffersNV wrap_glDrawBuffersNV
+#define glDrawElements wrap_glDrawElements
+#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex
+#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT
+#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES
+#define glDrawElementsIndirect wrap_glDrawElementsIndirect
+#define glDrawElementsInstanced wrap_glDrawElementsInstanced
+#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE
+#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex
+#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT
+#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES
+#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT
+#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV
+#define glDrawRangeElements wrap_glDrawRangeElements
+#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex
+#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT
+#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES
+#define glDrawTexfOES wrap_glDrawTexfOES
+#define glDrawTexfvOES wrap_glDrawTexfvOES
+#define glDrawTexiOES wrap_glDrawTexiOES
+#define glDrawTexivOES wrap_glDrawTexivOES
+#define glDrawTexsOES wrap_glDrawTexsOES
+#define glDrawTexsvOES wrap_glDrawTexsvOES
+#define glDrawTexxOES wrap_glDrawTexxOES
+#define glDrawTexxvOES wrap_glDrawTexxvOES
+#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES
+#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES
+#define glEnable wrap_glEnable
+#define glEnableClientState wrap_glEnableClientState
+#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM
+#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray
+#define glEnablei wrap_glEnablei
+#define glEnableiEXT wrap_glEnableiEXT
+#define glEnableiNV wrap_glEnableiNV
+#define glEnableiOES wrap_glEnableiOES
+#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV
+#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD
+#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL
+#define glEndQuery wrap_glEndQuery
+#define glEndQueryEXT wrap_glEndQueryEXT
+#define glEndTilingQCOM wrap_glEndTilingQCOM
+#define glEndTransformFeedback wrap_glEndTransformFeedback
+#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM
+#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM
+#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM
+#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM
+#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM
+#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM
+#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM
+#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM
+#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM
+#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM
+#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM
+#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM
+#define glFenceSync wrap_glFenceSync
+#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE
+#define glFinish wrap_glFinish
+#define glFinishFenceNV wrap_glFinishFenceNV
+#define glFlush wrap_glFlush
+#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange
+#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT
+#define glFogf wrap_glFogf
+#define glFogfv wrap_glFogfv
+#define glFogx wrap_glFogx
+#define glFogxOES wrap_glFogxOES
+#define glFogxv wrap_glFogxv
+#define glFogxvOES wrap_glFogxvOES
+#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV
+#define glFramebufferParameteri wrap_glFramebufferParameteri
+#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer
+#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES
+#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV
+#define glFramebufferTexture wrap_glFramebufferTexture
+#define glFramebufferTexture2D wrap_glFramebufferTexture2D
+#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT
+#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG
+#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES
+#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES
+#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT
+#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer
+#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR
+#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR
+#define glFramebufferTextureOES wrap_glFramebufferTextureOES
+#define glFrontFace wrap_glFrontFace
+#define glFrustumf wrap_glFrustumf
+#define glFrustumfOES wrap_glFrustumfOES
+#define glFrustumx wrap_glFrustumx
+#define glFrustumxOES wrap_glFrustumxOES
+#define glGenBuffers wrap_glGenBuffers
+#define glGenFencesNV wrap_glGenFencesNV
+#define glGenFramebuffers wrap_glGenFramebuffers
+#define glGenFramebuffersOES wrap_glGenFramebuffersOES
+#define glGenPathsNV wrap_glGenPathsNV
+#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD
+#define glGenProgramPipelines wrap_glGenProgramPipelines
+#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT
+#define glGenQueries wrap_glGenQueries
+#define glGenQueriesEXT wrap_glGenQueriesEXT
+#define glGenRenderbuffers wrap_glGenRenderbuffers
+#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES
+#define glGenSamplers wrap_glGenSamplers
+#define glGenTextures wrap_glGenTextures
+#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks
+#define glGenVertexArrays wrap_glGenVertexArrays
+#define glGenVertexArraysOES wrap_glGenVertexArraysOES
+#define glGenerateMipmap wrap_glGenerateMipmap
+#define glGenerateMipmapOES wrap_glGenerateMipmapOES
+#define glGetActiveAttrib wrap_glGetActiveAttrib
+#define glGetActiveUniform wrap_glGetActiveUniform
+#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName
+#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv
+#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv
+#define glGetAttachedShaders wrap_glGetAttachedShaders
+#define glGetAttribLocation wrap_glGetAttribLocation
+#define glGetBooleani_v wrap_glGetBooleani_v
+#define glGetBooleanv wrap_glGetBooleanv
+#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v
+#define glGetBufferParameteriv wrap_glGetBufferParameteriv
+#define glGetBufferPointerv wrap_glGetBufferPointerv
+#define glGetBufferPointervOES wrap_glGetBufferPointervOES
+#define glGetClipPlanef wrap_glGetClipPlanef
+#define glGetClipPlanefOES wrap_glGetClipPlanefOES
+#define glGetClipPlanex wrap_glGetClipPlanex
+#define glGetClipPlanexOES wrap_glGetClipPlanexOES
+#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV
+#define glGetDebugMessageLog wrap_glGetDebugMessageLog
+#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR
+#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM
+#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM
+#define glGetError wrap_glGetError
+#define glGetFenceivNV wrap_glGetFenceivNV
+#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL
+#define glGetFixedv wrap_glGetFixedv
+#define glGetFixedvOES wrap_glGetFixedvOES
+#define glGetFloati_vNV wrap_glGetFloati_vNV
+#define glGetFloatv wrap_glGetFloatv
+#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT
+#define glGetFragDataLocation wrap_glGetFragDataLocation
+#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv
+#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES
+#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv
+#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus
+#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT
+#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR
+#define glGetImageHandleNV wrap_glGetImageHandleNV
+#define glGetInteger64i_v wrap_glGetInteger64i_v
+#define glGetInteger64v wrap_glGetInteger64v
+#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE
+#define glGetIntegeri_v wrap_glGetIntegeri_v
+#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT
+#define glGetIntegerv wrap_glGetIntegerv
+#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV
+#define glGetInternalformativ wrap_glGetInternalformativ
+#define glGetLightfv wrap_glGetLightfv
+#define glGetLightxv wrap_glGetLightxv
+#define glGetLightxvOES wrap_glGetLightxvOES
+#define glGetMaterialfv wrap_glGetMaterialfv
+#define glGetMaterialxv wrap_glGetMaterialxv
+#define glGetMaterialxvOES wrap_glGetMaterialxvOES
+#define glGetMultisamplefv wrap_glGetMultisamplefv
+#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL
+#define glGetObjectLabel wrap_glGetObjectLabel
+#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT
+#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR
+#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel
+#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR
+#define glGetPathCommandsNV wrap_glGetPathCommandsNV
+#define glGetPathCoordsNV wrap_glGetPathCoordsNV
+#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV
+#define glGetPathLengthNV wrap_glGetPathLengthNV
+#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV
+#define glGetPathMetricsNV wrap_glGetPathMetricsNV
+#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV
+#define glGetPathParameterivNV wrap_glGetPathParameterivNV
+#define glGetPathSpacingNV wrap_glGetPathSpacingNV
+#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL
+#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD
+#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD
+#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD
+#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD
+#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD
+#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD
+#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL
+#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL
+#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL
+#define glGetPointerv wrap_glGetPointerv
+#define glGetPointervKHR wrap_glGetPointervKHR
+#define glGetProgramBinary wrap_glGetProgramBinary
+#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES
+#define glGetProgramInfoLog wrap_glGetProgramInfoLog
+#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv
+#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog
+#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT
+#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv
+#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT
+#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex
+#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation
+#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT
+#define glGetProgramResourceName wrap_glGetProgramResourceName
+#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV
+#define glGetProgramResourceiv wrap_glGetProgramResourceiv
+#define glGetProgramiv wrap_glGetProgramiv
+#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT
+#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT
+#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT
+#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv
+#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT
+#define glGetQueryiv wrap_glGetQueryiv
+#define glGetQueryivEXT wrap_glGetQueryivEXT
+#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv
+#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES
+#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv
+#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT
+#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES
+#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv
+#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT
+#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES
+#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv
+#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv
+#define glGetShaderInfoLog wrap_glGetShaderInfoLog
+#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat
+#define glGetShaderSource wrap_glGetShaderSource
+#define glGetShaderiv wrap_glGetShaderiv
+#define glGetString wrap_glGetString
+#define glGetStringi wrap_glGetStringi
+#define glGetSynciv wrap_glGetSynciv
+#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE
+#define glGetTexEnvfv wrap_glGetTexEnvfv
+#define glGetTexEnviv wrap_glGetTexEnviv
+#define glGetTexEnvxv wrap_glGetTexEnvxv
+#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES
+#define glGetTexGenfvOES wrap_glGetTexGenfvOES
+#define glGetTexGenivOES wrap_glGetTexGenivOES
+#define glGetTexGenxvOES wrap_glGetTexGenxvOES
+#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv
+#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv
+#define glGetTexParameterIiv wrap_glGetTexParameterIiv
+#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT
+#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES
+#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv
+#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT
+#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES
+#define glGetTexParameterfv wrap_glGetTexParameterfv
+#define glGetTexParameteriv wrap_glGetTexParameteriv
+#define glGetTexParameterxv wrap_glGetTexParameterxv
+#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES
+#define glGetTextureHandleNV wrap_glGetTextureHandleNV
+#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV
+#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying
+#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE
+#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex
+#define glGetUniformIndices wrap_glGetUniformIndices
+#define glGetUniformLocation wrap_glGetUniformLocation
+#define glGetUniformfv wrap_glGetUniformfv
+#define glGetUniformiv wrap_glGetUniformiv
+#define glGetUniformuiv wrap_glGetUniformuiv
+#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv
+#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv
+#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv
+#define glGetVertexAttribfv wrap_glGetVertexAttribfv
+#define glGetVertexAttribiv wrap_glGetVertexAttribiv
+#define glGetnUniformfv wrap_glGetnUniformfv
+#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT
+#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR
+#define glGetnUniformiv wrap_glGetnUniformiv
+#define glGetnUniformivEXT wrap_glGetnUniformivEXT
+#define glGetnUniformivKHR wrap_glGetnUniformivKHR
+#define glGetnUniformuiv wrap_glGetnUniformuiv
+#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR
+#define glHint wrap_glHint
+#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT
+#define glInterpolatePathsNV wrap_glInterpolatePathsNV
+#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer
+#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer
+#define glIsBuffer wrap_glIsBuffer
+#define glIsEnabled wrap_glIsEnabled
+#define glIsEnabledi wrap_glIsEnabledi
+#define glIsEnablediEXT wrap_glIsEnablediEXT
+#define glIsEnablediNV wrap_glIsEnablediNV
+#define glIsEnablediOES wrap_glIsEnablediOES
+#define glIsFenceNV wrap_glIsFenceNV
+#define glIsFramebuffer wrap_glIsFramebuffer
+#define glIsFramebufferOES wrap_glIsFramebufferOES
+#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV
+#define glIsPathNV wrap_glIsPathNV
+#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV
+#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV
+#define glIsProgram wrap_glIsProgram
+#define glIsProgramPipeline wrap_glIsProgramPipeline
+#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT
+#define glIsQuery wrap_glIsQuery
+#define glIsQueryEXT wrap_glIsQueryEXT
+#define glIsRenderbuffer wrap_glIsRenderbuffer
+#define glIsRenderbufferOES wrap_glIsRenderbufferOES
+#define glIsSampler wrap_glIsSampler
+#define glIsShader wrap_glIsShader
+#define glIsSync wrap_glIsSync
+#define glIsSyncAPPLE wrap_glIsSyncAPPLE
+#define glIsTexture wrap_glIsTexture
+#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV
+#define glIsTransformFeedback wrap_glIsTransformFeedback
+#define glIsVertexArray wrap_glIsVertexArray
+#define glIsVertexArrayOES wrap_glIsVertexArrayOES
+#define glLabelObjectEXT wrap_glLabelObjectEXT
+#define glLightModelf wrap_glLightModelf
+#define glLightModelfv wrap_glLightModelfv
+#define glLightModelx wrap_glLightModelx
+#define glLightModelxOES wrap_glLightModelxOES
+#define glLightModelxv wrap_glLightModelxv
+#define glLightModelxvOES wrap_glLightModelxvOES
+#define glLightf wrap_glLightf
+#define glLightfv wrap_glLightfv
+#define glLightx wrap_glLightx
+#define glLightxOES wrap_glLightxOES
+#define glLightxv wrap_glLightxv
+#define glLightxvOES wrap_glLightxvOES
+#define glLineWidth wrap_glLineWidth
+#define glLineWidthx wrap_glLineWidthx
+#define glLineWidthxOES wrap_glLineWidthxOES
+#define glLinkProgram wrap_glLinkProgram
+#define glLoadIdentity wrap_glLoadIdentity
+#define glLoadMatrixf wrap_glLoadMatrixf
+#define glLoadMatrixx wrap_glLoadMatrixx
+#define glLoadMatrixxOES wrap_glLoadMatrixxOES
+#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES
+#define glLogicOp wrap_glLogicOp
+#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV
+#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV
+#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV
+#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV
+#define glMapBufferOES wrap_glMapBufferOES
+#define glMapBufferRange wrap_glMapBufferRange
+#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT
+#define glMaterialf wrap_glMaterialf
+#define glMaterialfv wrap_glMaterialfv
+#define glMaterialx wrap_glMaterialx
+#define glMaterialxOES wrap_glMaterialxOES
+#define glMaterialxv wrap_glMaterialxv
+#define glMaterialxvOES wrap_glMaterialxvOES
+#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES
+#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV
+#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV
+#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV
+#define glMatrixMode wrap_glMatrixMode
+#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV
+#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV
+#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV
+#define glMemoryBarrier wrap_glMemoryBarrier
+#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion
+#define glMinSampleShading wrap_glMinSampleShading
+#define glMinSampleShadingOES wrap_glMinSampleShadingOES
+#define glMultMatrixf wrap_glMultMatrixf
+#define glMultMatrixx wrap_glMultMatrixx
+#define glMultMatrixxOES wrap_glMultMatrixxOES
+#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT
+#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT
+#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT
+#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES
+#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT
+#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT
+#define glMultiTexCoord4f wrap_glMultiTexCoord4f
+#define glMultiTexCoord4x wrap_glMultiTexCoord4x
+#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES
+#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV
+#define glNormal3f wrap_glNormal3f
+#define glNormal3x wrap_glNormal3x
+#define glNormal3xOES wrap_glNormal3xOES
+#define glNormalPointer wrap_glNormalPointer
+#define glObjectLabel wrap_glObjectLabel
+#define glObjectLabelKHR wrap_glObjectLabelKHR
+#define glObjectPtrLabel wrap_glObjectPtrLabel
+#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR
+#define glOrthof wrap_glOrthof
+#define glOrthofOES wrap_glOrthofOES
+#define glOrthox wrap_glOrthox
+#define glOrthoxOES wrap_glOrthoxOES
+#define glPatchParameteri wrap_glPatchParameteri
+#define glPatchParameteriEXT wrap_glPatchParameteriEXT
+#define glPatchParameteriOES wrap_glPatchParameteriOES
+#define glPathCommandsNV wrap_glPathCommandsNV
+#define glPathCoordsNV wrap_glPathCoordsNV
+#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV
+#define glPathDashArrayNV wrap_glPathDashArrayNV
+#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV
+#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV
+#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV
+#define glPathGlyphsNV wrap_glPathGlyphsNV
+#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV
+#define glPathParameterfNV wrap_glPathParameterfNV
+#define glPathParameterfvNV wrap_glPathParameterfvNV
+#define glPathParameteriNV wrap_glPathParameteriNV
+#define glPathParameterivNV wrap_glPathParameterivNV
+#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV
+#define glPathStencilFuncNV wrap_glPathStencilFuncNV
+#define glPathStringNV wrap_glPathStringNV
+#define glPathSubCommandsNV wrap_glPathSubCommandsNV
+#define glPathSubCoordsNV wrap_glPathSubCoordsNV
+#define glPauseTransformFeedback wrap_glPauseTransformFeedback
+#define glPixelStorei wrap_glPixelStorei
+#define glPointAlongPathNV wrap_glPointAlongPathNV
+#define glPointParameterf wrap_glPointParameterf
+#define glPointParameterfv wrap_glPointParameterfv
+#define glPointParameterx wrap_glPointParameterx
+#define glPointParameterxOES wrap_glPointParameterxOES
+#define glPointParameterxv wrap_glPointParameterxv
+#define glPointParameterxvOES wrap_glPointParameterxvOES
+#define glPointSize wrap_glPointSize
+#define glPointSizePointerOES wrap_glPointSizePointerOES
+#define glPointSizex wrap_glPointSizex
+#define glPointSizexOES wrap_glPointSizexOES
+#define glPolygonModeNV wrap_glPolygonModeNV
+#define glPolygonOffset wrap_glPolygonOffset
+#define glPolygonOffsetx wrap_glPolygonOffsetx
+#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES
+#define glPopDebugGroup wrap_glPopDebugGroup
+#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR
+#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT
+#define glPopMatrix wrap_glPopMatrix
+#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox
+#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT
+#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES
+#define glProgramBinary wrap_glProgramBinary
+#define glProgramBinaryOES wrap_glProgramBinaryOES
+#define glProgramParameteri wrap_glProgramParameteri
+#define glProgramParameteriEXT wrap_glProgramParameteriEXT
+#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV
+#define glProgramUniform1f wrap_glProgramUniform1f
+#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT
+#define glProgramUniform1fv wrap_glProgramUniform1fv
+#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT
+#define glProgramUniform1i wrap_glProgramUniform1i
+#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT
+#define glProgramUniform1iv wrap_glProgramUniform1iv
+#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT
+#define glProgramUniform1ui wrap_glProgramUniform1ui
+#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT
+#define glProgramUniform1uiv wrap_glProgramUniform1uiv
+#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT
+#define glProgramUniform2f wrap_glProgramUniform2f
+#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT
+#define glProgramUniform2fv wrap_glProgramUniform2fv
+#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT
+#define glProgramUniform2i wrap_glProgramUniform2i
+#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT
+#define glProgramUniform2iv wrap_glProgramUniform2iv
+#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT
+#define glProgramUniform2ui wrap_glProgramUniform2ui
+#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT
+#define glProgramUniform2uiv wrap_glProgramUniform2uiv
+#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT
+#define glProgramUniform3f wrap_glProgramUniform3f
+#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT
+#define glProgramUniform3fv wrap_glProgramUniform3fv
+#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT
+#define glProgramUniform3i wrap_glProgramUniform3i
+#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT
+#define glProgramUniform3iv wrap_glProgramUniform3iv
+#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT
+#define glProgramUniform3ui wrap_glProgramUniform3ui
+#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT
+#define glProgramUniform3uiv wrap_glProgramUniform3uiv
+#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT
+#define glProgramUniform4f wrap_glProgramUniform4f
+#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT
+#define glProgramUniform4fv wrap_glProgramUniform4fv
+#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT
+#define glProgramUniform4i wrap_glProgramUniform4i
+#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT
+#define glProgramUniform4iv wrap_glProgramUniform4iv
+#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT
+#define glProgramUniform4ui wrap_glProgramUniform4ui
+#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT
+#define glProgramUniform4uiv wrap_glProgramUniform4uiv
+#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT
+#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV
+#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV
+#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv
+#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT
+#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv
+#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT
+#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv
+#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT
+#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv
+#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT
+#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv
+#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT
+#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv
+#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT
+#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv
+#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT
+#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv
+#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT
+#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv
+#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT
+#define glPushDebugGroup wrap_glPushDebugGroup
+#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR
+#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT
+#define glPushMatrix wrap_glPushMatrix
+#define glQueryCounterEXT wrap_glQueryCounterEXT
+#define glQueryMatrixxOES wrap_glQueryMatrixxOES
+#define glRasterSamplesEXT wrap_glRasterSamplesEXT
+#define glReadBuffer wrap_glReadBuffer
+#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT
+#define glReadBufferNV wrap_glReadBufferNV
+#define glReadPixels wrap_glReadPixels
+#define glReadnPixels wrap_glReadnPixels
+#define glReadnPixelsEXT wrap_glReadnPixelsEXT
+#define glReadnPixelsKHR wrap_glReadnPixelsKHR
+#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler
+#define glRenderbufferStorage wrap_glRenderbufferStorage
+#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample
+#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE
+#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE
+#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT
+#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG
+#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV
+#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES
+#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV
+#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE
+#define glResumeTransformFeedback wrap_glResumeTransformFeedback
+#define glRotatef wrap_glRotatef
+#define glRotatex wrap_glRotatex
+#define glRotatexOES wrap_glRotatexOES
+#define glSampleCoverage wrap_glSampleCoverage
+#define glSampleCoveragex wrap_glSampleCoveragex
+#define glSampleCoveragexOES wrap_glSampleCoveragexOES
+#define glSampleMaski wrap_glSampleMaski
+#define glSamplerParameterIiv wrap_glSamplerParameterIiv
+#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT
+#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES
+#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv
+#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT
+#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES
+#define glSamplerParameterf wrap_glSamplerParameterf
+#define glSamplerParameterfv wrap_glSamplerParameterfv
+#define glSamplerParameteri wrap_glSamplerParameteri
+#define glSamplerParameteriv wrap_glSamplerParameteriv
+#define glScalef wrap_glScalef
+#define glScalex wrap_glScalex
+#define glScalexOES wrap_glScalexOES
+#define glScissor wrap_glScissor
+#define glScissorArrayvNV wrap_glScissorArrayvNV
+#define glScissorIndexedNV wrap_glScissorIndexedNV
+#define glScissorIndexedvNV wrap_glScissorIndexedvNV
+#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD
+#define glSetFenceNV wrap_glSetFenceNV
+#define glShadeModel wrap_glShadeModel
+#define glShaderBinary wrap_glShaderBinary
+#define glShaderSource wrap_glShaderSource
+#define glStartTilingQCOM wrap_glStartTilingQCOM
+#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV
+#define glStencilFillPathNV wrap_glStencilFillPathNV
+#define glStencilFunc wrap_glStencilFunc
+#define glStencilFuncSeparate wrap_glStencilFuncSeparate
+#define glStencilMask wrap_glStencilMask
+#define glStencilMaskSeparate wrap_glStencilMaskSeparate
+#define glStencilOp wrap_glStencilOp
+#define glStencilOpSeparate wrap_glStencilOpSeparate
+#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV
+#define glStencilStrokePathNV wrap_glStencilStrokePathNV
+#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV
+#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV
+#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV
+#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV
+#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV
+#define glTestFenceNV wrap_glTestFenceNV
+#define glTexBuffer wrap_glTexBuffer
+#define glTexBufferEXT wrap_glTexBufferEXT
+#define glTexBufferOES wrap_glTexBufferOES
+#define glTexBufferRange wrap_glTexBufferRange
+#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT
+#define glTexBufferRangeOES wrap_glTexBufferRangeOES
+#define glTexCoordPointer wrap_glTexCoordPointer
+#define glTexEnvf wrap_glTexEnvf
+#define glTexEnvfv wrap_glTexEnvfv
+#define glTexEnvi wrap_glTexEnvi
+#define glTexEnviv wrap_glTexEnviv
+#define glTexEnvx wrap_glTexEnvx
+#define glTexEnvxOES wrap_glTexEnvxOES
+#define glTexEnvxv wrap_glTexEnvxv
+#define glTexEnvxvOES wrap_glTexEnvxvOES
+#define glTexGenfOES wrap_glTexGenfOES
+#define glTexGenfvOES wrap_glTexGenfvOES
+#define glTexGeniOES wrap_glTexGeniOES
+#define glTexGenivOES wrap_glTexGenivOES
+#define glTexGenxOES wrap_glTexGenxOES
+#define glTexGenxvOES wrap_glTexGenxvOES
+#define glTexImage2D wrap_glTexImage2D
+#define glTexImage3D wrap_glTexImage3D
+#define glTexImage3DOES wrap_glTexImage3DOES
+#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT
+#define glTexParameterIiv wrap_glTexParameterIiv
+#define glTexParameterIivEXT wrap_glTexParameterIivEXT
+#define glTexParameterIivOES wrap_glTexParameterIivOES
+#define glTexParameterIuiv wrap_glTexParameterIuiv
+#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT
+#define glTexParameterIuivOES wrap_glTexParameterIuivOES
+#define glTexParameterf wrap_glTexParameterf
+#define glTexParameterfv wrap_glTexParameterfv
+#define glTexParameteri wrap_glTexParameteri
+#define glTexParameteriv wrap_glTexParameteriv
+#define glTexParameterx wrap_glTexParameterx
+#define glTexParameterxOES wrap_glTexParameterxOES
+#define glTexParameterxv wrap_glTexParameterxv
+#define glTexParameterxvOES wrap_glTexParameterxvOES
+#define glTexStorage1DEXT wrap_glTexStorage1DEXT
+#define glTexStorage2D wrap_glTexStorage2D
+#define glTexStorage2DEXT wrap_glTexStorage2DEXT
+#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample
+#define glTexStorage3D wrap_glTexStorage3D
+#define glTexStorage3DEXT wrap_glTexStorage3DEXT
+#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample
+#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES
+#define glTexSubImage2D wrap_glTexSubImage2D
+#define glTexSubImage3D wrap_glTexSubImage3D
+#define glTexSubImage3DOES wrap_glTexSubImage3DOES
+#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT
+#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT
+#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT
+#define glTextureViewEXT wrap_glTextureViewEXT
+#define glTextureViewOES wrap_glTextureViewOES
+#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings
+#define glTransformPathNV wrap_glTransformPathNV
+#define glTranslatef wrap_glTranslatef
+#define glTranslatex wrap_glTranslatex
+#define glTranslatexOES wrap_glTranslatexOES
+#define glUniform1f wrap_glUniform1f
+#define glUniform1fv wrap_glUniform1fv
+#define glUniform1i wrap_glUniform1i
+#define glUniform1iv wrap_glUniform1iv
+#define glUniform1ui wrap_glUniform1ui
+#define glUniform1uiv wrap_glUniform1uiv
+#define glUniform2f wrap_glUniform2f
+#define glUniform2fv wrap_glUniform2fv
+#define glUniform2i wrap_glUniform2i
+#define glUniform2iv wrap_glUniform2iv
+#define glUniform2ui wrap_glUniform2ui
+#define glUniform2uiv wrap_glUniform2uiv
+#define glUniform3f wrap_glUniform3f
+#define glUniform3fv wrap_glUniform3fv
+#define glUniform3i wrap_glUniform3i
+#define glUniform3iv wrap_glUniform3iv
+#define glUniform3ui wrap_glUniform3ui
+#define glUniform3uiv wrap_glUniform3uiv
+#define glUniform4f wrap_glUniform4f
+#define glUniform4fv wrap_glUniform4fv
+#define glUniform4i wrap_glUniform4i
+#define glUniform4iv wrap_glUniform4iv
+#define glUniform4ui wrap_glUniform4ui
+#define glUniform4uiv wrap_glUniform4uiv
+#define glUniformBlockBinding wrap_glUniformBlockBinding
+#define glUniformHandleui64NV wrap_glUniformHandleui64NV
+#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV
+#define glUniformMatrix2fv wrap_glUniformMatrix2fv
+#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv
+#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV
+#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv
+#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV
+#define glUniformMatrix3fv wrap_glUniformMatrix3fv
+#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv
+#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV
+#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv
+#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV
+#define glUniformMatrix4fv wrap_glUniformMatrix4fv
+#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv
+#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV
+#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv
+#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV
+#define glUnmapBuffer wrap_glUnmapBuffer
+#define glUnmapBufferOES wrap_glUnmapBufferOES
+#define glUseProgram wrap_glUseProgram
+#define glUseProgramStages wrap_glUseProgramStages
+#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT
+#define glValidateProgram wrap_glValidateProgram
+#define glValidateProgramPipeline wrap_glValidateProgramPipeline
+#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT
+#define glVertexAttrib1f wrap_glVertexAttrib1f
+#define glVertexAttrib1fv wrap_glVertexAttrib1fv
+#define glVertexAttrib2f wrap_glVertexAttrib2f
+#define glVertexAttrib2fv wrap_glVertexAttrib2fv
+#define glVertexAttrib3f wrap_glVertexAttrib3f
+#define glVertexAttrib3fv wrap_glVertexAttrib3fv
+#define glVertexAttrib4f wrap_glVertexAttrib4f
+#define glVertexAttrib4fv wrap_glVertexAttrib4fv
+#define glVertexAttribBinding wrap_glVertexAttribBinding
+#define glVertexAttribDivisor wrap_glVertexAttribDivisor
+#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE
+#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT
+#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV
+#define glVertexAttribFormat wrap_glVertexAttribFormat
+#define glVertexAttribI4i wrap_glVertexAttribI4i
+#define glVertexAttribI4iv wrap_glVertexAttribI4iv
+#define glVertexAttribI4ui wrap_glVertexAttribI4ui
+#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv
+#define glVertexAttribIFormat wrap_glVertexAttribIFormat
+#define glVertexAttribIPointer wrap_glVertexAttribIPointer
+#define glVertexAttribPointer wrap_glVertexAttribPointer
+#define glVertexBindingDivisor wrap_glVertexBindingDivisor
+#define glVertexPointer wrap_glVertexPointer
+#define glViewport wrap_glViewport
+#define glViewportArrayvNV wrap_glViewportArrayvNV
+#define glViewportIndexedfNV wrap_glViewportIndexedfNV
+#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV
+#define glWaitSync wrap_glWaitSync
+#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE
+#define glWeightPathsNV wrap_glWeightPathsNV
+#define glWeightPointerOES wrap_glWeightPointerOES
+
+#endif // HWUI_GLES_WRAP_ENABLED
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 75dcf16..81363d9 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -241,7 +241,7 @@
const Glop::Mesh::Indices& indices = mesh.indices;
const Glop::Fill& fill = glop.fill;
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// ---------------------------------------------
// ---------- Program + uniform setup ----------
@@ -286,7 +286,7 @@
roundedOutRadius);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// --------------------------------
// ---------- Mesh setup ----------
@@ -339,7 +339,7 @@
// Shader uniforms
SkiaShader::apply(*mCaches, fill.skiaShaderData);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ?
fill.skiaShaderData.bitmapData.bitmapTexture : nullptr;
const AutoTexture autoCleanup(texture);
@@ -349,7 +349,7 @@
// ------------------------------------
blend().setFactors(glop.blend.src, glop.blend.dst);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// ------------------------------------
// ---------- Actual drawing ----------
@@ -379,7 +379,7 @@
glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
// -----------------------------------
// ---------- Mesh teardown ----------
@@ -391,7 +391,7 @@
glDisableVertexAttribArray(colorLocation);
}
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
}
void RenderState::dump() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index cdd2da0..7148c4b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,6 +19,7 @@
#include "AnimationContext.h"
#include "Caches.h"
+#include "Canvas.h"
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
#include "LayerUpdateQueue.h"
@@ -214,13 +215,13 @@
// node(s) are non client / filler nodes.
info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
node->prepareTree(info);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
}
mAnimationContext->runRemainingAnimations(info);
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
freePrefetechedLayers();
- GL_CHECKPOINT();
+ GL_CHECKPOINT(MODERATE);
if (CC_UNLIKELY(!mNativeWindow.get())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -394,7 +395,7 @@
backdropBounds.doIntersect(targetBounds);
// Check if we have to draw something on the left side ...
if (targetBounds.left < contentBounds.left) {
- mCanvas->save(SkCanvas::kClip_SaveFlag);
+ mCanvas->save(SaveFlags::Clip);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
contentBounds.left, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
@@ -407,7 +408,7 @@
// ... or on the right side ...
if (targetBounds.right > contentBounds.right &&
!targetBounds.isEmpty()) {
- mCanvas->save(SkCanvas::kClip_SaveFlag);
+ mCanvas->save(SaveFlags::Clip);
if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
targetBounds.right, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
@@ -420,7 +421,7 @@
// ... or at the top ...
if (targetBounds.top < contentBounds.top &&
!targetBounds.isEmpty()) {
- mCanvas->save(SkCanvas::kClip_SaveFlag);
+ mCanvas->save(SaveFlags::Clip);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
contentBounds.top,
SkRegion::kIntersect_Op)) {
@@ -433,7 +434,7 @@
// ... or at the bottom.
if (targetBounds.bottom > contentBounds.bottom &&
!targetBounds.isEmpty()) {
- mCanvas->save(SkCanvas::kClip_SaveFlag);
+ mCanvas->save(SaveFlags::Clip);
if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
targetBounds.bottom, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
@@ -442,7 +443,7 @@
}
} else if (layer == 1) { // Content
// It gets cropped against the bounds of the backdrop to stay inside.
- mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ mCanvas->save(SaveFlags::MatrixClip);
// We shift and clip the content to match its final location in the window.
const float left = mContentDrawBounds.left;
@@ -468,7 +469,7 @@
bool drew = mCanvas->finish();
#endif
- GL_CHECKPOINT();
+ GL_CHECKPOINT(LOW);
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index db6402c..a5fd712 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -32,7 +32,7 @@
canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
card = TestUtils::createNode(0, 0, 200, 400,
[](RenderProperties& props, TestCanvas& canvas) {
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
{
canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
canvas.translate(100, 100);
@@ -43,7 +43,7 @@
}
canvas.restore();
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
{
SkPath clipCircle;
clipCircle.addCircle(100, 300, 100);
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index c899850..6904bec 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -34,18 +34,18 @@
card = TestUtils::createNode(0, 0, 400, 800,
[](RenderProperties& props, TestCanvas& canvas) {
// nested clipped saveLayers
- canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
canvas.restore();
canvas.restore();
// single unclipped saveLayer
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(0, 400);
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(Color::Green_700);
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 0cba3447..6d27c9d 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -69,7 +69,7 @@
float cellSize = floorf(width / 7 - cellSpace);
// each combination of strokeWidth + style gets a column
- int outerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ int outerCount = canvas.save(SaveFlags::MatrixClip);
SkPaint paint;
paint.setAntiAlias(true);
SkPaint::Style styles[] = {
@@ -79,9 +79,9 @@
for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) {
paint.setStrokeWidth(strokeWidth);
// fill column with each op
- int middleCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ int middleCount = canvas.save(SaveFlags::MatrixClip);
for (auto op : ops) {
- int innerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ int innerCount = canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
op(canvas, cellSize, paint);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 2e59eb4..83af148 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -78,8 +78,8 @@
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
canvas.reset(100, 100);
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.save(SaveFlags::MatrixClip);
MicroBench::DoNotOptimize(&canvas);
canvas.restore();
canvas.restore();
@@ -121,12 +121,12 @@
for (int i = 0; i < iters; ++i) {
canvas.reset(100, 100);
{
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.drawRect(0, 0, 100, 100, rectPaint);
canvas.restore();
}
{
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(10, 10);
canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
canvas.restore();
@@ -151,8 +151,8 @@
StartBenchmarkTiming();
for (int i = 0; i < iters; ++i) {
- state.save(SkCanvas::kMatrixClip_SaveFlag);
- state.save(SkCanvas::kMatrixClip_SaveFlag);
+ state.save(SaveFlags::MatrixClip);
+ state.save(SaveFlags::MatrixClip);
MicroBench::DoNotOptimize(&state);
state.restore();
state.restore();
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 67c95e2..f9c2b67 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -47,7 +47,7 @@
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
for (int i = 0; i < 30; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, paint);
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 4df2687..68d74ee 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -16,6 +16,7 @@
#include "CanvasState.h"
+#include "Canvas.h"
#include "Matrix.h"
#include "Rect.h"
#include "utils/LinearAllocator.h"
@@ -23,7 +24,6 @@
#include <gtest/gtest.h>
#include <SkPath.h>
#include <SkRegion.h>
-#include <SkCanvas.h>
namespace android {
namespace uirenderer {
@@ -83,7 +83,7 @@
state.initializeSaveStack(200, 200,
0, 0, 200, 200, Vector3());
- state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ state.save(SaveFlags::MatrixClip);
{
// rotated clip causes complex clip
state.rotate(10);
@@ -93,7 +93,7 @@
}
state.restore();
- state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ state.save(SaveFlags::MatrixClip);
{
// subtracted clip causes complex clip
EXPECT_TRUE(state.clipIsSimple());
@@ -102,7 +102,7 @@
}
state.restore();
- state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ state.save(SaveFlags::MatrixClip);
{
// complex path causes complex clip
SkPath path;
@@ -119,7 +119,7 @@
state.initializeSaveStack(200, 200,
0, 0, 200, 200, Vector3());
- state.save(SkCanvas::kClip_SaveFlag);
+ state.save(SaveFlags::Clip);
{
state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
@@ -129,7 +129,7 @@
Matrix4 simpleTranslate;
simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SkCanvas::kMatrix_SaveFlag);
+ state.save(SaveFlags::Matrix);
{
state.translate(10, 10, 0);
EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
@@ -143,7 +143,7 @@
state.initializeSaveStack(200, 200,
0, 0, 200, 200, Vector3());
- state.save(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved
+ state.save(SaveFlags::Matrix); // NOTE: clip not saved
{
state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
@@ -153,7 +153,7 @@
Matrix4 simpleTranslate;
simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SkCanvas::kClip_SaveFlag); // NOTE: matrix not saved
+ state.save(SaveFlags::Clip); // NOTE: matrix not saved
{
state.translate(10, 10, 0);
EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index b51bd2f..b75724c 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -167,7 +167,7 @@
TEST(FrameBuilder, simpleRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
@@ -198,7 +198,7 @@
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
for (int i = 0; i < LOOPS; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, SkPaint());
@@ -336,7 +336,7 @@
auto node = TestUtils::createNode(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
canvas.drawLayer(layerUpdater.get());
canvas.restore();
@@ -380,7 +380,7 @@
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(40, 40);
canvas.drawRenderNode(child.get());
canvas.restore();
@@ -448,7 +448,7 @@
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
@@ -512,10 +512,10 @@
auto node = TestUtils::createNode(0, 0, 800, 800,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
{
canvas.drawRect(0, 0, 800, 800, SkPaint());
- canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
{
canvas.drawRect(0, 0, 400, 400, SkPaint());
}
@@ -534,9 +534,9 @@
TEST(FrameBuilder, saveLayer_contentRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
- canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
// draw within save layer may still be recorded, but shouldn't be drawn
canvas.drawRect(200, 200, 400, 400, SkPaint());
@@ -583,7 +583,7 @@
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
@@ -632,12 +632,12 @@
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- int restoreTo = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ int restoreTo = canvas.save(SaveFlags::MatrixClip);
canvas.scale(2, 2);
- canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
- canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
- canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
- canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
+ canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
+ canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
+ canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restoreToCount(restoreTo);
});
@@ -695,9 +695,9 @@
auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped
- canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped
- canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped
+ canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
+ canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
+ canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(200, 200, 300, 300, SkPaint());
canvas.restore();
canvas.restore();
@@ -849,7 +849,7 @@
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
- canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
canvas.drawRenderNode(childPtr);
canvas.restore();
});
@@ -987,7 +987,7 @@
});
auto parent = TestUtils::createNode(0, 0, 100, 100,
[&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
canvas.drawRenderNode(receiverBackground.get());
canvas.drawRenderNode(child.get());
@@ -1071,7 +1071,7 @@
[](RenderProperties& props, RecordingCanvas& canvas) {
// save/restore outside of reorderBarrier, so they don't get moved out of place
canvas.translate(20, 10);
- int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
canvas.insertReorderBarrier(true);
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
canvas.insertReorderBarrier(false);
@@ -1111,7 +1111,7 @@
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
canvas.insertReorderBarrier(true);
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(20, 10);
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
canvas.restore();
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 41e44fc..4a635fb 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -32,7 +32,7 @@
RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index ff098c8..01bfc5a 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -35,7 +35,7 @@
TEST(RecordingCanvas, emptyPlayback) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.restore();
});
playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
@@ -43,7 +43,7 @@
TEST(RecordingCanvas, clipRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
canvas.drawRect(0, 0, 50, 50, SkPaint());
canvas.drawRect(50, 50, 100, 100, SkPaint());
@@ -176,16 +176,16 @@
SkPaint paint;
paint.setColor(SK_ColorBLUE);
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
{
// a background!
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.drawRect(0, 0, 100, 200, paint);
canvas.restore();
}
{
// an image!
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(25, 25);
canvas.scale(2, 2);
canvas.drawBitmap(bitmap, 0, 0, nullptr);
@@ -224,7 +224,7 @@
TEST(RecordingCanvas, saveLayer_simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
});
@@ -258,7 +258,7 @@
TEST(RecordingCanvas, saveLayer_missingRestore) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
// Note: restore omitted, shouldn't result in unmatched save
});
@@ -273,7 +273,7 @@
TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+ canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
});
@@ -305,9 +305,9 @@
TEST(RecordingCanvas, saveLayer_addClipFlag) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+ canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
canvas.restore();
@@ -327,7 +327,7 @@
// shouldn't matter, since saveLayer will clip to its bounds
canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
@@ -348,12 +348,12 @@
TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
canvas.rotate(45);
canvas.translate(-50, -50);
- canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restore();
@@ -374,13 +374,13 @@
TEST(RecordingCanvas, saveLayer_rotateClipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
canvas.rotate(45);
canvas.translate(-200, -200);
// area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
- canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index 55104de..33209759 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -21,10 +21,19 @@
#include "GLUtils.h"
+#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH && !defined(HWUI_GLES_WRAP_ENABLED)
+#error Setting DEBUG_OPENGL to HIGH requires setting HWUI_ENABLE_OPENGL_VALIDATION to true in the Android.mk!
+#endif
+
namespace android {
namespace uirenderer {
bool GLUtils::dumpGLErrors() {
+#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH
+ // If DEBUG_LEVEL_HIGH is set then every GLES call is already wrapped
+ // and asserts that there was no error. So this can just return success.
+ return false;
+#else
bool errorObserved = false;
GLenum status = GL_NO_ERROR;
while ((status = glGetError()) != GL_NO_ERROR) {
@@ -47,6 +56,7 @@
}
}
return errorObserved;
+#endif
}
}; // namespace uirenderer
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index 85a10f9..b49c1eb 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -23,17 +23,22 @@
namespace android {
namespace uirenderer {
+
#if DEBUG_OPENGL
-#define GL_CHECKPOINT() LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
- "GL errors! %s:%d", __FILE__, __LINE__)
+#define GL_CHECKPOINT(LEVEL) \
+ do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
+ LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
+ "GL errors! %s:%d", __FILE__, __LINE__);\
+ } } while (0)
#else
-#define GL_CHECKPOINT()
+#define GL_CHECKPOINT(LEVEL)
#endif
class GLUtils {
public:
/**
* Print out any GL errors with ALOGE, returns true if any errors were found.
+ * You probably want to use GL_CHECKPOINT(LEVEL) instead of calling this directly
*/
static bool dumpGLErrors();
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 86c0e5d..8ef5ca0 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -43,4 +43,10 @@
void onTimeShiftStatusChanged(int status, int seq);
void onTimeShiftStartPositionChanged(long timeMs, int seq);
void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
+
+ // For the recording session
+ void onConnected(int seq);
+ void onRecordingStarted(int seq);
+ void onRecordingStopped(in Uri recordedProgramUri, int seq);
+ void onError(int error, int seq);
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index f8057db..0febc16 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -55,7 +55,8 @@
void addBlockedRating(in String rating, int userId);
void removeBlockedRating(in String rating, int userId);
- void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
+ void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
+ int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
void setMainSession(in IBinder sessionToken, int userId);
@@ -77,12 +78,18 @@
void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId);
+ void timeShiftPlay(in IBinder sessionToken, in Uri recordedProgramUri, int userId);
void timeShiftPause(in IBinder sessionToken, int userId);
void timeShiftResume(in IBinder sessionToken, int userId);
void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
+ // For the recording session
+ void connect(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
+ void startRecording(in IBinder sessionToken, int userId);
+ void stopRecording(in IBinder sessionToken, int userId);
+
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 6792680..3bf415b 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,13 +16,18 @@
package android.media.tv;
+import android.media.tv.TvInputInfo;
+
/**
* Interface to receive callbacks from ITvInputManager regardless of sessions.
* @hide
*/
oneway interface ITvInputManagerCallback {
- void onInputStateChanged(in String inputId, int state);
void onInputAdded(in String inputId);
void onInputRemoved(in String inputId);
void onInputUpdated(in String inputId);
+
+ void onInputStateChanged(in String inputId, int state);
+
+ void onTvInputInfoChanged(in String inputId, in TvInputInfo TvInputInfo);
}
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 7a853d1..bd05184 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -27,10 +27,11 @@
* @hide
*/
oneway interface ITvInputService {
- void registerCallback(ITvInputServiceCallback callback);
+ void registerCallback(in ITvInputServiceCallback callback);
void unregisterCallback(in ITvInputServiceCallback callback);
- void createSession(in InputChannel channel, ITvInputSessionCallback callback,
+ void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
in String inputId);
+ void createRecordingSession(in ITvInputSessionCallback callback, in String inputId);
// For hardware TvInputService
void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 74ab562..9f13882 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -27,4 +27,6 @@
void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo);
void addHdmiTvInput(in int id, in TvInputInfo inputInfo);
void removeTvInput(in String inputId);
+
+ void setTvInputInfo(in String inputId, in TvInputInfo inputInfo);
}
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 6a06b8f..408a762 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -48,9 +48,16 @@
void unblockContent(in String unblockedRating);
+ void timeShiftPlay(in Uri recordedProgramUri);
void timeShiftPause();
void timeShiftResume();
void timeShiftSeekTo(long timeMs);
void timeShiftSetPlaybackParams(in PlaybackParams params);
void timeShiftEnablePositionTracking(boolean enable);
+
+ // For the recording session
+ void connect(in Uri channelUri, in Bundle params);
+ void disconnect();
+ void startRecording();
+ void stopRecording();
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index e9368108..cb6a05e 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -40,4 +40,10 @@
void onTimeShiftStatusChanged(int status);
void onTimeShiftStartPositionChanged(long timeMs);
void onTimeShiftCurrentPositionChanged(long timeMs);
+
+ // For the recording session
+ void onConnected();
+ void onRecordingStarted();
+ void onRecordingStopped(in Uri recordedProgramUri);
+ void onError(int error);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index f8c6f3f..4ac5876 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -59,20 +59,29 @@
private static final int DO_RELAYOUT_OVERLAY_VIEW = 11;
private static final int DO_REMOVE_OVERLAY_VIEW = 12;
private static final int DO_UNBLOCK_CONTENT = 13;
- private static final int DO_TIME_SHIFT_PAUSE = 14;
- private static final int DO_TIME_SHIFT_RESUME = 15;
- private static final int DO_TIME_SHIFT_SEEK_TO = 16;
- private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 17;
- private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 18;
+ private static final int DO_TIME_SHIFT_PLAY = 14;
+ private static final int DO_TIME_SHIFT_PAUSE = 15;
+ private static final int DO_TIME_SHIFT_RESUME = 16;
+ private static final int DO_TIME_SHIFT_SEEK_TO = 17;
+ private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 18;
+ private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 19;
+ private static final int DO_CONNECT = 20;
+ private static final int DO_DISCONNECT = 21;
+ private static final int DO_START_RECORDING = 22;
+ private static final int DO_STOP_RECORDING = 23;
+ private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
private TvInputService.Session mTvInputSessionImpl;
+ private TvInputService.RecordingSession mTvInputRecordingSessionImpl;
+
private InputChannel mChannel;
private TvInputEventReceiver mReceiver;
public ITvInputSessionWrapper(Context context, TvInputService.Session sessionImpl,
InputChannel channel) {
+ mIsRecordingSession = false;
mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
mTvInputSessionImpl = sessionImpl;
mChannel = channel;
@@ -81,9 +90,19 @@
}
}
+ public ITvInputSessionWrapper(Context context,
+ TvInputService.RecordingSession recordingSessionImpl) {
+ mIsRecordingSession = true;
+ mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+ mTvInputRecordingSessionImpl = recordingSessionImpl;
+ }
+
@Override
public void executeMessage(Message msg) {
- if (mTvInputSessionImpl == null) {
+ if (!mIsRecordingSession && mTvInputSessionImpl == null) {
+ return;
+ }
+ if (mIsRecordingSession && mTvInputRecordingSessionImpl == null) {
return;
}
@@ -138,7 +157,12 @@
}
case DO_APP_PRIVATE_COMMAND: {
SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2);
+ if (mIsRecordingSession) {
+ mTvInputRecordingSessionImpl.appPrivateCommand(
+ (String) args.arg1, (Bundle) args.arg2);
+ } else {
+ mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2);
+ }
args.recycle();
break;
}
@@ -160,6 +184,10 @@
mTvInputSessionImpl.unblockContent((String) msg.obj);
break;
}
+ case DO_TIME_SHIFT_PLAY: {
+ mTvInputSessionImpl.timeShiftPlay((Uri) msg.obj);
+ break;
+ }
case DO_TIME_SHIFT_PAUSE: {
mTvInputSessionImpl.timeShiftPause();
break;
@@ -180,6 +208,25 @@
mTvInputSessionImpl.timeShiftEnablePositionTracking((Boolean) msg.obj);
break;
}
+ case DO_CONNECT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputRecordingSessionImpl.connect((Uri) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_DISCONNECT: {
+ mTvInputRecordingSessionImpl.disconnect();
+ mTvInputRecordingSessionImpl = null;
+ break;
+ }
+ case DO_START_RECORDING: {
+ mTvInputRecordingSessionImpl.startRecording();
+ break;
+ }
+ case DO_STOP_RECORDING: {
+ mTvInputRecordingSessionImpl.stopRecording();
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -274,6 +321,12 @@
}
@Override
+ public void timeShiftPlay(Uri recordedProgramUri) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(
+ DO_TIME_SHIFT_PLAY, recordedProgramUri));
+ }
+
+ @Override
public void timeShiftPause() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_TIME_SHIFT_PAUSE));
}
@@ -300,6 +353,28 @@
DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, enable));
}
+ @Override
+ public void connect(Uri channelUri, Bundle params) {
+ // Clear the pending connect requests.
+ mCaller.removeMessages(DO_CONNECT);
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CONNECT, channelUri, params));
+ }
+
+ @Override
+ public void disconnect() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_DISCONNECT));
+ }
+
+ @Override
+ public void startRecording() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_RECORDING));
+ }
+
+ @Override
+ public void stopRecording() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_RECORDING));
+ }
+
private final class TvInputEventReceiver extends InputEventReceiver {
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 7cd086e..62a01dc 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -54,6 +54,7 @@
private static final String PATH_CHANNEL = "channel";
private static final String PATH_PROGRAM = "program";
+ private static final String PATH_RECORDED_PROGRAM = "recorded_program";
private static final String PATH_PASSTHROUGH = "passthrough";
/**
@@ -273,6 +274,15 @@
}
/**
+ * Builds a URI that points to a specific recorded program.
+ *
+ * @param recordedProgramId The ID of the recorded program to point to.
+ */
+ public static final Uri buildRecordedProgramUri(long recordedProgramId) {
+ return ContentUris.withAppendedId(RecordedPrograms.CONTENT_URI, recordedProgramId);
+ }
+
+ /**
* Builds a URI that points to a specific program the user watched.
*
* @param watchedProgramId The ID of the watched program to point to.
@@ -941,6 +951,8 @@
*
* <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
*
+ * <p>This is a required field.
+ *
* <p>Type: INTEGER (long)
*/
public static final String COLUMN_CHANNEL_ID = "channel_id";
@@ -1336,6 +1348,382 @@
}
/**
+ * Column definitions for the recorded TV programs table.
+ *
+ * <p>By default, the query results will be sorted by {@link #COLUMN_START_TIME_UTC_MILLIS} in
+ * ascending order.
+ */
+ public static final class RecordedPrograms implements BaseTvColumns {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_RECORDED_PROGRAM);
+
+ /** The MIME type of a directory of recorded TV programs. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+
+ /** The MIME type of a single recorded TV program. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+
+ /**
+ * The ID of the TV channel that provided this recorded TV program.
+ *
+ * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: INTEGER (long)
+ * @see Programs#COLUMN_CHANNEL_ID
+ */
+ public static final String COLUMN_CHANNEL_ID = Programs.COLUMN_CHANNEL_ID;
+
+ /**
+ * The title of this recorded TV program.
+ *
+ * <p>If this recorded program is an episodic TV show, it is recommended that the title is
+ * the series title and its related fields ({@link #COLUMN_SEASON_NUMBER},
+ * {@link #COLUMN_EPISODE_NUMBER}, and {@link #COLUMN_EPISODE_TITLE}) are filled in.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_TITLE
+ */
+ public static final String COLUMN_TITLE = Programs.COLUMN_TITLE;
+
+ /**
+ * The season number of this recorded TV program for episodic TV shows.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_SEASON_NUMBER
+ */
+ public static final String COLUMN_SEASON_NUMBER = Programs.COLUMN_SEASON_NUMBER;
+
+ /**
+ * The episode number of this recorded TV program for episodic TV shows.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_EPISODE_NUMBER
+ */
+ public static final String COLUMN_EPISODE_NUMBER = Programs.COLUMN_EPISODE_NUMBER;
+
+ /**
+ * The episode title of this recorded TV program for episodic TV shows.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_EPISODE_TITLE
+ */
+ public static final String COLUMN_EPISODE_TITLE = Programs.COLUMN_EPISODE_TITLE;
+
+ /**
+ * The start time of the original TV program, in milliseconds since the epoch.
+ *
+ * <p>Type: INTEGER (long)
+ * @see Programs#COLUMN_START_TIME_UTC_MILLIS
+ */
+ public static final String COLUMN_START_TIME_UTC_MILLIS =
+ Programs.COLUMN_START_TIME_UTC_MILLIS;
+
+ /**
+ * The end time of the original TV program, in milliseconds since the epoch.
+ *
+ * <p>Type: INTEGER (long)
+ * @see Programs#COLUMN_END_TIME_UTC_MILLIS
+ */
+ public static final String COLUMN_END_TIME_UTC_MILLIS = Programs.COLUMN_END_TIME_UTC_MILLIS;
+
+ /**
+ * The comma-separated genre string of this recorded TV program.
+ *
+ * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+ * (For example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+ * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_BROADCAST_GENRE
+ */
+ public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE;
+
+ /**
+ * The comma-separated canonical genre string of this recorded TV program.
+ *
+ * <p>Canonical genres are defined in {@link Programs.Genres}. Use
+ * {@link Programs.Genres#encode Genres.encode()} to create a text that can be stored in
+ * this column. Use {@link Programs.Genres#decode Genres.decode()} to get the canonical
+ * genre strings from the text stored in this column.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_CANONICAL_GENRE
+ * @see Programs.Genres
+ */
+ public static final String COLUMN_CANONICAL_GENRE = Programs.COLUMN_CANONICAL_GENRE;
+
+ /**
+ * The short description of this recorded TV program that is displayed to the user by
+ * default.
+ *
+ * <p>It is recommended to limit the length of the descriptions to 256 characters.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_SHORT_DESCRIPTION
+ */
+ public static final String COLUMN_SHORT_DESCRIPTION = Programs.COLUMN_SHORT_DESCRIPTION;
+
+ /**
+ * The detailed, lengthy description of this recorded TV program that is displayed only when
+ * the user wants to see more information.
+ *
+ * <p>TV input services should leave this field empty if they have no additional details
+ * beyond {@link #COLUMN_SHORT_DESCRIPTION}.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_LONG_DESCRIPTION
+ */
+ public static final String COLUMN_LONG_DESCRIPTION = Programs.COLUMN_LONG_DESCRIPTION;
+
+ /**
+ * The width of the video for this recorded TV program, in the unit of pixels.
+ *
+ * <p>Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video
+ * resolution of the current recorded TV program. Can be empty if it is not known or the
+ * recorded program does not convey any video.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_VIDEO_WIDTH
+ */
+ public static final String COLUMN_VIDEO_WIDTH = Programs.COLUMN_VIDEO_WIDTH;
+
+ /**
+ * The height of the video for this recorded TV program, in the unit of pixels.
+ *
+ * <p>Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video
+ * resolution of the current recorded TV program. Can be empty if it is not known or the
+ * recorded program does not convey any video.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_VIDEO_HEIGHT
+ */
+ public static final String COLUMN_VIDEO_HEIGHT = Programs.COLUMN_VIDEO_HEIGHT;
+
+ /**
+ * The comma-separated audio languages of this recorded TV program.
+ *
+ * <p>This is used to describe available audio languages included in the recorded program.
+ * Use either ISO 639-1 or 639-2/T codes.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_AUDIO_LANGUAGE
+ */
+ public static final String COLUMN_AUDIO_LANGUAGE = Programs.COLUMN_AUDIO_LANGUAGE;
+
+ /**
+ * The comma-separated content ratings of this recorded TV program.
+ *
+ * <p>This is used to describe the content rating(s) of this recorded program. Each
+ * comma-separated content rating sub-string should be generated by calling
+ * {@link TvContentRating#flattenToString}. Note that in most cases the recorded program
+ * content is rated by a single rating system, thus resulting in a corresponding single
+ * sub-string that does not require comma separation and multiple sub-strings appear only
+ * when the recorded program content is rated by two or more content rating systems. If any
+ * of those ratings is specified as "blocked rating" in the user's parental control
+ * settings, the TV input service should block the current content and wait for the signal
+ * that it is okay to unblock.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_CONTENT_RATING
+ */
+ public static final String COLUMN_CONTENT_RATING = Programs.COLUMN_CONTENT_RATING;
+
+ /**
+ * The URI for the poster art of this recorded TV program.
+ *
+ * <p>The data in the column must be a URL, or a URI in one of the following formats:
+ *
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * </li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_POSTER_ART_URI
+ */
+ public static final String COLUMN_POSTER_ART_URI = Programs.COLUMN_POSTER_ART_URI;
+
+ /**
+ * The URI for the thumbnail of this recorded TV program.
+ *
+ * <p>The system can generate a thumbnail from the poster art if this column is not
+ * specified. Thus it is not necessary for TV input services to include a thumbnail if it is
+ * just a scaled image of the poster art.
+ *
+ * <p>The data in the column must be a URL, or a URI in one of the following formats:
+ *
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * </li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ * @see Programs#COLUMN_THUMBNAIL_URI
+ */
+ public static final String COLUMN_THUMBNAIL_URI = Programs.COLUMN_THUMBNAIL_URI;
+
+ /**
+ * The flag indicating whether this recorded TV program is searchable or not.
+ *
+ * <p>The columns of searchable recorded programs can be read by other applications that
+ * have proper permission. Care must be taken not to open sensitive data.
+ *
+ * <p>A value of 1 indicates that the recorded program is searchable and its columns can be
+ * read by other applications, a value of 0 indicates that the recorded program is hidden
+ * and its columns can be read only by the package that owns the recorded program and the
+ * system. If not specified, this value is set to 1 (searchable) by default.
+ *
+ * <p>Type: INTEGER (boolean)
+ * @see Programs#COLUMN_SEARCHABLE
+ */
+ public static final String COLUMN_SEARCHABLE = Programs.COLUMN_SEARCHABLE;
+
+ /**
+ * The URI of the recording data for this recorded program.
+ *
+ * <p>Together with {@link #COLUMN_RECORDING_DATA_BYTES}, applications can use this
+ * information to manage recording storage. The URI should indicate a file or directory with
+ * the scheme {@link android.content.ContentResolver#SCHEME_FILE}.
+ *
+ * <p>Type: TEXT
+ * @see #COLUMN_RECORDING_DATA_BYTES
+ */
+ public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+
+ /**
+ * The data size (in bytes) for this recorded program.
+ *
+ * <p>Together with {@link #COLUMN_RECORDING_DATA_URI}, applications can use this
+ * information to manage recording storage.
+ *
+ * <p>Type: INTEGER (long)
+ * @see #COLUMN_RECORDING_DATA_URI
+ */
+ public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+
+ /**
+ * The duration (in milliseconds) of this recorded program.
+ *
+ * <p>The actual duration of the recorded program can differ from the one calculated by
+ * {@link #COLUMN_END_TIME_UTC_MILLIS} - {@link #COLUMN_START_TIME_UTC_MILLIS} as program
+ * recording can be interrupted in the middle for some reason, resulting in a partially
+ * recorded program, which is still playable.
+ *
+ * <p>Type: INTEGER
+ */
+ public static final String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+
+ /**
+ * The expiration time for this recorded program, in milliseconds since the epoch.
+ *
+ * <p>Recorded TV programs do not expire by default unless explicitly requested by the user
+ * or the user allows applications to delete them in order to free up disk space for future
+ * recording. However, some TV content can have expiration date set by the content provider
+ * when recorded. This field is used to indicate such a restriction.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: INTEGER (long)
+ */
+ public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
+ "recording_expire_time_utc_millis";
+
+
+ /**
+ * Internal data used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Type: BLOB
+ * @see Programs#COLUMN_INTERNAL_PROVIDER_DATA
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_DATA =
+ Programs.COLUMN_INTERNAL_PROVIDER_DATA;
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 =
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG1;
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 =
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG2;
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 =
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG3;
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 =
+ Programs.COLUMN_INTERNAL_PROVIDER_FLAG4;
+
+ /**
+ * The version number of this row entry used by TV input services.
+ *
+ * <p>This is best used by sync adapters to identify the rows to update. The number can be
+ * defined by individual TV input services. One may assign the same value as
+ * {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
+ * broadcast.
+ *
+ * <p>Type: INTEGER
+ * @see Programs#COLUMN_VERSION_NUMBER
+ */
+ public static final String COLUMN_VERSION_NUMBER = Programs.COLUMN_VERSION_NUMBER;
+
+ private RecordedPrograms() {}
+ }
+
+ /**
* Column definitions for the TV programs that the user watched. Applications do not have access
* to this table.
*
@@ -1376,6 +1764,8 @@
/**
* The ID of the TV channel that provides this TV program.
*
+ * <p>This is a required field.
+ *
* <p>Type: INTEGER (long)
*/
public static final String COLUMN_CHANNEL_ID = "channel_id";
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index a3d748e..3960230 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -419,6 +419,27 @@
}
/**
+ * Returns the number of tuners this TV input has.
+ *
+ * <p>This method is valid only for the input of type {@link #TYPE_TUNER}.
+ *
+ * <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
+ * <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
+ * concurrently.
+ *
+ */
+ public int getTunerCount() {
+ return mType == TYPE_TUNER ? 1 : 0;
+ }
+
+ /**
+ * Returns {@code true} if this TV input can record TV programs, {@code false} otherwise.
+ */
+ public boolean canRecord() {
+ return false;
+ }
+
+ /**
* Returns the HDMI device information of this TV input.
* @hide
*/
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 6a13f82..f1de8fd 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -44,6 +45,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
@@ -126,6 +129,35 @@
public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE;
/**
+ * RecordingError when a requested operation cannot be completed due to a problem that does not
+ * fit under any other error code.
+ */
+ public static final int RECORDING_ERROR_UNKNOWN = 0;
+
+ /**
+ * RecordingError when an attempt to connect to a recording session has failed or the
+ * established connection has been disconnected without a known reason.
+ */
+ public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
+
+ /**
+ * RecordingError when recording cannot proceed due to insufficient storage space.
+ */
+ public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2;
+
+ /**
+ * RecordingError when recording cannot proceed because the required recording resource is not
+ * able to be allocated.
+ */
+ public static final int RECORDING_ERROR_RESOURCE_BUSY = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
+ RECORDING_ERROR_INSUFFICIENT_SPACE, RECORDING_ERROR_RESOURCE_BUSY})
+ public @interface RecordingError {}
+
+ /**
* The TV input is connected.
*
* <p>This state indicates that a source device is connected to the input port and is in the
@@ -416,6 +448,39 @@
*/
public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
}
+
+ /**
+ * This is called when a recording session initiated by a call to {@link
+ * TvRecordingClient#connect(String, Uri)} has been established.
+ */
+ void onConnected(Session session) {
+ }
+
+ /**
+ * This is called when TV program recording on the current channel has started.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ */
+ void onRecordingStarted(Session session) {
+ }
+
+ /**
+ * This is called when TV program recording on the current channel has stopped. The passed
+ * URI contains information about the new recorded program.
+ *
+ * @param recordedProgramUri The URI for the new recorded program.
+ * @see android.media.tv.TvContract.RecordedPrograms
+ **/
+ void onRecordingStopped(Session session, Uri recordedProgramUri) {
+ }
+
+ /**
+ * This is called when an issue has occurred before or during recording.
+ *
+ * @param error The error code.
+ */
+ void onError(Session session, @TvInputManager.RecordingError int error) {
+ }
}
private static final class SessionCallbackRecord {
@@ -565,6 +630,46 @@
}
});
}
+
+ // For the recording session only
+ void postConnected() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onConnected(mSession);
+ }
+ });
+ }
+
+ // For the recording session only
+ void postRecordingStarted() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRecordingStarted(mSession);
+ }
+ });
+ }
+
+ // For the recording session only
+ void postRecordingStopped(final Uri recordedProgramUri) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRecordingStopped(mSession, recordedProgramUri);
+ }
+ });
+ }
+
+ // For the recording session only
+ void postError(final int error) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onError(mSession, error);
+ }
+ });
+ }
}
/**
@@ -574,7 +679,7 @@
/**
* This is called when the state of a given TV input is changed.
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
* @param state State of the TV input. The value is one of the following:
* <ul>
* <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
@@ -591,7 +696,7 @@
* <p>Normally it happens when the user installs a new TV input package that implements
* {@link TvInputService} interface.
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
*/
public void onInputAdded(String inputId) {
}
@@ -602,7 +707,7 @@
* <p>Normally it happens when the user uninstalls the previously installed TV input
* package.
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
*/
public void onInputRemoved(String inputId) {
}
@@ -613,12 +718,21 @@
* <p>Normally it happens when a previously installed TV input package is re-installed or
* the media on which a newer version of the package exists becomes available/unavailable.
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
* @hide
*/
@SystemApi
public void onInputUpdated(String inputId) {
}
+
+ /**
+ * This is called when the information about a given TV input is changed.
+ *
+ * @param inputId The ID of the TV input.
+ * @param inputInfo TvInputInfo object that contains the information about the TV input.
+ */
+ public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+ }
}
private static final class TvInputCallbackRecord {
@@ -634,15 +748,6 @@
return mCallback;
}
- public void postInputStateChanged(final String inputId, final int state) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mCallback.onInputStateChanged(inputId, state);
- }
- });
- }
-
public void postInputAdded(final String inputId) {
mHandler.post(new Runnable() {
@Override
@@ -669,6 +774,24 @@
}
});
}
+
+ public void postInputStateChanged(final String inputId, final int state) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onInputStateChanged(inputId, state);
+ }
+ });
+ }
+
+ public void postTvInputInfoChanged(final String inputId, final TvInputInfo inputInfo) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onTvInputInfoChanged(inputId, inputInfo);
+ }
+ });
+ }
}
/**
@@ -876,19 +999,57 @@
record.postTimeShiftCurrentPositionChanged(timeMs);
}
}
- };
- ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
+
@Override
- public void onInputStateChanged(String inputId, int state) {
- synchronized (mLock) {
- mStateMap.put(inputId, state);
- for (TvInputCallbackRecord record : mCallbackRecords) {
- record.postInputStateChanged(inputId, state);
+ public void onConnected(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
}
+ record.postConnected();
}
}
@Override
+ public void onRecordingStarted(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRecordingStarted();
+ }
+ }
+
+ @Override
+ public void onRecordingStopped(Uri recordedProgramUri, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRecordingStopped(recordedProgramUri);
+ }
+ }
+
+ @Override
+ public void onError(int error, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postError(error);
+ }
+ }
+ };
+ ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
+ @Override
public void onInputAdded(String inputId) {
synchronized (mLock) {
mStateMap.put(inputId, INPUT_STATE_CONNECTED);
@@ -916,6 +1077,25 @@
}
}
}
+
+ @Override
+ public void onInputStateChanged(String inputId, int state) {
+ synchronized (mLock) {
+ mStateMap.put(inputId, state);
+ for (TvInputCallbackRecord record : mCallbackRecords) {
+ record.postInputStateChanged(inputId, state);
+ }
+ }
+ }
+
+ @Override
+ public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+ synchronized (mLock) {
+ for (TvInputCallbackRecord record : mCallbackRecords) {
+ record.postTvInputInfoChanged(inputId, inputInfo);
+ }
+ }
+ }
};
try {
if (mService != null) {
@@ -972,7 +1152,7 @@
* <li>{@link #INPUT_STATE_DISCONNECTED}
* </ul>
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
* @throws IllegalArgumentException if the argument is {@code null}.
*/
public int getInputState(@NonNull String inputId) {
@@ -1139,7 +1319,7 @@
* <p>The number of sessions that can be created at the same time is limited by the capability
* of the given TV input.
*
- * @param inputId The id of the TV input.
+ * @param inputId The ID of the TV input.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
* @hide
@@ -1147,6 +1327,28 @@
@SystemApi
public void createSession(@NonNull String inputId, @NonNull final SessionCallback callback,
@NonNull Handler handler) {
+ createSessionInternal(inputId, false, callback, handler);
+ }
+
+ /**
+ * Creates a recording {@link Session} for a given TV input.
+ *
+ * <p>The number of sessions that can be created at the same time is limited by the capability
+ * of the given TV input.
+ *
+ * @param inputId The ID of the TV input.
+ * @param callback A callback used to receive the created session.
+ * @param handler A {@link Handler} that the session creation will be delivered to.
+ * @hide
+ */
+ @SystemApi
+ public void createRecordingSession(@NonNull String inputId,
+ @NonNull final SessionCallback callback, @NonNull Handler handler) {
+ createSessionInternal(inputId, true, callback, handler);
+ }
+
+ private void createSessionInternal(String inputId, boolean isRecordingSession,
+ SessionCallback callback, Handler handler) {
Preconditions.checkNotNull(inputId);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
@@ -1155,7 +1357,7 @@
int seq = mNextSeq++;
mSessionCallbackRecordMap.put(seq, record);
try {
- mService.createSession(mClient, inputId, seq, mUserId);
+ mService.createSession(mClient, inputId, isRecordingSession, seq, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -1171,7 +1373,7 @@
* here. This method is designed to be used with {@link #captureFrame} in
* capture scenarios specifically and not suitable for any other use.
*
- * @param inputId the id of the TV input.
+ * @param inputId The ID of the TV input.
* @return List of {@link TvStreamConfig} which is available for capturing
* of the given TV input.
* @hide
@@ -1188,7 +1390,7 @@
/**
* Take a snapshot of the given TV input into the provided Surface.
*
- * @param inputId the id of the TV input.
+ * @param inputId The ID of the TV input.
* @param surface the {@link Surface} to which the snapshot is captured.
* @param config the {@link TvStreamConfig} which is used for capturing.
* @return true when the {@link Surface} is ready to be captured.
@@ -1607,7 +1809,7 @@
* Returns the selected track for a given type. Returns {@code null} if the information is
* not available or any of the tracks for the given type is not selected.
*
- * @return the ID of the selected track.
+ * @return The ID of the selected track.
* @see #selectTrack
*/
@Nullable
@@ -1697,6 +1899,21 @@
}
/**
+ * Plays a given recorded TV program.
+ */
+ void timeShiftPlay(Uri recordedProgramUri) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.timeShiftPlay(mToken, recordedProgramUri, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Pauses the playback. Call {@link #timeShiftResume()} to restart the playback.
*/
void timeShiftPause() {
@@ -1782,6 +1999,62 @@
}
/**
+ * Connects to a given channel for TV program recording.
+ */
+ void connect(Uri channelUri) {
+ connect(channelUri, null);
+ }
+
+ /**
+ * Tunes to a given channel.
+ *
+ * @param channelUri The URI of a channel.
+ * @param params Extra parameters.
+ */
+ void connect(@NonNull Uri channelUri, Bundle params) {
+ Preconditions.checkNotNull(channelUri);
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.connect(mToken, channelUri, params, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Starts TV program recording for the current recording session.
+ */
+ void startRecording() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.startRecording(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Stops TV program recording for the current recording session.
+ */
+ void stopRecording() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.stopRecording(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)
* TvInputService.Session.appPrivateCommand()} on the current TvView.
*
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 053d43b6..f74ae66 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -136,6 +137,18 @@
}
@Override
+ public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
+ if (cb == null) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = cb;
+ args.arg2 = inputId;
+ mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
+ .sendToTarget();
+ }
+
+ @Override
public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT,
hardwareInfo).sendToTarget();
@@ -174,6 +187,19 @@
public abstract Session onCreateSession(String inputId);
/**
+ * Returns a concrete implementation of {@link RecordingSession}.
+ *
+ * <p>May return {@code null} if this TV input service fails to create a recording session for
+ * some reason.
+ *
+ * @param inputId The ID of the TV input associated with the recording session.
+ */
+ @Nullable
+ public RecordingSession onCreateRecordingSession(String inputId) {
+ return null;
+ }
+
+ /**
* Returns a new {@link TvInputInfo} object if this service is responsible for
* {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
* ignoring all hardware input.
@@ -229,6 +255,25 @@
return null;
}
+
+ /**
+ * Sets the TvInputInfo for this TV input.
+ *
+ * <p>The system service automatically creates the TvInputInfo for each TV input based on
+ * information collected from the AndroidManifest.xml, thus it is not necessary to call this
+ * method unless the TV input has additional information to pass such as ability to record and
+ * tuner count.
+ *
+ * @param inputId The ID of the TV input.
+ * @param inputInfo The TvInputInfo object that contains that new information.
+ */
+ public final void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = inputId;
+ args.arg2 = inputInfo;
+ mServiceHandler.obtainMessage(ServiceHandler.DO_SET_TV_INPUT_INFO, args).sendToTarget();
+ }
+
private boolean isPassthroughInput(String inputId) {
if (mTvInputManager == null) {
mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
@@ -322,7 +367,7 @@
@SystemApi
public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
Preconditions.checkNotNull(eventType);
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
@Override
public void run() {
try {
@@ -347,7 +392,8 @@
* @param channelUri The URI of the new channel.
*/
public void notifyChannelRetuned(final Uri channelUri) {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -387,7 +433,8 @@
// TODO: Validate the track list.
final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks);
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -417,7 +464,8 @@
* @see #onSelectTrack
*/
public void notifyTrackSelected(final int type, final String trackId) {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -443,7 +491,8 @@
* @see #notifyVideoUnavailable
*/
public void notifyVideoAvailable() {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -478,7 +527,8 @@
|| reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) {
throw new IllegalArgumentException("Unknown reason: " + reason);
}
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -518,7 +568,8 @@
* @see TvInputManager
*/
public void notifyContentAllowed() {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -562,7 +613,8 @@
*/
public void notifyContentBlocked(@NonNull final TvContentRating rating) {
Preconditions.checkNotNull(rating);
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -603,7 +655,8 @@
* </ul>
*/
public void notifyTimeShiftStatusChanged(final int status) {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -619,7 +672,8 @@
}
private void notifyTimeShiftStartPositionChanged(final long timeMs) {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -635,7 +689,8 @@
}
private void notifyTimeShiftCurrentPositionChanged(final long timeMs) {
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -665,7 +720,8 @@
if (left > right || top > bottom) {
throw new IllegalArgumentException("Invalid parameter");
}
- executeOrPostRunnable(new Runnable() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
@Override
public void run() {
try {
@@ -858,13 +914,28 @@
}
/**
+ * Called when the application requests to play a given recorded TV program.
+ *
+ * @param recordedProgramUri The URI of a recorded TV program.
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetStartPosition()
+ * @see #onTimeShiftGetCurrentPosition()
+ */
+ public void onTimeShiftPlay(Uri recordedProgramUri) {
+ }
+
+ /**
* Called when the application requests to pause playback.
*
- * @see #onTimeShiftResume
- * @see #onTimeShiftSeekTo
- * @see #onTimeShiftSetPlaybackParams
- * @see #onTimeShiftGetStartPosition
- * @see #onTimeShiftGetCurrentPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetStartPosition()
+ * @see #onTimeShiftGetCurrentPosition()
*/
public void onTimeShiftPause() {
}
@@ -872,11 +943,12 @@
/**
* Called when the application requests to resume playback.
*
- * @see #onTimeShiftPause
- * @see #onTimeShiftSeekTo
- * @see #onTimeShiftSetPlaybackParams
- * @see #onTimeShiftGetStartPosition
- * @see #onTimeShiftGetCurrentPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetStartPosition()
+ * @see #onTimeShiftGetCurrentPosition()
*/
public void onTimeShiftResume() {
}
@@ -888,11 +960,12 @@
* not in the range.
*
* @param timeMs The time position to seek to, in milliseconds since the epoch.
- * @see #onTimeShiftResume
- * @see #onTimeShiftPause
- * @see #onTimeShiftSetPlaybackParams
- * @see #onTimeShiftGetStartPosition
- * @see #onTimeShiftGetCurrentPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetStartPosition()
+ * @see #onTimeShiftGetCurrentPosition()
*/
public void onTimeShiftSeekTo(long timeMs) {
}
@@ -905,11 +978,12 @@
* parameters previously set.
*
* @param params The playback params.
- * @see #onTimeShiftResume
- * @see #onTimeShiftPause
- * @see #onTimeShiftSeekTo
- * @see #onTimeShiftGetStartPosition
- * @see #onTimeShiftGetCurrentPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftGetStartPosition()
+ * @see #onTimeShiftGetCurrentPosition()
*/
public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
}
@@ -925,11 +999,12 @@
* seek to, thus failure to notifying its change immediately might result in bad experience
* where the application allows the user to seek to an invalid time position.
*
- * @see #onTimeShiftResume
- * @see #onTimeShiftPause
- * @see #onTimeShiftSeekTo
- * @see #onTimeShiftSetPlaybackParams
- * @see #onTimeShiftGetCurrentPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetCurrentPosition()
*/
public long onTimeShiftGetStartPosition() {
return TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -944,11 +1019,12 @@
* playback position reported by {@link #onTimeShiftGetStartPosition}. Failure to notifying
* the correct current position might lead to bad user experience.
*
- * @see #onTimeShiftResume
- * @see #onTimeShiftPause
- * @see #onTimeShiftSeekTo
- * @see #onTimeShiftSetPlaybackParams
- * @see #onTimeShiftGetStartPosition
+ * @see #onTimeShiftPlay(Uri)
+ * @see #onTimeShiftResume()
+ * @see #onTimeShiftPause()
+ * @see #onTimeShiftSeekTo(long)
+ * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+ * @see #onTimeShiftGetStartPosition()
*/
public long onTimeShiftGetCurrentPosition() {
return TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -1263,6 +1339,14 @@
}
/**
+ * Calls {@link #onTimeShiftPlay(Uri)}.
+ */
+ void timeShiftPlay(Uri recordedProgramUri) {
+ mCurrentPositionMs = 0;
+ onTimeShiftPlay(recordedProgramUri);
+ }
+
+ /**
* Calls {@link #onTimeShiftPause}.
*/
void timeShiftPause() {
@@ -1385,7 +1469,7 @@
}
}
- private void executeOrPostRunnable(Runnable action) {
+ private void executeOrPostRunnableOnMainThread(Runnable action) {
synchronized(mLock) {
if (mSessionCallback == null) {
// The session is not initialized yet.
@@ -1449,6 +1533,267 @@
}
/**
+ * Base class for derived classes to implement to provide a TV input recording session.
+ */
+ public abstract static class RecordingSession {
+ final Handler mHandler;
+
+ private final Object mLock = new Object();
+ // @GuardedBy("mLock")
+ private ITvInputSessionCallback mSessionCallback;
+ // @GuardedBy("mLock")
+ private final List<Runnable> mPendingActions = new ArrayList<>();
+
+ /**
+ * Creates a new Recording Session for TV program recording.
+ *
+ * @param context The context of the application
+ */
+ public RecordingSession(Context context) {
+ mHandler = new Handler(context.getMainLooper());
+ }
+
+ /**
+ * Informs the application that recording session has been connected.
+ */
+ public void notifyConnected() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyConnected");
+ if (mSessionCallback != null) {
+ mSessionCallback.onConnected();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyConnected", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Informs the application that recording has started.
+ */
+ public void notifyRecordingStarted() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyRecordingStarted");
+ if (mSessionCallback != null) {
+ mSessionCallback.onRecordingStarted();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyRecordingStarted", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Informs the application that recording has stopped successfully. Each TV input service
+ * should create a new data entry in the recorded programs table upon completion of the
+ * recording and send its URI.
+ *
+ * @param recordedProgramUri The URI of the new recorded program.
+ */
+ public void notifyRecordingStopped(final Uri recordedProgramUri) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyRecordingStopped");
+ if (mSessionCallback != null) {
+ mSessionCallback.onRecordingStopped(recordedProgramUri);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyRecordingStopped", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends an error to the application at any moment.
+ *
+ * @param error The error code. Should be one of the followings.
+ * <ul>
+ * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+ * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
+ * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+ * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+ * </ul>
+ */
+ public void notifyError(@TvInputManager.RecordingError final int error) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyError");
+ if (mSessionCallback != null) {
+ mSessionCallback.onError(error);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyError", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Dispatches an event to the application using this recording session.
+ *
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ @SystemApi
+ public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
+ Preconditions.checkNotNull(eventType);
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
+ if (mSessionCallback != null) {
+ mSessionCallback.onSessionEvent(eventType, eventArgs);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in sending event (event=" + eventType + ")", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Called when the recording session is connected.
+ *
+ * @param channelUri The URI of the channel.
+ */
+ public abstract void onConnect(Uri channelUri);
+
+ /**
+ * Called when the recording session is connected.
+ *
+ * @param channelUri The URI of the channel.
+ * @param params Extra parameters.
+ * @hide
+ */
+ @SystemApi
+ public void onConnect(Uri channelUri, Bundle params) {
+ onConnect(channelUri);
+ }
+
+ /**
+ * Called when the application requests to disconnect the current recording session.
+ */
+ public abstract void onDisconnect();
+
+ /**
+ * Called when the application requests to start recording. Recording must start
+ * immediately.
+ *
+ * <p>The session must call either {@link #notifyRecordingStarted()} or
+ * {@link #notifyError(int)}}.
+ */
+ public abstract void onStartRecording();
+
+ /**
+ * Called when the application requests to stop recording. Recording must stop immediately.
+ *
+ * <p>The session must call either {@link #notifyRecordingStopped(Uri)} or
+ * {@link #notifyError(int)}}.
+ */
+ public abstract void onStopRecording();
+
+ /**
+ * Processes a private command sent from the application to the TV input. This can be used
+ * to provide domain-specific features that are only known between certain TV inputs and
+ * their clients.
+ *
+ * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
+ * i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @hide
+ */
+ @SystemApi
+ public void onAppPrivateCommand(@NonNull String action, Bundle data) {
+ }
+
+ /**
+ * Calls {@link #onConnect(Uri, Bundle)}.
+ *
+ */
+ void connect(Uri channelUri, Bundle params) {
+ onConnect(channelUri, params);
+ }
+
+ /**
+ * Calls {@link #onDisconnect()}.
+ *
+ */
+ void disconnect() {
+ onDisconnect();
+ }
+
+ /**
+ * Calls {@link #onStartRecording()}.
+ *
+ */
+ void startRecording() {
+ onStartRecording();
+ }
+
+ /**
+ * Calls {@link #onStopRecording()}.
+ *
+ */
+ void stopRecording() {
+ onStopRecording();
+ }
+
+ /**
+ * Calls {@link #onAppPrivateCommand(String, Bundle)}.
+ */
+ void appPrivateCommand(String action, Bundle data) {
+ onAppPrivateCommand(action, data);
+ }
+
+ private void initialize(ITvInputSessionCallback callback) {
+ synchronized(mLock) {
+ mSessionCallback = callback;
+ for (Runnable runnable : mPendingActions) {
+ runnable.run();
+ }
+ mPendingActions.clear();
+ }
+ }
+
+ private void executeOrPostRunnableOnMainThread(Runnable action) {
+ synchronized(mLock) {
+ if (mSessionCallback == null) {
+ // The session is not initialized yet.
+ mPendingActions.add(action);
+ } else {
+ if (mHandler.getLooper().isCurrentThread()) {
+ action.run();
+ } else {
+ // Posts the runnable if this is not called from the main thread
+ mHandler.post(action);
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Base class for a TV input session which represents an external device connected to a
* hardware TV input.
*
@@ -1588,10 +1933,12 @@
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
private static final int DO_NOTIFY_SESSION_CREATED = 2;
- private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
- private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
- private static final int DO_ADD_HDMI_TV_INPUT = 5;
- private static final int DO_REMOVE_HDMI_TV_INPUT = 6;
+ private static final int DO_CREATE_RECORDING_SESSION = 3;
+ private static final int DO_ADD_HARDWARE_TV_INPUT = 4;
+ private static final int DO_REMOVE_HARDWARE_TV_INPUT = 5;
+ private static final int DO_ADD_HDMI_TV_INPUT = 6;
+ private static final int DO_REMOVE_HDMI_TV_INPUT = 7;
+ private static final int DO_SET_TV_INPUT_INFO = 8;
private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
@@ -1629,6 +1976,18 @@
mCallbacks.finishBroadcast();
}
+ private void broadcastSetTvInputInfo(String inputId, TvInputInfo inputInfo) {
+ int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ mCallbacks.getBroadcastItem(i).setTvInputInfo(inputId, inputInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in broadcastSetTvInputInfo", e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+
@Override
public final void handleMessage(Message msg) {
switch (msg.what) {
@@ -1704,6 +2063,31 @@
args.recycle();
return;
}
+ case DO_CREATE_RECORDING_SESSION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1;
+ String inputId = (String) args.arg2;
+ args.recycle();
+ RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId);
+ if (recordingSessionImpl == null) {
+ try {
+ // Failed to create a recording session.
+ cb.onSessionCreated(null, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onSessionCreated", e);
+ }
+ return;
+ }
+ ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+ recordingSessionImpl);
+ try {
+ cb.onSessionCreated(stub, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onSessionCreated", e);
+ }
+ recordingSessionImpl.initialize(cb);
+ return;
+ }
case DO_ADD_HARDWARE_TV_INPUT: {
TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
@@ -1736,6 +2120,16 @@
}
return;
}
+ case DO_SET_TV_INPUT_INFO: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String inputId = (String) args.arg1;
+ TvInputInfo inputInfo = (TvInputInfo) args.arg2;
+ if (inputInfo != null) {
+ broadcastSetTvInputInfo(inputId, inputInfo);
+ }
+ args.recycle();
+ return;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
new file mode 100644
index 0000000..865e000
--- /dev/null
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * The public interface object used to interact with a specific TV input service for TV program
+ * recording.
+ */
+public class TvRecordingClient {
+ private static final String TAG = "TvRecordingClient";
+ private static final boolean DEBUG = false;
+
+ private final RecordingCallback mCallback;
+ private final Handler mHandler;
+
+ private final TvInputManager mTvInputManager;
+ private TvInputManager.Session mSession;
+ private MySessionCallback mSessionCallback;
+
+ private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>();
+
+ /**
+ * Creates a new TvRecordingClient object.
+ *
+ * @param context The application context to create the TvRecordingClient with.
+ * @param tag A short name for debugging purposes.
+ * @param callback The callback to receive recording status changes.
+ * @param handler The handler to invoke the callback on.
+ */
+ public TvRecordingClient(Context context, String tag, @NonNull RecordingCallback callback,
+ Handler handler) {
+ mCallback = callback;
+ mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
+ mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ }
+
+ /**
+ * Connects to a given input for TV program recording. This will create a new recording session
+ * from the TV input and establishes the connection between the application and the session.
+ *
+ * <p>The recording session will respond by calling
+ * {@link RecordingCallback#onConnected()} or {@link RecordingCallback#onError(int)}.
+ *
+ * @param inputId The ID of the TV input for the given channel.
+ * @param channelUri The URI of a channel.
+ */
+ public void connect(String inputId, Uri channelUri) {
+ connect(inputId, channelUri, null);
+ }
+
+ /**
+ * Connects to a given input for TV program recording. This will create a new recording session
+ * from the TV input and establishes the connection between the application and the session.
+ *
+ * <p>The recording session will respond by calling
+ * {@link RecordingCallback#onConnected()} or {@link RecordingCallback#onError(int)}.
+ *
+ * @param inputId The ID of the TV input for the given channel.
+ * @param channelUri The URI of a channel.
+ * @param params Extra parameters.
+ * @hide
+ */
+ @SystemApi
+ public void connect(String inputId, Uri channelUri, Bundle params) {
+ if (DEBUG) Log.d(TAG, "connect(" + channelUri + ")");
+ if (TextUtils.isEmpty(inputId)) {
+ throw new IllegalArgumentException("inputId cannot be null or an empty string");
+ }
+ if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) {
+ if (mSession != null) {
+ mSession.connect(channelUri, params);
+ } else {
+ mSessionCallback.mChannelUri = channelUri;
+ mSessionCallback.mConnectionParams = params;
+ }
+ } else {
+ resetInternal();
+ mSessionCallback = new MySessionCallback(inputId, channelUri, params);
+ if (mTvInputManager != null) {
+ mTvInputManager.createRecordingSession(inputId, mSessionCallback, mHandler);
+ }
+ }
+ }
+
+ /**
+ * Disconnects the established connection between the application and the recording session.
+ *
+ * <p>The recording session will respond by calling
+ * {@link RecordingCallback#onDisconnected()} or {@link RecordingCallback#onError(int)}.
+ */
+ public void disconnect() {
+ if (DEBUG) Log.d(TAG, "disconnect()");
+ resetInternal();
+ }
+
+ private void resetInternal() {
+ mSessionCallback = null;
+ mPendingAppPrivateCommands.clear();
+ if (mSession != null) {
+ mSession.release();
+ mSession = null;
+ }
+ }
+
+ /**
+ * Starts TV program recording for the current recording session. It is expected that recording
+ * starts immediately after calling this method.
+ *
+ * <p>The recording session will respond by calling
+ * {@link RecordingCallback#onRecordingStarted()} or {@link RecordingCallback#onError(int)}.
+ */
+ public void startRecording() {
+ if (mSession != null) {
+ mSession.startRecording();
+ }
+ }
+
+ /**
+ * Stops TV program recording for the current recording session. It is expected that recording
+ * stops immediately after calling this method.
+ *
+ * <p>The recording session will respond by calling
+ * {@link RecordingCallback#onRecordingStopped(Uri)} or {@link RecordingCallback#onError(int)}.
+ */
+ public void stopRecording() {
+ if (mSession != null) {
+ mSession.stopRecording();
+ }
+ }
+
+ /**
+ * Calls {@link TvInputService.RecordingSession#appPrivateCommand(String, Bundle)
+ * TvInputService.RecordingSession.appPrivateCommand()} on the current TvView.
+ *
+ * @param action The name of the private command to send. This <em>must</em> be a scoped name,
+ * i.e. prefixed with a package name you own, so that different developers will not
+ * create conflicting commands.
+ * @param data An optional bundle to send with the command.
+ * @hide
+ */
+ @SystemApi
+ public void sendAppPrivateCommand(@NonNull String action, Bundle data) {
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("action cannot be null or an empty string");
+ }
+ if (mSession != null) {
+ mSession.sendAppPrivateCommand(action, data);
+ } else {
+ Log.w(TAG, "sendAppPrivateCommand - session not yet created (action \"" + action
+ + "\" pending)");
+ mPendingAppPrivateCommands.add(Pair.create(action, data));
+ }
+ }
+
+ /**
+ * Callback used to receive various status updates on the
+ * {@link android.media.tv.TvInputService.RecordingSession}
+ */
+ public class RecordingCallback {
+ /**
+ * This is called when a recording session initiated by a call to
+ * {@link #connect(String, Uri)} has been established.
+ */
+ public void onConnected() {
+ }
+
+ /**
+ * This is called when the established connection between the application and the recording
+ * session has been disconnected. Disconnection can be initiated either by an explicit
+ * request (i.e. a call to {@link #disconnect()} or by an error on the TV input service
+ * side.
+ */
+ public void onDisconnected() {
+ }
+
+ /**
+ * This is called when TV program recording on the current channel has started.
+ */
+ public void onRecordingStarted() {
+ }
+
+ /**
+ * This is called when TV program recording on the current channel has stopped. The passed
+ * URI contains information about the new recorded program.
+ *
+ * @param recordedProgramUri The URI for the new recorded program.
+ * @see android.media.tv.TvContract.RecordedPrograms
+ */
+ public void onRecordingStopped(Uri recordedProgramUri) {
+ }
+
+ /**
+ * This is called when an issue has occurred before or during recording. If the TV input
+ * service cannot proceed recording due to this error, a call to {@link #onDisconnected()}
+ * is expected to follow.
+ *
+ * @param error The error code. Should be one of the followings.
+ * <ul>
+ * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+ * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
+ * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+ * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+ * </ul>
+ */
+ public void onError(@TvInputManager.RecordingError int error) {
+ }
+
+ /**
+ * This is invoked when a custom event from the bound TV input is sent to this client.
+ *
+ * @param inputId The ID of the TV input bound to this client.
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ @SystemApi
+ public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+ }
+ }
+
+ private class MySessionCallback extends TvInputManager.SessionCallback {
+ final String mInputId;
+ Uri mChannelUri;
+ Bundle mConnectionParams;
+
+ MySessionCallback(String inputId, Uri channelUri, Bundle connectionParams) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
+ mConnectionParams = connectionParams;
+ }
+
+ @Override
+ public void onSessionCreated(TvInputManager.Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onSessionCreated()");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onSessionCreated - session already created");
+ // This callback is obsolete.
+ if (session != null) {
+ session.release();
+ }
+ return;
+ }
+ mSession = session;
+ if (session != null) {
+ // Sends the pending app private commands.
+ for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
+ mSession.sendAppPrivateCommand(command.first, command.second);
+ }
+ mPendingAppPrivateCommands.clear();
+ mSession.connect(mChannelUri, mConnectionParams);
+ } else {
+ mSessionCallback = null;
+ mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+ }
+ }
+
+ @Override
+ public void onSessionReleased(TvInputManager.Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onSessionReleased()");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onSessionReleased - session not created");
+ return;
+ }
+ mSessionCallback = null;
+ mSession = null;
+ mCallback.onDisconnected();
+ }
+
+ @Override
+ public void onRecordingStarted(TvInputManager.Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onRecordingStarted()");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRecordingStarted - session not created");
+ return;
+ }
+ mCallback.onRecordingStarted();
+ }
+
+ @Override
+ public void onRecordingStopped(TvInputManager.Session session, Uri recordedProgramUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onRecordingStopped()");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRecordingStopped - session not created");
+ return;
+ }
+ mCallback.onRecordingStopped(recordedProgramUri);
+ }
+
+ @Override
+ public void onError(TvInputManager.Session session, int error) {
+ if (DEBUG) {
+ Log.d(TAG, "onError()");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onError - session not created");
+ return;
+ }
+ mCallback.onError(error);
+ }
+
+ @Override
+ public void onSessionEvent(TvInputManager.Session session, String eventType,
+ Bundle eventArgs) {
+ if (DEBUG) {
+ Log.d(TAG, "onSessionEvent(" + eventType + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onSessionEvent - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onEvent(mInputId, eventType, eventArgs);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 003a274..0132d24 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -448,6 +448,37 @@
}
/**
+ * Plays a given recorded TV program.
+ *
+ * @param inputId The ID of the TV input that created the given recorded program.
+ * @param recordedProgramUri The URI of a recorded program.
+ */
+ public void timeShiftPlay(String inputId, Uri recordedProgramUri) {
+ if (DEBUG) Log.d(TAG, "timeShiftPlay(" + recordedProgramUri + ")");
+ if (TextUtils.isEmpty(inputId)) {
+ throw new IllegalArgumentException("inputId cannot be null or an empty string");
+ }
+ synchronized (sMainTvViewLock) {
+ if (sMainTvView.get() == null) {
+ sMainTvView = new WeakReference<>(this);
+ }
+ }
+ if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) {
+ if (mSession != null) {
+ mSession.timeShiftPlay(recordedProgramUri);
+ } else {
+ mSessionCallback.mRecordedProgramUri = recordedProgramUri;
+ }
+ } else {
+ resetInternal();
+ mSessionCallback = new MySessionCallback(inputId, recordedProgramUri);
+ if (mTvInputManager != null) {
+ mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ }
+ }
+ }
+
+ /**
* Pauses playback. No-op if it is already paused. Call {@link #timeShiftResume} to resume.
*/
public void timeShiftPause() {
@@ -994,6 +1025,7 @@
final String mInputId;
Uri mChannelUri;
Bundle mTuneParams;
+ Uri mRecordedProgramUri;
MySessionCallback(String inputId, Uri channelUri, Bundle tuneParams) {
mInputId = inputId;
@@ -1001,6 +1033,11 @@
mTuneParams = tuneParams;
}
+ MySessionCallback(String inputId, Uri recordedProgramUri) {
+ mInputId = inputId;
+ mRecordedProgramUri = recordedProgramUri;
+ }
+
@Override
public void onSessionCreated(Session session) {
if (DEBUG) {
@@ -1043,7 +1080,11 @@
if (mCaptionEnabled != null) {
mSession.setCaptionEnabled(mCaptionEnabled);
}
- mSession.tune(mChannelUri, mTuneParams);
+ if (mChannelUri != null) {
+ mSession.tune(mChannelUri, mTuneParams);
+ } else {
+ mSession.timeShiftPlay(mRecordedProgramUri);
+ }
ensurePositionTracking();
} else {
mSessionCallback = null;
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 8ac228e..5a9bc16 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -179,8 +179,8 @@
</plurals>
<!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
<string name="notification_touch_for_details">Tap to view details</string>
- <!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] -->
- <string name="retry">Retry</string>
+ <!-- Label of the close dialog button.[CHAR LIMIT=24] -->
+ <string name="close">Close</string>
<!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
<string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
<!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 7f6f1c6..b8ef5ac 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -33,17 +33,14 @@
import com.android.documentsui.services.FileOperations;
import java.util.ArrayList;
+import java.util.List;
/**
* Alert dialog for failed operations.
*/
-public class FailureDialogFragment extends DialogFragment
- implements DialogInterface.OnClickListener {
+public class FailureDialogFragment extends DialogFragment {
private static final String TAG = "FailureDialogFragment";
- private int mOperationType;
- private ArrayList<DocumentInfo> mFailedSrcList;
-
public static void show(FragmentManager fm, int failure,
ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) {
// TODO: Add support for other failures than copy.
@@ -65,36 +62,25 @@
}
@Override
- public void onClick(DialogInterface dialog, int whichButton) {
- if (whichButton == DialogInterface.BUTTON_POSITIVE) {
- FileOperations.start(
- getActivity(),
- mFailedSrcList,
- (DocumentStack) getActivity().getIntent().getParcelableExtra(
- Shared.EXTRA_STACK),
- mOperationType);
- }
- }
-
- @Override
public Dialog onCreateDialog(Bundle inState) {
super.onCreate(inState);
- mOperationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
- mFailedSrcList = getArguments().getParcelableArrayList(FileOperationService.EXTRA_SRC_LIST);
+ final int operationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
+ final List<DocumentInfo> failedSrcList = getArguments().getParcelableArrayList(
+ FileOperationService.EXTRA_SRC_LIST);
final StringBuilder list = new StringBuilder("<p>");
- for (DocumentInfo documentInfo : mFailedSrcList) {
+ for (DocumentInfo documentInfo : failedSrcList) {
list.append(String.format("• %s<br>", documentInfo.displayName));
}
list.append("</p>");
// TODO: Add support for other file operations.
checkArgument(
- mOperationType == FileOperationService.OPERATION_COPY
- || mOperationType == FileOperationService.OPERATION_MOVE);
+ operationType == FileOperationService.OPERATION_COPY
+ || operationType == FileOperationService.OPERATION_MOVE);
- int messageId = mOperationType == FileOperationService.OPERATION_COPY
+ int messageId = operationType == FileOperationService.OPERATION_COPY
? R.string.copy_failure_alert_content
: R.string.move_failure_alert_content;
@@ -105,8 +91,12 @@
return new AlertDialog.Builder(getActivity())
.setMessage(Html.fromHtml(message))
- .setPositiveButton(R.string.retry, this)
- .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ })
.create();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index 7a238bd..2a0262c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -21,6 +21,7 @@
import android.app.Notification;
import android.app.Notification.Builder;
import android.content.Context;
+import android.net.Uri;
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -85,7 +86,7 @@
if (src.authority.equals(dest.authority)) {
if ((src.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
- dest.derivedUri) == null) {
+ Uri.EMPTY /* Not used yet */, dest.derivedUri) == null) {
onFileFailed(src);
return false;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
index 3c40b67..467d97e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -20,6 +20,7 @@
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.buildRootsUri;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import static com.android.internal.util.Preconditions.checkArgument;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
@@ -37,7 +38,7 @@
import android.provider.DocumentsContract.Root;
import android.support.annotation.Nullable;
import android.test.MoreAsserts;
-import android.util.Log;
+import android.text.TextUtils;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -60,13 +61,13 @@
private final ContentProviderClient mClient;
public DocumentsProviderHelper(String authority, ContentProviderClient client) {
+ checkArgument(!TextUtils.isEmpty(authority));
mAuthority = authority;
mClient = client;
}
public RootInfo getRoot(String documentId) throws RemoteException {
final Uri rootsUri = buildRootsUri(mAuthority);
-
Cursor cursor = null;
try {
cursor = mClient.query(rootsUri, null, null, null, null);
@@ -126,7 +127,6 @@
}
public byte[] readDocument(Uri documentUri) throws RemoteException, IOException {
- Log.d("DocumentsProviderHelper", "Trying to read file contents: " + documentUri);
ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null);
byte[] buf = null;
try (AutoCloseInputStream in = new AutoCloseInputStream(file)) {
@@ -245,21 +245,18 @@
Uri uri = buildChildDocumentsUri(mAuthority, documentId);
List<DocumentInfo> children = new ArrayList<>();
try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) {
- while (cursor.moveToNext()) {
- children.add(DocumentInfo.fromDirectoryCursor(cursor));
+ Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, 100);
+ while (wrapper.moveToNext()) {
+ children.add(DocumentInfo.fromDirectoryCursor(wrapper));
}
}
return children;
}
public void assertFileContents(Uri documentUri, byte[] expected) throws Exception {
- // TODO: Fix this: java.lang.SecurityException:
- // The authority of the uri content:/document/%2Fdata%2Fuser%2F0%2Fcom.android.documentsui.\
- // tests%2Fcache%2FTEST_ROOT_1%2Ftest1.txt does not match the one of the contentProvider: \
- // com.android.documentsui.stubprovider
-// MoreAsserts.assertEquals(
-// "Copied file contents differ",
-// expected, readDocument(documentUri));
+ MoreAsserts.assertEquals(
+ "Copied file contents differ",
+ expected, readDocument(documentUri));
}
public void assertFileContents(String parentId, String fileName, byte[] expected)
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index c6e5531..43527a2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -463,7 +463,8 @@
}
@Override
- public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+ public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+ String targetParentDocumentId)
throws FileNotFoundException {
final File before = getFileForDocId(sourceDocumentId);
final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 66a3247..7b57d89 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -332,23 +332,20 @@
uint64_t offset,
uint32_t size,
void* buf) {
- const uint32_t read_size = static_cast<uint32_t>(std::min(
- static_cast<uint64_t>(size),
- get_file_size(inode) - offset));
ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod(
self_,
app_fuse_get_object_bytes,
inode,
offset,
- read_size)));
+ size)));
if (array.get() == nullptr) {
return -1;
}
ScopedByteArrayRO bytes(env_, array.get());
- if (bytes.size() != read_size || bytes.get() == nullptr) {
+ if (bytes.get() == nullptr) {
return -1;
}
-
+ const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size);
memcpy(buf, bytes.get(), read_size);
return read_size;
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 3381656..3ac1b3d 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -37,10 +37,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -103,18 +103,28 @@
}
@VisibleForTesting
- void onCreateForTesting(
+ boolean onCreateForTesting(
Resources resources,
MtpManager mtpManager,
ContentResolver resolver,
- MtpDatabase database) {
+ MtpDatabase database,
+ StorageManager storageManager) {
mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
mDatabase = database;
mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
+ mAppFuse = new AppFuse(TAG, new AppFuseCallback());
+ // TODO: Mount AppFuse on demands.
+ try {
+ mAppFuse.mount(storageManager);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to start app fuse.", e);
+ return false;
+ }
resume();
+ return true;
}
@Override
@@ -165,7 +175,7 @@
// Fallback to non-seekable file descriptor.
// TODO: Use getPartialObject64 for MTP devices that support Android vendor
// extension.
- if (fileSize <= 0xffffffff) {
+ if (fileSize <= 0xffffffffl) {
return mAppFuse.openFile(Integer.parseInt(documentId));
} else {
return getPipeManager(identifier).readDocument(mMtpManager, identifier);
@@ -305,6 +315,7 @@
throw new RuntimeException(e);
} finally {
mDatabase.close();
+ mAppFuse.close();
super.shutdown();
}
}
@@ -385,9 +396,11 @@
@Override
public byte[] getObjectBytes(int inode, long offset, int size) throws IOException {
final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
- mMtpManager.getPartialObject(
- identifier.mDeviceId, identifier.mObjectHandle, (int) offset, size, mBytes);
- return mBytes;
+ final long readSize = mMtpManager.getPartialObject(
+ identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes);
+ // TODO: Change signature so that getObjectBytes can return read size without copying
+ // bytes.
+ return Arrays.copyOf(mBytes, (int) readSize);
}
@Override
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index b51cf71..7066f7d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -20,18 +20,25 @@
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
import android.provider.DocumentsContract.Root;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.Arrays;
import java.util.concurrent.TimeoutException;
import static com.android.mtp.MtpDatabase.strings;
-@SmallTest
+@MediumTest
public class MtpDocumentsProviderTest extends AndroidTestCase {
private final static Uri ROOTS_URI =
DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
@@ -421,10 +428,71 @@
MtpDocumentsProvider.AUTHORITY, "1")));
}
+ public void testOpenDocument() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+ });
+ final byte[] bytes = "Hello world".getBytes();
+ setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
+ new MtpObjectInfo.Builder()
+ .setName("test.txt")
+ .setObjectHandle(1)
+ .setCompressedSize(bytes.length)
+ .setParent(-1)
+ .build()
+ });
+ mMtpManager.setImportFileBytes(0, 1, bytes);
+ try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
+ final byte[] readBytes = new byte[5];
+ assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET));
+ assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
+ assertTrue(Arrays.equals("world".getBytes(), readBytes));
+
+ assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET));
+ assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
+ assertTrue(Arrays.equals("Hello".getBytes(), readBytes));
+ }
+ }
+
+ public void testOpenDocument_shortBytes() throws Exception {
+ mMtpManager = new TestMtpManager(getContext()) {
+ @Override
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
+ if (objectHandle == 1) {
+ return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle))
+ .setObjectHandle(1).setCompressedSize(1024 * 1024).build();
+ }
+
+ return super.getObjectInfo(deviceId, objectHandle);
+ }
+ };
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+ });
+ final byte[] bytes = "Hello world".getBytes();
+ setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
+ new MtpObjectInfo.Builder()
+ .setName("test.txt")
+ .setObjectHandle(1)
+ .setCompressedSize(bytes.length)
+ .setParent(-1)
+ .build()
+ });
+ mMtpManager.setImportFileBytes(0, 1, bytes);
+ try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
+ final byte[] readBytes = new byte[1024 * 1024];
+ assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length));
+ }
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
- mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ assertTrue(mProvider.onCreateForTesting(
+ mResources, mMtpManager, mResolver, mDatabase, storageManager));
}
private String[] getStrings(Cursor cursor) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index a1732dc..1aaeb60 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -208,4 +208,16 @@
byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException {
return mImportFileBytes.get(pack(deviceId, objectHandle));
}
+
+ @Override
+ long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer)
+ throws IOException {
+ final byte[] bytes = mImportFileBytes.get(pack(deviceId, objectHandle));
+ int i = 0;
+ while (i < size && i + offset < bytes.length) {
+ buffer[i] = bytes[(int) (i + offset)];
+ i++;
+ }
+ return i;
+ }
}
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index 15b2a97..3e1fc4a 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -16,7 +16,8 @@
<resources>
<declare-styleable name="RestrictedPreference">
- <attr name="userRestriction" format="string"/>
+ <attr name="userRestriction" format="string" />
+ <attr name="useAdminDisabledSummary" format="boolean" />
</declare-styleable>
<declare-styleable name="WifiEncryptionState">
<attr name="state_encrypted" format="boolean" />
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6dfa9ad..d3c8416 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -765,4 +765,7 @@
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_full">Full</string>
+ <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
+ <string name="disabled_by_admin_summary_text">Disabled by administrator</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index d69250b..bcb5bd8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -69,6 +69,9 @@
String userRestriction, int userId) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
boolean enforcedByDeviceOwner = false;
@@ -125,6 +128,9 @@
int keyguardNotificationFeatures) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
boolean isDisabledByMultipleAdmins = false;
ComponentName adminComponent = null;
List<ComponentName> admins = dpm.getActiveAdmins();
@@ -170,6 +176,9 @@
if (ipm.getBlockUninstallForUser(packageName, userId)) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
ComponentName admin = dpm.getProfileOwner();
if (admin == null) {
admin = dpm.getDeviceOwnerComponentOnCallingUser();
@@ -197,6 +206,9 @@
}
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
boolean isAccountTypeDisabled = false;
String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled();
for (String type : disabledTypes) {
@@ -221,7 +233,7 @@
public static EnforcedAdmin checkIfAutoTimeRequired(Context context) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- if (dpm.getAutoTimeRequired()) {
+ if (dpm == null || !dpm.getAutoTimeRequired()) {
return null;
}
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
@@ -240,6 +252,9 @@
public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
boolean isDisabledByMultipleAdmins = false;
ComponentName adminComponent = null;
List<ComponentName> admins = dpm.getActiveAdmins();
@@ -271,6 +286,9 @@
public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ if (dpm == null) {
+ return null;
+ }
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
if (adminComponent != null) {
return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 13a46d0..810f6eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -65,6 +65,10 @@
}
}
+ public void useAdminDisabledSummary(boolean useSummary) {
+ mHelper.useAdminDisabledSummary(useSummary);
+ }
+
@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
mHelper.onAttachedToHierarchy();
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 06aba96..9bd4eb1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -27,6 +27,7 @@
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.View;
import android.widget.TextView;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -44,6 +45,7 @@
private boolean mDisabledByAdmin;
private EnforcedAdmin mEnforcedAdmin;
private String mAttrUserRestriction = null;
+ private boolean mUseAdminDisabledSummary = false;
public RestrictedPreferenceHelper(Context context, Preference preference,
AttributeSet attrs) {
@@ -68,6 +70,14 @@
}
}
mAttrUserRestriction = data == null ? null : data.toString();
+
+ final TypedValue useAdminDisabledSummary =
+ attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary);
+ if (useAdminDisabledSummary != null) {
+ mUseAdminDisabledSummary =
+ (useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN
+ && useAdminDisabledSummary.data != 0);
+ }
}
}
@@ -82,6 +92,21 @@
holder.itemView.setEnabled(true);
}
}
+ if (mUseAdminDisabledSummary) {
+ final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
+ if (summaryView != null) {
+ if (mDisabledByAdmin) {
+ summaryView.setText(R.string.disabled_by_admin_summary_text);
+ summaryView.setVisibility(View.VISIBLE);
+ } else {
+ summaryView.setVisibility(View.GONE);
+ }
+ }
+ }
+ }
+
+ public void useAdminDisabledSummary(boolean useSummary) {
+ mUseAdminDisabledSummary = useSummary;
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 84e2bff..6cae8aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -65,6 +65,10 @@
}
}
+ public void useAdminDisabledSummary(boolean useSummary) {
+ mHelper.useAdminDisabledSummary(useSummary);
+ }
+
@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
mHelper.onAttachedToHierarchy();
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index 66233b8..f5a2aae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -29,12 +29,6 @@
public class TetherUtil {
- // Types of tethering.
- public static final int TETHERING_INVALID = -1;
- public static final int TETHERING_WIFI = 0;
- public static final int TETHERING_USB = 1;
- public static final int TETHERING_BLUETOOTH = 2;
-
// Extras used for communicating with the TetherService.
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
@@ -43,14 +37,6 @@
* Tells the service to run a provision check now.
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
- /**
- * Enables wifi tethering if the provision check is successful. Used by
- * QS to enable tethering.
- */
- public static final String EXTRA_ENABLE_WIFI_TETHER = "extraEnableWifiTether";
-
- public static ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
public static boolean setWifiTethering(boolean enable, Context context) {
final WifiManager wifiManager =
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
index 2fd043f..dc6002d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
@@ -61,6 +61,8 @@
@Override
public void onClick(DialogInterface dialog, int which) {
UserHandle user = mSelectedTile.userHandle.get(which);
+ // Show menu on top level items.
+ mSelectedTile.intent.putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true);
getActivity().startActivityAsUser(mSelectedTile.intent, user);
((SettingsDrawerActivity) getActivity()).onProfileTileOpen();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 3fc0c22..56c4edb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -48,6 +48,8 @@
protected static final boolean DEBUG_TIMING = false;
private static final String TAG = "SettingsDrawerActivity";
+ static final String EXTRA_SHOW_MENU = "show_drawer_menu";
+
private static List<DashboardCategory> sDashboardCategories;
private static HashMap<Pair<String, String>, Tile> sTileCache;
@@ -56,6 +58,7 @@
private SettingsDrawerAdapter mDrawerAdapter;
private DrawerLayout mDrawerLayout;
+ private boolean mShowingMenu;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -94,7 +97,7 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerLayout != null && item.getItemId() == android.R.id.home
+ if (mShowingMenu && mDrawerLayout != null && item.getItemId() == android.R.id.home
&& mDrawerAdapter.getCount() != 0) {
openDrawer();
return true;
@@ -116,6 +119,9 @@
new CategoriesUpdater().execute();
}
+ if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+ showMenuIcon();
+ }
}
@Override
@@ -171,13 +177,17 @@
mDrawerAdapter.updateCategories();
if (mDrawerAdapter.getCount() != 0) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
- getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
- getActionBar().setDisplayHomeAsUpEnabled(true);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
}
+ public void showMenuIcon() {
+ mShowingMenu = true;
+ getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
public List<DashboardCategory> getDashboardCategories() {
if (sDashboardCategories == null) {
sTileCache = new HashMap<>();
@@ -204,8 +214,12 @@
ProfileSelectDialog.show(getFragmentManager(), tile);
return false;
} else if (numUserHandles == 1) {
+ // Show menu on top level items.
+ tile.intent.putExtra(EXTRA_SHOW_MENU, true);
startActivityAsUser(tile.intent, tile.userHandle.get(0));
} else {
+ // Show menu on top level items.
+ tile.intent.putExtra(EXTRA_SHOW_MENU, true);
startActivity(tile.intent);
}
return true;
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 2377684..ab590f8 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -70,6 +70,18 @@
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+ <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
+ android:background="@drawable/ripple_drawable" >
+ <ImageView android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 543a2f3..8ae2d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -73,7 +73,7 @@
mCurrentTiles = tileSpecs;
final TileGroup group = new TileGroup("com.android.settings", mContext);
String possible = mContext.getString(R.string.quick_settings_tiles_default)
- + ",user,hotspot,inversion,saver";
+ + ",hotspot,inversion,saver";
String[] possibleTiles = possible.split(",");
for (int i = 0; i < possibleTiles.length; i++) {
final String spec = possibleTiles[i];
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 881aa6a..5cd540b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -26,10 +26,6 @@
*/
public class RecentsDebugFlags implements TunerService.Tunable {
- private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
- private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
- private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
-
public static class Static {
// Enables debug drawing for the transition thumbnail
public static final boolean EnableTransitionThumbnailDebugMode = false;
@@ -39,18 +35,23 @@
public static final boolean DisableBackgroundCache = false;
// Enables the task affiliations
public static final boolean EnableAffiliatedTaskGroups = true;
- // Enables the simulated task affiliations
- public static final boolean EnableSimulatedTaskGroups = false;
- // Defines the number of mock task affiliations per group
- public static final int TaskAffiliationsGroupCount = 12;
+
// Enables us to create mock recents tasks
- public static final boolean EnableSystemServicesProxy = false;
+ public static final boolean EnableMockTasks = false;
// Defines the number of mock recents packages to create
- public static final int SystemServicesProxyMockPackageCount = 3;
+ public static final int MockTasksPackageCount = 3;
// Defines the number of mock recents tasks to create
- public static final int SystemServicesProxyMockTaskCount = 100;
+ public static final int MockTaskCount = 100;
+ // Enables the simulated task affiliations
+ public static final boolean EnableMockTaskGroups = false;
+ // Defines the number of mock task affiliations per group
+ public static final int MockTaskGroupsTaskCount = 12;
}
+ private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
+ private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
+ private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
+
private boolean mFastToggleRecents;
private boolean mFastToggleIndicator;
private boolean mInitialStatePaging;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index f8cbf65..5f11bee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -108,6 +108,10 @@
public void onActivityPinned() {
}
+ @Override
+ public void onPinnedActivityRestartAttempt() {
+ }
+
/** Preloads the next task */
public void run() {
RecentsConfiguration config = Recents.getConfiguration();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3f52ae8..87cfcff1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -151,7 +151,7 @@
// Resolve the assist intent
mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
// Create a dummy icon
mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
mDummyIcon.eraseColor(0xFF999999);
@@ -164,20 +164,20 @@
if (mAm == null) return null;
// If we are mocking, then create some recent tasks
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
ArrayList<ActivityManager.RecentTaskInfo> tasks =
new ArrayList<ActivityManager.RecentTaskInfo>();
- int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.SystemServicesProxyMockTaskCount);
+ int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
for (int i = 0; i < count; i++) {
// Create a dummy component name
- int packageIndex = i % RecentsDebugFlags.Static.SystemServicesProxyMockPackageCount;
+ int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
ComponentName cn = new ComponentName("com.android.test" + packageIndex,
"com.android.test" + i + ".Activity");
String description = "" + i + " - " +
Long.toString(Math.abs(new Random().nextLong()), 36);
// Create the recent task info
ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
- rti.id = rti.persistentId = i;
+ rti.id = rti.persistentId = rti.affiliatedTaskId = i;
rti.baseIntent = new Intent();
rti.baseIntent.setComponent(cn);
rti.description = description;
@@ -418,7 +418,7 @@
if (mAm == null) return null;
// If we are mocking, then just return a dummy thumbnail
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
Bitmap.Config.ARGB_8888);
thumbnail.eraseColor(0xff333333);
@@ -484,7 +484,7 @@
/** Moves a task to the front with the specified activity options. */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return;
if (opts != null) {
mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
@@ -497,7 +497,7 @@
/** Removes the task */
public void removeTask(final int taskId) {
if (mAm == null) return;
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return;
// Remove the task.
BackgroundThread.getHandler().post(new Runnable() {
@@ -528,7 +528,7 @@
*/
public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
if (mIpm == null) return null;
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
+ if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
try {
return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
@@ -545,7 +545,7 @@
*/
public ActivityInfo getActivityInfo(ComponentName cn) {
if (mPm == null) return null;
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
+ if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
try {
return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
@@ -562,7 +562,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
return "Recent Task: " + userId;
}
@@ -576,7 +576,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
return "Recent Task App: " + userId;
}
@@ -588,6 +588,11 @@
* description joins the app and activity labels.
*/
public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return "Recent Task Content Description: " + userId;
+ }
+
String activityLabel = info.loadLabel(mPm).toString();
String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
@@ -604,7 +609,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
return new ColorDrawable(0xFF666666);
}
@@ -620,7 +625,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
return new ColorDrawable(0xFF666666);
}
@@ -635,7 +640,7 @@
int userId, Resources res) {
// If we are mocking, then return a mock label
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
return new ColorDrawable(0xFF666666);
}
@@ -673,7 +678,7 @@
/** Returns the package name of the home activity. */
public String getHomeActivityPackageName() {
if (mPm == null) return null;
- if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return null;
ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 9cdd703..d15828a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -22,6 +22,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.os.Debug;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -31,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
@@ -130,6 +132,9 @@
R.string.accessibility_recents_item_will_be_dismissed);
long lastStackActiveTime = Prefs.getLong(mContext,
Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ lastStackActiveTime = 0;
+ }
long newLastStackActiveTime = -1;
int taskCount = mRawTasks.size();
for (int i = 0; i < taskCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index de1daa8..66eeac6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -776,7 +776,7 @@
* Temporary: This method will simulate affiliation groups by
*/
public void createAffiliatedGroupings(Context context) {
- if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) {
+ if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
// Sort all tasks by increasing firstActiveTime of the task
ArrayList<Task> tasks = mStackTaskList.getTasks();
@@ -792,7 +792,7 @@
String prevPackage = "";
int prevAffiliation = -1;
Random r = new Random();
- int groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
+ int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
String packageName = t.key.getComponent().getPackageName();
@@ -807,7 +807,7 @@
addGroup(group);
prevAffiliation = affiliation;
prevPackage = packageName;
- groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
+ groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
}
group.addTask(t);
taskMap.put(t.key, t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
new file mode 100644
index 0000000..9f2b00a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * This is an optimized FrameLayout whose layout is completely directed by its parent, and as a
+ * result, does not propagate <code>requestLayout()</code> up the view hierarchy. Instead, it will
+ * relayout its children with the last known layout bounds when a layout is requested from a child
+ * view.
+ */
+public class FixedSizeFrameLayout extends FrameLayout {
+
+ private final Rect mLayoutBounds = new Rect();
+
+ public FixedSizeFrameLayout(Context context) {
+ super(context);
+ }
+
+ public FixedSizeFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureContents(MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec));
+ }
+
+ @Override
+ protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mLayoutBounds.set(left, top, right, bottom);
+ layoutContents(mLayoutBounds, changed);
+ }
+
+ @Override
+ public final void requestLayout() {
+ // The base ViewGroup constructor attempts to call requestLayout() before this class's
+ // members are initialized so we should just propagate in that case
+ if (mLayoutBounds == null || mLayoutBounds.isEmpty()) {
+ super.requestLayout();
+ } else {
+ // If we are already laid out, then just reuse the same bounds to layout the children
+ // (but not itself)
+ // TODO: Investigate whether we should coalesce these to the next frame if needed
+ measureContents(getMeasuredWidth(), getMeasuredHeight());
+ layoutContents(mLayoutBounds, false);
+ }
+ }
+
+ /**
+ * Measures the contents of this fixed layout.
+ */
+ protected void measureContents(int width, int height) {
+ super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+ }
+
+ /**
+ * Lays out the contents of this fixed layout.
+ */
+ protected void layoutContents(Rect bounds, boolean changed) {
+ super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
index 3f5d0a8..f5ab01f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -23,13 +23,13 @@
import android.widget.ImageView;
/**
- * This is an optimized ImageView that does not trigger a requestLayout() or invalidate() when
- * setting the image to Null.
+ * This is an optimized ImageView that does not trigger a <code>requestLayout()</code> or
+ * <code>invalidate()</code> when setting the image to <code>null</code>.
*/
public class FixedSizeImageView extends ImageView {
- boolean mAllowRelayout = true;
- boolean mAllowInvalidate = true;
+ private boolean mAllowRelayout = true;
+ private boolean mAllowInvalidate = true;
public FixedSizeImageView(Context context) {
this(context, null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fe9c68e..ccc8581 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1375,11 +1375,6 @@
// Report that this tasks's data is no longer being used
Recents.getTaskLoader().unloadTaskData(task);
- // Detach the view from the hierarchy
- detachViewFromParent(tv);
- // Update the task views list after removing the task view
- updateTaskViewsList();
-
// Reset the view properties and view state
tv.resetViewProperties();
tv.setFocusedState(false, false /* requestViewFocus */);
@@ -1387,19 +1382,15 @@
if (mScreenPinningEnabled) {
tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
}
+
+ // Detach the view from the hierarchy
+ detachViewFromParent(tv);
+ // Update the task views list after removing the task view
+ updateTaskViewsList();
}
@Override
public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
- // Rebind the task and request that this task's data be filled into the TaskView
- tv.onTaskBound(task);
-
- // Load the task data
- Recents.getTaskLoader().loadTaskData(task);
-
- // If the doze trigger has already fired, then update the state for this task view
- tv.setNoUserInteractionState();
-
// Find the index where this task should be placed in the stack
int taskIndex = mStack.indexOfStackTask(task);
int insertIndex = findTaskViewInsertIndex(task, taskIndex);
@@ -1413,6 +1404,15 @@
// Update the task views list after adding the new task view
updateTaskViewsList();
+ // Rebind the task and request that this task's data be filled into the TaskView
+ tv.onTaskBound(task);
+
+ // Load the task data
+ Recents.getTaskLoader().loadTaskData(task);
+
+ // If the doze trigger has already fired, then update the state for this task view
+ tv.setNoUserInteractionState();
+
// Set the new state for this view, including the callbacks and view clipping
tv.setCallbacks(this);
tv.setTouchEnabled(true);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 32bebb3..5a4064a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.views;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -40,7 +39,6 @@
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
@@ -62,8 +60,13 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-/* A task view */
-public class TaskView extends FrameLayout implements Task.TaskCallbacks,
+/**
+ * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
+ * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
+ * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
+ * with the previous bounds if any child requests layout).
+ */
+public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
/** The TaskView callbacks */
@@ -219,33 +222,20 @@
return super.onInterceptTouchEvent(ev);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
+ @Override
+ protected void measureContents(int width, int height) {
int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
- int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height);
// Measure the content
mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
- // Measure the bar view, and action button
- mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY));
- mActionButtonView.measure(
- MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
- // Measure the thumbnail to be square
- mThumbnailView.measure(
- MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
+ // Optimization: Prevent overdraw of the thumbnail under the header view
mThumbnailView.updateClipToTaskBar(mHeaderView);
setMeasuredDimension(width, height);
- invalidateOutline();
}
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 827ee408..cb108da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -245,11 +245,7 @@
* to match the frame changes.
*/
public void onTaskViewSizeChanged(int width, int height) {
- // Return early if the bounds have not changed
- if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
- return;
- }
-
+ // TODO: Optimize this path
mTaskViewRect.set(0, 0, width, height);
boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
boolean isFreeformTask = (mTask != null) && mTask.isFreeformTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 39d0604..de96d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -190,7 +190,6 @@
if (!mInvisible) {
updateThumbnailPaintFilter();
}
- invalidate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9e83dcf..08793e8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -44,7 +44,6 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageButton;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
@@ -72,6 +71,18 @@
private static final float DIM_START_FRACTION = 0.5f;
private static final float DIM_DAMP_FACTOR = 1.7f;
+ /**
+ * Fraction of the divider position between two snap targets to switch to the full-screen
+ * target.
+ */
+ private static final float SWITCH_FULLSCREEN_FRACTION = 0.12f;
+
+ /**
+ * Fraction of the divider position between two snap targets to switch to the larger target
+ * for the bottom/right app layout.
+ */
+ private static final float BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION = 0.2f;
+
private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
new PathInterpolator(0.5f, 1f, 0.5f, 1f);
@@ -404,6 +415,12 @@
restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
int taskPositionOther =
restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
+
+ taskPositionDocked = minimizeHoles(position, taskPositionDocked, mDockSide,
+ taskSnapTarget);
+ taskPositionOther = minimizeHoles(position, taskPositionOther, dockSideInverted,
+ taskSnapTarget);
+
calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
alignTopLeft(mDockedRect, mDockedTaskRect);
@@ -435,6 +452,51 @@
}
/**
+ * Given the current split position and the task position calculated by dragging, this
+ * method calculates a "better" task position in a sense so holes get smaller while dragging.
+ *
+ * @return the new task position
+ */
+ private int minimizeHoles(int position, int taskPosition, int dockSide,
+ SnapTarget taskSnapTarget) {
+ if (dockSideTopLeft(dockSide)) {
+ if (position > taskPosition) {
+ SnapTarget nextTarget = mSnapAlgorithm.getNextTarget(taskSnapTarget);
+
+ // If the next target is the dismiss end target, switch earlier to make the hole
+ // smaller.
+ if (nextTarget != taskSnapTarget
+ && nextTarget == mSnapAlgorithm.getDismissEndTarget()) {
+ float t = (float) (position - taskPosition)
+ / (nextTarget.position - taskPosition);
+ if (t > SWITCH_FULLSCREEN_FRACTION) {
+ return nextTarget.position;
+ }
+ }
+ }
+ } else if (dockSideBottomRight(dockSide)) {
+ if (position < taskPosition) {
+ SnapTarget previousTarget = mSnapAlgorithm.getPreviousTarget(taskSnapTarget);
+ if (previousTarget != taskSnapTarget) {
+ float t = (float) (taskPosition - position)
+ / (taskPosition - previousTarget.position);
+
+ // In general, switch a bit earlier (at 20% instead of 50%), but if we are
+ // dismissing the top, switch really early.
+ float threshold = previousTarget == mSnapAlgorithm.getDismissStartTarget()
+ ? SWITCH_FULLSCREEN_FRACTION
+ : BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION;
+ if (t > threshold) {
+ return previousTarget.position;
+ }
+
+ }
+ }
+ }
+ return taskPosition;
+ }
+
+ /**
* When the snap target is dismissing one side, make sure that the dismissing side doesn't get
* 0 size.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a8e09f8..e20936b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -105,6 +105,10 @@
}
@Override
+ public void onPinnedActivityRestartAttempt() {
+ }
+
+ @Override
public void onTaskStackChanged() {
mHandler.removeCallbacks(this);
mHandler.post(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 71267cd..8717a15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -65,6 +65,10 @@
setUserSwitcherController(qsPanel.getHost().getUserSwitcherController());
}
+ public boolean hasMultipleUsers() {
+ return mUserListener.getCount() != 0;
+ }
+
public void setUserSwitcherController(UserSwitcherController userSwitcherController) {
mUserSwitcherController = userSwitcherController;
registerListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index d0c14f1..ed6d940 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -1108,6 +1108,10 @@
@Override
public void onActivityPinned() {
}
+
+ @Override
+ public void onPinnedActivityRestartAttempt() {
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 5b44f0a..f18c341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
@@ -70,6 +71,8 @@
private QuickQSPanel mHeaderQsPanel;
private boolean mShowEmergencyCallsOnly;
private float mDateTimeTranslation;
+ private MultiUserSwitch mMultiUserSwitch;
+ private ImageView mMultiUserAvatar;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -100,6 +103,9 @@
mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
+ mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
+ mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) getBackground()).setForceSoftware(true);
@@ -173,6 +179,12 @@
? View.VISIBLE : View.INVISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
+ mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE
+ : View.GONE);
+ }
+
+ private boolean hasMultiUsers() {
+ return false;
}
private void updateListeners() {
@@ -194,6 +206,7 @@
setupHost(qsPanel.getHost());
if (mQsPanel != null) {
mQsPanel.setCallback(mQsPanelCallback);
+ mMultiUserSwitch.setQsPanel(qsPanel);
}
}
@@ -254,7 +267,12 @@
@Override
public void setUserInfoController(UserInfoController userInfoController) {
- // Don't care.
+ userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
+ @Override
+ public void onUserInfoChanged(String name, Drawable picture) {
+ mMultiUserAvatar.setImageDrawable(picture);
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 41aeac9..5719f76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
-import android.os.UserHandle;
import android.util.Log;
import com.android.settingslib.TetherUtil;
@@ -34,21 +34,18 @@
private static final String TAG = "HotspotController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final Intent TETHER_SERVICE_INTENT = new Intent()
- .putExtra(TetherUtil.EXTRA_ADD_TETHER_TYPE, TetherUtil.TETHERING_WIFI)
- .putExtra(TetherUtil.EXTRA_SET_ALARM, true)
- .putExtra(TetherUtil.EXTRA_RUN_PROVISION, true)
- .putExtra(TetherUtil.EXTRA_ENABLE_WIFI_TETHER, true)
- .setComponent(TetherUtil.TETHER_SERVICE);
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final Receiver mReceiver = new Receiver();
+ private final ConnectivityManager mConnectivityManager;
private final Context mContext;
private int mHotspotState;
public HotspotControllerImpl(Context context) {
mContext = context;
+ mConnectivityManager = (ConnectivityManager)context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -72,6 +69,7 @@
return null;
}
+ @Override
public void addCallback(Callback callback) {
if (callback == null || mCallbacks.contains(callback)) return;
if (DEBUG) Log.d(TAG, "addCallback " + callback);
@@ -79,6 +77,7 @@
mReceiver.setListening(!mCallbacks.isEmpty());
}
+ @Override
public void removeCallback(Callback callback) {
if (callback == null) return;
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
@@ -96,13 +95,24 @@
return TetherUtil.isTetheringSupported(mContext);
}
+ static final class OnStartTetheringCallback extends
+ ConnectivityManager.OnStartTetheringCallback {
+ @Override
+ public void onTetheringStarted() {}
+ @Override
+ public void onTetheringFailed() {
+ // TODO: Show error.
+ }
+ }
+
@Override
public void setHotspotEnabled(boolean enabled) {
- // Call provisioning app which is called when enabling Tethering from Settings
- if (enabled && TetherUtil.isProvisioningNeeded(mContext)) {
- mContext.startServiceAsUser(TETHER_SERVICE_INTENT, UserHandle.CURRENT);
+ if (enabled) {
+ OnStartTetheringCallback callback = new OnStartTetheringCallback();
+ mConnectivityManager.startTethering(
+ ConnectivityManager.TETHERING_WIFI, false, callback);
} else {
- TetherUtil.setWifiTethering(enabled, mContext);
+ mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index bed1e9e..b237400 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -29,7 +29,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -38,7 +37,6 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
/**
@@ -314,6 +312,10 @@
// Post the message back to the UI thread.
mHandler.post(mOnActivityPinnedRunnable);
}
+
+ @Override
+ public void onPinnedActivityRestartAttempt() {
+ }
}
/**
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index a4876b9..a71ba636 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -16,14 +16,16 @@
package android.renderscript;
+import java.nio.ByteBuffer;
import java.util.HashMap;
+
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.view.Surface;
-import android.util.Log;
import android.graphics.Canvas;
import android.os.Trace;
+import android.util.Log;
+import android.view.Surface;
/**
* <p> This class provides the primary method through which data is passed to
@@ -78,6 +80,8 @@
OnBufferAvailableListener mBufferNotifier;
private Surface mGetSurfaceSurface = null;
+ private ByteBuffer mByteBuffer = null;
+ private long mByteBufferStride = -1;
private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) {
final Class c = d.getClass();
@@ -2050,6 +2054,59 @@
}
/**
+ * @hide
+ * Gets or creates a ByteBuffer that contains the raw data of the current Allocation.
+ * If the Allocation is created with USAGE_IO_INPUT, the returned ByteBuffer
+ * would contain the up-to-date data as READ ONLY.
+ * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
+ * the Allocation has certain alignment. The size of each row including padding,
+ * called stride, can be queried using the {@link #getStride()} method.
+ *
+ * Note: Operating on the ByteBuffer of a destroyed Allocation will triger errors.
+ *
+ * @return ByteBuffer The ByteBuffer associated with raw data pointer of the Allocation.
+ */
+ public ByteBuffer getByteBuffer() {
+ // Create a new ByteBuffer if it is not initialized or using IO_INPUT.
+ if (mType.hasFaces()) {
+ throw new RSInvalidStateException("Cubemap is not supported for getByteBuffer().");
+ }
+ if (mType.getYuv() == android.graphics.ImageFormat.NV21 ||
+ mType.getYuv() == android.graphics.ImageFormat.YV12 ||
+ mType.getYuv() == android.graphics.ImageFormat.YUV_420_888 ) {
+ throw new RSInvalidStateException("YUV format is not supported for getByteBuffer().");
+ }
+ if (mByteBuffer == null || (mUsage & USAGE_IO_INPUT) != 0) {
+ int xBytesSize = mType.getX() * mType.getElement().getBytesSize();
+ long[] stride = new long[1];
+ mByteBuffer = mRS.nAllocationGetByteBuffer(getID(mRS), stride, xBytesSize, mType.getY(), mType.getZ());
+ mByteBufferStride = stride[0];
+ }
+ if ((mUsage & USAGE_IO_INPUT) != 0) {
+ return mByteBuffer.asReadOnlyBuffer();
+ }
+ return mByteBuffer;
+ }
+
+ /**
+ * @hide
+ * Gets the stride of the Allocation.
+ * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
+ * the Allocation has certain alignment. The size of each row including such
+ * padding is called stride.
+ *
+ * @return the stride. For 1D Allocation, the stride will be the number of
+ * bytes of this Allocation. For 2D and 3D Allocations, the stride
+ * will be the stride in X dimension measuring in bytes.
+ */
+ public long getStride() {
+ if (mByteBufferStride == -1) {
+ getByteBuffer();
+ }
+ return mByteBufferStride;
+ }
+
+ /**
* Returns the handle to a raw buffer that is being managed by the screen
* compositor. This operation is only valid for Allocations with {@link
* #USAGE_IO_INPUT}.
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 3668ddd..4788223 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -18,17 +18,18 @@
import java.io.File;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
-import android.util.Log;
-import android.view.Surface;
import android.os.SystemProperties;
import android.os.Trace;
-import java.util.ArrayList;
+import android.util.Log;
+import android.view.Surface;
// TODO: Clean up the whitespace that separates methods in this class.
@@ -489,6 +490,13 @@
validate();
rsnAllocationSyncAll(mContext, alloc, src);
}
+
+ native ByteBuffer rsnAllocationGetByteBuffer(long con, long alloc, long[] stride, int xBytesSize, int dimY, int dimZ);
+ synchronized ByteBuffer nAllocationGetByteBuffer(long alloc, long[] stride, int xBytesSize, int dimY, int dimZ) {
+ validate();
+ return rsnAllocationGetByteBuffer(mContext, alloc, stride, xBytesSize, dimY, dimZ);
+ }
+
native Surface rsnAllocationGetSurface(long con, long alloc);
synchronized Surface nAllocationGetSurface(long alloc) {
validate();
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 84f980d..2b06780 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -315,7 +315,6 @@
/**
* Only intended for use by generated reflected code. (General reduction)
*
- * @hide
*/
protected void reduce(int slot, Allocation[] ains, Allocation aout, LaunchOptions sc) {
mRS.validate();
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3954070..398d89b 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -2752,7 +2752,43 @@
return (jint)sizeof(void*);
}
+static jobject
+nAllocationGetByteBuffer(JNIEnv *_env, jobject _this, jlong con, jlong alloc,
+ jlongArray strideArr, jint xBytesSize,
+ jint dimY, jint dimZ) {
+ if (kLogApi) {
+ ALOGD("nAllocationGetByteBuffer, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
+ }
+ jlong *jStridePtr = _env->GetLongArrayElements(strideArr, nullptr);
+ if (jStridePtr == nullptr) {
+ ALOGE("Failed to get Java array elements: strideArr");
+ return 0;
+ }
+
+ size_t strideIn = xBytesSize;
+ void* ptr = nullptr;
+ if (alloc != 0) {
+ ptr = rsAllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
+ RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
+ &strideIn, sizeof(size_t));
+ }
+
+ jobject byteBuffer = nullptr;
+ if (ptr != nullptr) {
+ size_t bufferSize = strideIn;
+ jStridePtr[0] = strideIn;
+ if (dimY > 0) {
+ bufferSize *= dimY;
+ }
+ if (dimZ > 0) {
+ bufferSize *= dimZ;
+ }
+ byteBuffer = _env->NewDirectByteBuffer(ptr, (jlong) bufferSize);
+ }
+ _env->ReleaseLongArrayElements(strideArr, jStridePtr, 0);
+ return byteBuffer;
+}
// ---------------------------------------------------------------------------
@@ -2909,6 +2945,7 @@
{"rsnMeshGetIndices", "(JJ[J[II)V", (void*)nMeshGetIndices },
{"rsnSystemGetPointerSize", "()I", (void*)nSystemGetPointerSize },
+{"rsnAllocationGetByteBuffer", "(JJ[JIII)Ljava/nio/ByteBuffer;", (void*)nAllocationGetByteBuffer },
};
static int registerFuncs(JNIEnv *_env)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9927fd6c..3c13630 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -87,6 +87,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -2686,6 +2687,21 @@
mTethering.getUpstreamIfaceTypes().length != 0);
}
+ public void startTethering(int type, ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ if (!isTetheringSupported()) {
+ receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
+ return;
+ }
+ mTethering.startTethering(type, receiver, showProvisioningUi);
+ }
+
+ public void stopTethering(int type) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ mTethering.stopTethering(type);
+ }
+
// Called when we lose the default network and have no replacement yet.
// This will automatically be cleared after X seconds or a new default network
// becomes CONNECTED, whichever happens first. The timer is started by the
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1434e5e..b61dafe 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -203,7 +203,6 @@
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
@@ -1473,6 +1472,7 @@
static final int LOG_STACK_STATE = 62;
static final int VR_MODE_CHANGE_MSG = 63;
static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
+ static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2056,6 +2056,20 @@
}
break;
}
+ case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+ try {
+ // Make a one-way callback to the listener
+ mTaskStackListeners.getBroadcastItem(i).onPinnedActivityRestartAttempt();
+ } catch (RemoteException e){
+ // Handled by the RemoteCallbackList
+ }
+ }
+ mTaskStackListeners.finishBroadcast();
+ }
+ break;
+ }
case NOTIFY_CLEARTEXT_NETWORK_MSG: {
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
@@ -8989,6 +9003,11 @@
continue;
}
+ if (tr.realActivitySuspended) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
+ continue;
+ }
+
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
// foreground app. We may filter others if the caller has
@@ -11284,6 +11303,16 @@
mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
}
+ /**
+ * Notifies all listeners when an attempt was made to start an an activity that is already
+ * running in the pinned stack and the activity was not actually started, but the task is
+ * either brought to the front or a new Intent is delivered to it.
+ */
+ void notifyPinnedActivityRestartAttemptLocked() {
+ mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
+ }
+
@Override
public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -17563,6 +17592,8 @@
case Intent.ACTION_PACKAGE_CHANGED:
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
@@ -17643,6 +17674,20 @@
}
}
break;
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
+ final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(
+ intent.getAction());
+ final String[] packageNames = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ final int userHandle = intent.getIntExtra(
+ Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
+ synchronized(ActivityManagerService.this) {
+ mRecentTasks.onPackagesSuspendedChanged(
+ packageNames, suspended, userHandle);
+ }
+ break;
}
break;
case Intent.ACTION_PACKAGE_ADDED:
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 23dc0f6..7b7359f 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -504,18 +504,45 @@
err = startActivityUnchecked(
r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask);
+ postStartActivityUncheckedProcessing(r, err, stack.mStackId);
+ return err;
+ }
- if (err < 0) {
- // If someone asked to have the keyguard dismissed on the next
- // activity start, but we are not actually doing an activity
- // switch... just dismiss the keyguard now, because we
- // probably want to see whatever is behind it.
+ void postStartActivityUncheckedProcessing(
+ ActivityRecord r, int result, int prevFocusedStackId) {
+
+ if (result < START_SUCCESS) {
+ // If someone asked to have the keyguard dismissed on the next activity start,
+ // but we are not actually doing an activity switch... just dismiss the keyguard now,
+ // because we probably want to see whatever is behind it.
mSupervisor.notifyActivityDrawnForKeyguard();
- } else {
- launchRecentsAppIfNeeded(stack);
+ return;
}
- return err;
+ int startedActivityStackId = INVALID_STACK_ID;
+ if (r.task != null && r.task.stack != null) {
+ startedActivityStackId = r.task.stack.mStackId;
+ } else if (mTargetStack != null) {
+ startedActivityStackId = mTargetStack.mStackId;
+ }
+
+ if (startedActivityStackId == DOCKED_STACK_ID && prevFocusedStackId == HOME_STACK_ID) {
+ // We launch an activity while being in home stack, which means either launcher or
+ // recents into docked stack. We don't want the launched activity to be alone in a
+ // docked stack, so we want to immediately launch recents too.
+ if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+ mWindowManager.showRecentApps();
+ return;
+ }
+
+ if (startedActivityStackId == PINNED_STACK_ID
+ && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) {
+ // The activity was already running in the pinned stack so it wasn't started, but either
+ // brought to the front or the new intent was delivered to it since it was already in
+ // front. Notify anyone interested in this piece of information.
+ mService.notifyPinnedActivityRestartAttemptLocked();
+ return;
+ }
}
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
@@ -985,17 +1012,6 @@
return START_SUCCESS;
}
- private void launchRecentsAppIfNeeded(ActivityStack topStack) {
- if (topStack.mStackId == HOME_STACK_ID && mTargetStack != null
- && mTargetStack.mStackId == DOCKED_STACK_ID) {
- // We launch an activity while being in home stack, which means either launcher or
- // recents into docked stack. We don't want the launched activity to be alone in a
- // docked stack, so we want to immediately launch recents too.
- if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
- mWindowManager.showRecentApps();
- }
- }
-
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
@@ -1557,11 +1573,13 @@
final void doPendingActivityLaunchesLocked(boolean doResume) {
while (!mPendingActivityLaunches.isEmpty()) {
- PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-
+ final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+ final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
try {
- startActivityUnchecked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- doResume && mPendingActivityLaunches.isEmpty(), null, null);
+ final int result = startActivityUnchecked(
+ pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, null);
+ postStartActivityUncheckedProcessing(
+ pal.r, result, mSupervisor.mFocusedStack.mStackId);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 3f0674d..9c139d5 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -24,6 +24,8 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import com.google.android.collect.Sets;
+
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -45,6 +47,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Set;
/**
* Class for managing the recent tasks list.
@@ -188,6 +191,21 @@
}
}
+ void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
+ final Set<String> packageNames = Sets.newHashSet(packages);
+ for (int i = size() - 1; i >= 0; --i) {
+ final TaskRecord tr = get(i);
+ if (tr.realActivity != null
+ && packageNames.contains(tr.realActivity.getPackageName())
+ && tr.userId == userId
+ && tr.realActivitySuspended != suspended) {
+ tr.realActivitySuspended = suspended;
+ notifyTaskPersisterLocked(tr, false);
+ }
+ }
+
+ }
+
/**
* Update the recent tasks lists: make sure tasks should still be here (their
* applications / activities still exist), update their availability, fix-up ordering
@@ -683,5 +701,4 @@
// Let the caller know where we left off.
return start + tmpSize;
}
-
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4ce8b2f..fd787df 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -95,6 +95,7 @@
private static final String TAG_INTENT = "intent";
private static final String TAG_AFFINITYINTENT = "affinity_intent";
static final String ATTR_REALACTIVITY = "real_activity";
+ static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended";
private static final String ATTR_ORIGACTIVITY = "orig_activity";
private static final String TAG_ACTIVITY = "activity";
private static final String ATTR_AFFINITY = "affinity";
@@ -136,6 +137,8 @@
int effectiveUid; // The current effective uid of the identity of this task.
ComponentName origActivity; // The non-alias activity component of the intent.
ComponentName realActivity; // The actual activity component that started the task.
+ boolean realActivitySuspended; // True if the actual activity component that started the
+ // task is suspended.
long firstActiveTime; // First time this task was active.
long lastActiveTime; // Last time this task was active, including sleep.
boolean inRecents; // Actually in the recents list?
@@ -305,7 +308,7 @@
boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
- boolean resizeable, boolean privileged) {
+ boolean resizeable, boolean privileged, boolean realActivitySuspended) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -319,6 +322,7 @@
voiceSession = null;
voiceInteractor = null;
realActivity = _realActivity;
+ realActivitySuspended = realActivitySuspended;
origActivity = _origActivity;
rootWasReset = _rootWasReset;
isAvailable = true;
@@ -1027,6 +1031,7 @@
if (realActivity != null) {
out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
}
+ out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
if (origActivity != null) {
out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
}
@@ -1105,6 +1110,7 @@
Intent affinityIntent = null;
ArrayList<ActivityRecord> activities = new ArrayList<>();
ComponentName realActivity = null;
+ boolean realActivitySuspended = false;
ComponentName origActivity = null;
String affinity = null;
String rootAffinity = null;
@@ -1143,6 +1149,8 @@
if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
} else if (ATTR_REALACTIVITY.equals(attrName)) {
realActivity = ComponentName.unflattenFromString(attrValue);
+ } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
+ realActivitySuspended = Boolean.valueOf(attrValue);
} else if (ATTR_ORIGACTIVITY.equals(attrName)) {
origActivity = ComponentName.unflattenFromString(attrValue);
} else if (ATTR_AFFINITY.equals(attrName)) {
@@ -1253,7 +1261,8 @@
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
- taskAffiliationColor, callingUid, callingPackage, resizeable, privileged);
+ taskAffiliationColor, callingUid, callingPackage, resizeable, privileged,
+ realActivitySuspended);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3b43633..6648efd 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,10 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -38,20 +42,25 @@
import android.net.NetworkRequest;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.wifi.WifiManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
@@ -59,8 +68,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.InetAddress;
import java.net.Inet4Address;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -109,6 +118,10 @@
private BroadcastReceiver mStateReceiver;
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
+ .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
+
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
private static final int USB_PREFIX_LENGTH = 24;
@@ -332,6 +345,218 @@
}
}
+ public void startTethering(int type, ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ if (!isTetherProvisioningRequired()) {
+ enableTetheringInternal(type, true, receiver);
+ return;
+ }
+
+ if (showProvisioningUi) {
+ runUiTetherProvisioningAndEnable(type, receiver);
+ } else {
+ runSilentTetherProvisioningAndEnable(type, receiver);
+ }
+ }
+
+ public void stopTethering(int type) {
+ enableTetheringInternal(type, false, null);
+ if (isTetherProvisioningRequired()) {
+ cancelTetherProvisioningRechecks(type);
+ }
+ }
+
+ /**
+ * Check if the device requires a provisioning check in order to enable tethering.
+ *
+ * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+ */
+ private boolean isTetherProvisioningRequired() {
+ String[] provisionApp = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+ || provisionApp == null) {
+ return false;
+ }
+
+ // Check carrier config for entitlement checks
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ boolean isEntitlementCheckRequired = configManager.getConfig().getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+
+ if (!isEntitlementCheckRequired) {
+ return false;
+ }
+ return (provisionApp.length == 2);
+ }
+
+ /**
+ * Enables or disables tethering for the given type. This should only be called once
+ * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
+ * for the specified interface.
+ */
+ private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
+ boolean isProvisioningRequired = isTetherProvisioningRequired();
+ switch (type) {
+ case ConnectivityManager.TETHERING_WIFI:
+ final WifiManager wifiManager =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ if (wifiManager.setWifiApEnabled(null, enable)) {
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_NO_ERROR);
+ if (enable && isProvisioningRequired) {
+ scheduleProvisioningRechecks(type);
+ }
+ } else{
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
+ }
+ break;
+ case ConnectivityManager.TETHERING_USB:
+ int result = setUsbTethering(enable);
+ if (enable && isProvisioningRequired &&
+ result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ scheduleProvisioningRechecks(type);
+ }
+ sendTetherResult(receiver, result);
+ break;
+ case ConnectivityManager.TETHERING_BLUETOOTH:
+ setBluetoothTethering(enable, receiver);
+ break;
+ default:
+ Log.w(TAG, "Invalid tether type.");
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE);
+ }
+ }
+
+ private void sendTetherResult(ResultReceiver receiver, int result) {
+ if (receiver != null) {
+ receiver.send(result, null);
+ }
+ }
+
+ private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null || !adapter.isEnabled()) {
+ Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
+ (adapter == null));
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL);
+ return;
+ }
+
+ adapter.getProfileProxy(mContext, new ServiceListener() {
+ @Override
+ public void onServiceDisconnected(int profile) { }
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ ((BluetoothPan) proxy).setBluetoothTethering(enable);
+ // TODO: Enabling bluetooth tethering can fail asynchronously here.
+ // We should figure out a way to bubble up that failure instead of sending success.
+ int result = ((BluetoothPan) proxy).isTetheringOn() ?
+ ConnectivityManager.TETHER_ERROR_NO_ERROR :
+ ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ sendTetherResult(receiver, result);
+ if (enable && isTetherProvisioningRequired()) {
+ scheduleProvisioningRechecks(ConnectivityManager.TETHERING_BLUETOOTH);
+ }
+ adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
+ }
+ }, BluetoothProfile.PAN);
+ }
+
+ private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
+ sendUiTetherProvisionIntent(type, proxyReceiver);
+ }
+
+ private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
+ Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+ intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Creates a proxy {@link ResultReceiver} which enables tethering if the provsioning result is
+ * successful before firing back up to the wrapped receiver.
+ *
+ * @param type The type of tethering being enabled.
+ * @param receiver A ResultReceiver which will be called back with an int resultCode.
+ * @return The proxy receiver.
+ */
+ private ResultReceiver getProxyReceiver(final int type, final ResultReceiver receiver) {
+ ResultReceiver rr = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ // If provisioning is successful, enable tethering, otherwise just send the error.
+ if (resultCode == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ enableTetheringInternal(type, true, receiver);
+ } else {
+ sendTetherResult(receiver, resultCode);
+ }
+ }
+ };
+
+ // The following is necessary to avoid unmarshalling issues when sending the receiver
+ // across proccesses.
+ Parcel parcel = Parcel.obtain();
+ rr.writeToParcel(parcel,0);
+ parcel.setDataPosition(0);
+ ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return receiverForSending;
+ }
+
+ private void scheduleProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(ConnectivityManager.EXTRA_SET_ALARM, true);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
+ sendSilentTetherProvisionIntent(type, proxyReceiver);
+ }
+
+ private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void cancelTetherProvisioningRechecks(int type) {
+ if (getConnectivityManager().isTetheringSupported()) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_REM_TETHER_TYPE, type);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -615,13 +840,23 @@
synchronized (mPublicSync) {
if (enable) {
if (mRndisEnabled) {
- tetherUsb(true);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ tetherUsb(true);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
mUsbTetherRequested = true;
usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS);
}
} else {
- tetherUsb(false);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ tetherUsb(false);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (mRndisEnabled) {
usbManager.setCurrentFunction(null);
}
@@ -1407,15 +1642,6 @@
private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
private SimChangeBroadcastReceiver mBroadcastReceiver = null;
- // keep consts in sync with packages/apps/Settings TetherSettings.java
- private static final int WIFI_TETHERING = 0;
- private static final int USB_TETHERING = 1;
- private static final int BLUETOOTH_TETHERING = 2;
-
- // keep consts in sync with packages/apps/Settings TetherService.java
- private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
- private static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
private void startListeningForSimChanges() {
if (DBG) Log.d(TAG, "startListeningForSimChanges");
if (mBroadcastReceiver == null) {
@@ -1472,8 +1698,6 @@
try {
if (mContext.getResources().getString(com.android.internal.R.string.
config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
- final String tetherService = mContext.getResources().getString(
- com.android.internal.R.string.config_wifi_tether_enable);
ArrayList<Integer> tethered = new ArrayList<Integer>();
synchronized (mPublicSync) {
Set ifaces = mIfaces.keySet();
@@ -1481,21 +1705,25 @@
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null && sm.isTethered()) {
if (isUsb((String)iface)) {
- tethered.add(new Integer(USB_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_USB));
} else if (isWifi((String)iface)) {
- tethered.add(new Integer(WIFI_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_WIFI));
} else if (isBluetooth((String)iface)) {
- tethered.add(new Integer(BLUETOOTH_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_BLUETOOTH));
}
}
}
}
for (int tetherType : tethered) {
Intent startProvIntent = new Intent();
- startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(
- ComponentName.unflattenFromString(tetherService));
+ startProvIntent.putExtra(
+ ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(
+ ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
}
Log.d(TAG, "re-evaluate provisioning");
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7ac3c4b..e74d636 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.trust.TrustManager;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -103,6 +104,7 @@
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
private final String mKeyguardPackage;
+ private int mCurrentUserId = UserHandle.USER_CURRENT;
Handler mHandler = new Handler() {
@Override
@@ -125,6 +127,7 @@
private IFingerprintDaemon mDaemon;
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
+ private final UserManager mUserManager;
private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
@Override
@@ -152,6 +155,7 @@
mAlarmManager = mContext.getSystemService(AlarmManager.class);
mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
RESET_FINGERPRINT_LOCKOUT, null /* handler */);
+ mUserManager = UserManager.get(mContext);
}
@Override
@@ -170,7 +174,7 @@
mDaemon.init(mDaemonCallback);
mHalDeviceId = mDaemon.openHal();
if (mHalDeviceId != 0) {
- updateActiveGroup(ActivityManager.getCurrentUser());
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
mDaemon = null;
@@ -261,7 +265,7 @@
}
void handleUserSwitching(int userId) {
- updateActiveGroup(userId);
+ updateActiveGroup(userId, null);
}
private void removeClient(ClientMonitor client) {
@@ -414,7 +418,7 @@
removeClient(mEnrollClient);
}
- void startAuthentication(IBinder token, long opId, int groupId,
+ void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
IFingerprintDaemon daemon = getFingerprintDaemon();
@@ -423,6 +427,7 @@
return;
}
stopPendingOperations(true);
+ updateActiveGroup(groupId, opPackageName);
mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
@@ -564,7 +569,7 @@
checkPermission(USE_FINGERPRINT);
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
- if (opPackageName.equals(mKeyguardPackage)) {
+ if (isKeyguard(opPackageName)) {
return true; // Keyguard is always allowed
}
if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
@@ -583,6 +588,14 @@
return true;
}
+ /**
+ * @param clientPackage
+ * @return true if this is keyguard package
+ */
+ private boolean isKeyguard(String clientPackage) {
+ return mKeyguardPackage.equals(clientPackage);
+ }
+
private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
if (!mLockoutMonitors.contains(monitor)) {
mLockoutMonitors.add(monitor);
@@ -927,14 +940,15 @@
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
final int effectiveGroupId = getEffectiveUserId(groupId);
+ final int realUserId = Binder.getCallingUid();
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
- startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted,
- opPackageName);
+ startAuthentication(token, opId, realUserId, effectiveGroupId, receiver,
+ flags, restricted, opPackageName);
}
});
}
@@ -953,6 +967,17 @@
}
@Override // Binder call
+ public void setActiveUser(final int userId) {
+ checkPermission(MANAGE_FINGERPRINT);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateActiveGroup(userId, null);
+ }
+ });
+ }
+
+ @Override // Binder call
public void remove(final IBinder token, final int fingerId, final int groupId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -1102,33 +1127,56 @@
listenForUserSwitches();
}
- private void updateActiveGroup(int userId) {
+ private void updateActiveGroup(int userId, String clientPackage) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon != null) {
try {
- userId = getEffectiveUserId(userId);
- final File systemDir = Environment.getUserSystemDirectory(userId);
- final File fpDir = new File(systemDir, FP_DATA_DIR);
- if (!fpDir.exists()) {
- if (!fpDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
- return;
+ userId = getUserOrWorkProfileId(clientPackage, userId);
+ if (userId != mCurrentUserId) {
+ final File systemDir = Environment.getUserSystemDirectory(userId);
+ final File fpDir = new File(systemDir, FP_DATA_DIR);
+ if (!fpDir.exists()) {
+ if (!fpDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(fpDir)) {
+ Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+ return;
+ }
}
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(fpDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
+ daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
+ mCurrentUserId = userId;
}
- daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup():", e);
}
}
}
+ /**
+ * @param clientPackage the package of the caller
+ * @return the profile id
+ */
+ private int getUserOrWorkProfileId(String clientPackage, int userId) {
+ if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+ return userId;
+ }
+ return getEffectiveUserId(userId);
+ }
+
+ /**
+ * @param userId
+ * @return true if this is a work profile
+ */
+ private boolean isWorkProfile(int userId) {
+ UserInfo info = mUserManager.getUserInfo(userId);
+ return info != null && info.isManagedProfile();
+ }
+
private void listenForUserSwitches() {
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 1d7d24b..492632c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1155,7 +1155,7 @@
final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
for (NetworkState state : states) {
- if (state.networkInfo.isConnected()) {
+ if (state.networkInfo != null && state.networkInfo.isConnected()) {
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
final String baseIface = state.linkProperties.getInterfaceName();
@@ -1990,6 +1990,10 @@
if (policy != null) {
return policy.metered;
} else {
+ if (state.networkInfo == null) {
+ return false;
+ }
+
final int type = state.networkInfo.getType();
if (isNetworkTypeMobile(type) || type == TYPE_WIMAX) {
return true;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3193ff8..3ca99d4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -435,7 +435,11 @@
for (SessionState state : userState.sessionStateMap.values()) {
if (state.session != null) {
try {
- state.session.release();
+ if (state.isRecordingSession) {
+ state.session.disconnect();
+ } else {
+ state.session.release();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "error in release", e);
}
@@ -604,7 +608,11 @@
// Create a session. When failed, send a null token immediately.
try {
- service.createSession(channels[1], callback, sessionState.info.getId());
+ if (sessionState.isRecordingSession) {
+ service.createRecordingSession(callback, sessionState.info.getId());
+ } else {
+ service.createSession(channels[1], callback, sessionState.info.getId());
+ }
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
@@ -632,7 +640,11 @@
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
- sessionState.session.release();
+ if (sessionState.isRecordingSession) {
+ sessionState.session.disconnect();
+ } else {
+ sessionState.session.release();
+ }
}
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in releaseSession", e);
@@ -766,6 +778,21 @@
}
}
+ private void notifyTvInputInfoChanged(UserState userState, String inputId,
+ TvInputInfo inputInfo) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyTvInputInfoChanged(inputId=" + inputId + ", inputInfo=" + inputInfo
+ + ")");
+ }
+ for (ITvInputManagerCallback callback : userState.callbackSet) {
+ try {
+ callback.onTvInputInfoChanged(inputId, inputInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report changed input info to callback", e);
+ }
+ }
+ }
+
private void setStateLocked(String inputId, int state, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
TvInputState inputState = userState.inputMap.get(inputId);
@@ -1005,7 +1032,7 @@
@Override
public void createSession(final ITvInputClient client, final String inputId,
- int seq, int userId) {
+ boolean isRecordingSession, int seq, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "createSession");
@@ -1033,8 +1060,8 @@
// Create a new session token and a session state.
IBinder sessionToken = new Binder();
- SessionState sessionState = new SessionState(sessionToken, info, client,
- seq, callingUid, resolvedUserId);
+ SessionState sessionState = new SessionState(sessionToken, info,
+ isRecordingSession, client, seq, callingUid, resolvedUserId);
// Add them to the global session state map of the current user.
userState.sessionStateMap.put(sessionToken, sessionState);
@@ -1375,6 +1402,26 @@
}
@Override
+ public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "timeShiftPlay");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
+ recordedProgramUri);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in timeShiftPlay", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void timeShiftPause(IBinder sessionToken, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1383,8 +1430,7 @@
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId)
- .timeShiftPause();
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in timeShiftPause", e);
}
@@ -1477,6 +1523,64 @@
}
@Override
+ public void connect(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "connect");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).connect(
+ channelUri, params);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in connect", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void startRecording(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "startRecording");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording();
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in startRecording", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void stopRecording(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "stopRecording");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in stopRecording", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1912,6 +2016,7 @@
private final class SessionState implements IBinder.DeathRecipient {
private final TvInputInfo info;
+ private final boolean isRecordingSession;
private final ITvInputClient client;
private final int seq;
private final int callingUid;
@@ -1922,10 +2027,11 @@
// Not null if this session represents an external device connected to a hardware TV input.
private IBinder hardwareSessionToken;
- private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
- int seq, int callingUid, int userId) {
+ private SessionState(IBinder sessionToken, TvInputInfo info, boolean isRecordingSession,
+ ITvInputClient client, int seq, int callingUid, int userId) {
this.sessionToken = sessionToken;
this.info = info;
+ this.isRecordingSession = isRecordingSession;
this.client = client;
this.seq = seq;
this.callingUid = callingUid;
@@ -2126,6 +2232,18 @@
}
}
}
+
+ @Override
+ public void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
+ ensureValidInput(inputInfo);
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "setTvInputInfo(" + inputInfo + ")");
+ }
+ UserState userState = getOrCreateUserStateLocked(mUserId);
+ notifyTvInputInfoChanged(userState, inputId, inputInfo);
+ }
+ }
}
private final class SessionCallback extends ITvInputSessionCallback.Stub {
@@ -2393,6 +2511,78 @@
}
}
}
+
+ // For the recording session only
+ @Override
+ public void onConnected() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onConnected()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onConnected(mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onConnected", e);
+ }
+ }
+ }
+
+ // For the recording session only
+ @Override
+ public void onRecordingStarted() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onRecordingStarted()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onRecordingStarted(mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onRecordingStarted", e);
+ }
+ }
+ }
+
+ // For the recording session only
+ @Override
+ public void onRecordingStopped(Uri recordedProgramUri) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onRecordingStopped()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onRecordingStopped", e);
+ }
+ }
+ }
+
+ // For the recording session only
+ @Override
+ public void onError(int error) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onError()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onError(error, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onError", e);
+ }
+ }
+ }
}
private static final class WatchLogHandler extends Handler {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e3e90b66..04820a10 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -202,6 +202,7 @@
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
+ private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
@@ -364,6 +365,7 @@
int mPasswordOwner = -1;
long mLastMaximumTimeToLock = -1;
boolean mUserSetupComplete = false;
+ int mUserProvisioningState;
int mPermissionPolicy;
final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
@@ -2017,6 +2019,10 @@
out.attribute(null, ATTR_SETUP_COMPLETE,
Boolean.toString(true));
}
+ if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+ out.attribute(null, ATTR_PROVISIONING_STATE,
+ Integer.toString(policy.mUserProvisioningState));
+ }
if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
out.attribute(null, ATTR_PERMISSION_POLICY,
Integer.toString(policy.mPermissionPolicy));
@@ -2167,6 +2173,10 @@
if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
policy.mUserSetupComplete = true;
}
+ String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
+ if (!TextUtils.isEmpty(provisioningState)) {
+ policy.mUserProvisioningState = Integer.parseInt(provisioningState);
+ }
String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
if (!TextUtils.isEmpty(permissionPolicy)) {
policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
@@ -5379,6 +5389,7 @@
policy.mDelegatedCertInstallerPackage = null;
policy.mApplicationRestrictionsManagingPackage = null;
policy.mStatusBarDisabled = false;
+ policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
saveSettingsLocked(userId);
final long ident = mInjector.binderClearCallingIdentity();
@@ -5406,6 +5417,98 @@
}
@Override
+ public int getUserProvisioningState() {
+ if (!mHasFeature) {
+ return DevicePolicyManager.STATE_USER_UNMANAGED;
+ }
+ int userHandle = mInjector.userHandleGetCallingUserId();
+ return getUserProvisioningState(userHandle);
+ }
+
+ private int getUserProvisioningState(int userHandle) {
+ return getUserData(userHandle).mUserProvisioningState;
+ }
+
+ @Override
+ public void setUserProvisioningState(int newState, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
+ && getManagedUserId(userHandle) == -1) {
+ // No managed device, user or profile, so setting provisioning state makes no sense.
+ throw new IllegalStateException("Not allowed to change provisioning state unless a "
+ + "device or profile owner is set.");
+ }
+
+ synchronized (this) {
+ boolean transitionCheckNeeded = true;
+
+ // Calling identity/permission checks.
+ final int callingUid = mInjector.binderGetCallingUid();
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ // ADB shell can only move directly from un-managed to finalized as part of directly
+ // setting profile-owner or device-owner.
+ if (getUserProvisioningState(userHandle) !=
+ DevicePolicyManager.STATE_USER_UNMANAGED
+ || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+ throw new IllegalStateException("Not allowed to change provisioning state "
+ + "unless current provisioning state is unmanaged, and new state is "
+ + "finalized.");
+ }
+ transitionCheckNeeded = false;
+ } else {
+ // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ }
+
+ final DevicePolicyData policyData = getUserData(userHandle);
+ if (transitionCheckNeeded) {
+ // Optional state transition check for non-ADB case.
+ checkUserProvisioningStateTransition(policyData.mUserProvisioningState, newState);
+ }
+ policyData.mUserProvisioningState = newState;
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ private void checkUserProvisioningStateTransition(int currentState, int newState) {
+ // Valid transitions for normal use-cases.
+ switch (currentState) {
+ case DevicePolicyManager.STATE_USER_UNMANAGED:
+ // Can move to any state from unmanaged (except itself as an edge case)..
+ if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+ return;
+ }
+ break;
+ case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE:
+ case DevicePolicyManager.STATE_USER_SETUP_COMPLETE:
+ // Can only move to finalized from these states.
+ if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+ return;
+ }
+ break;
+ case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE:
+ // Current user has a managed-profile, but current user is not managed, so
+ // rather than moving to finalized state, go back to unmanaged once
+ // profile provisioning is complete.
+ if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+ return;
+ }
+ break;
+ case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
+ // Cannot transition out of finalized.
+ break;
+ }
+
+ // Didn't meet any of the accepted state transition checks above, throw appropriate error.
+ throw new IllegalStateException("Cannot move to user provisioning state [" + newState + "] "
+ + "from state [" + currentState + "]");
+ }
+
+ @Override
public void setProfileEnabled(ComponentName who) {
if (!mHasFeature) {
return;
@@ -5724,7 +5827,8 @@
for (int u = 0; u < userCount; u++) {
DevicePolicyData policy = getUserData(mUserData.keyAt(u));
pw.println();
- pw.println(" Enabled Device Admins (User " + policy.mUserHandle + "):");
+ pw.println(" Enabled Device Admins (User " + policy.mUserHandle
+ + ", provisioningState: " + policy.mUserProvisioningState + "):");
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin ap = policy.mAdminList.get(i);
@@ -6937,9 +7041,9 @@
@Override
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
- long actualDirectoryId, Intent originalIntent) {
- final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
- actualLookupKey, actualContactId, actualDirectoryId, originalIntent);
+ boolean isContactIdIgnored, long actualDirectoryId, Intent originalIntent) {
+ final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(actualLookupKey,
+ actualContactId, isContactIdIgnored, actualDirectoryId, originalIntent);
final int callingUserId = UserHandle.getCallingUserId();
final long ident = mInjector.binderClearCallingIdentity();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 536fb70..87569b7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -15,9 +15,6 @@
*/
package com.android.server.devicepolicy;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-
import android.Manifest.permission;
import android.app.Activity;
import android.app.admin.DeviceAdminReceiver;
@@ -27,21 +24,26 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
-import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import android.util.Pair;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -64,13 +66,18 @@
*
m FrameworksServicesTests &&
adb install \
- -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
+@SmallTest
public class DevicePolicyManagerTest extends DpmTestBase {
+ private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
+ permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+ permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
+
private DpmMockContext mContext;
public DevicePolicyManager dpm;
public DevicePolicyManagerServiceTestable dpms;
@@ -1543,4 +1550,156 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
assertTrue(dpm.isAffiliatedUser());
}
+
+ public void testGetUserProvisioningState_defaultResult() {
+ assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+ }
+
+ public void testSetUserProvisioningState_permission() throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_unprivileged() throws Exception {
+ setupProfileOwner();
+ try {
+ dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+ DpmMockContext.CALLER_USER_HANDLE);
+ fail("Expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ public void testSetUserProvisioningState_noManagement() {
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ try {
+ dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+ DpmMockContext.CALLER_USER_HANDLE);
+ fail("IllegalStateException expected");
+ } catch (IllegalStateException e) {
+ MoreAsserts.assertContainsRegex("change provisioning state unless a .* owner is set",
+ e.getMessage());
+ }
+ assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+ }
+
+ public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+ DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_deviceOwnerFromSetupWizardAlternative()
+ throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+ DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
+ throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
+ DevicePolicyManager.STATE_USER_UNMANAGED);
+ }
+
+ public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
+ throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+ }
+
+ public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ try {
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+ DevicePolicyManager.STATE_USER_UNMANAGED);
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
+ e.getMessage());
+ }
+ }
+
+ public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
+ throws Exception {
+ setupProfileOwner();
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+ try {
+ exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
+ DevicePolicyManager.STATE_USER_SETUP_COMPLETE);
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
+ e.getMessage());
+ }
+ }
+
+ private void exerciseUserProvisioningTransitions(int userId, int... states) {
+ assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+ for (int state : states) {
+ dpm.setUserProvisioningState(state, userId);
+ assertEquals(state, dpm.getUserProvisioningState());
+ }
+ }
+
+ private void setupProfileOwner() throws Exception {
+ mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
+
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+ dpm.setActiveAdmin(admin1, false);
+ assertTrue(dpm.setProfileOwner(admin1, null, DpmMockContext.CALLER_USER_HANDLE));
+
+ mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
+ }
+
+ private void setupDeviceOwner() throws Exception {
+ mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
+
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, false);
+ assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
+
+ mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
+ }
}
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index f3a9ea3..3ecb2c4 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -907,6 +907,8 @@
Maybe<std::string> versionCode, versionName;
Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
+ bool legacyXFlag = false;
+ bool requireLocalization = false;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
.requiredFlag("--manifest", "Path to the Android manifest to build",
@@ -922,6 +924,10 @@
.optionalSwitch("--no-auto-version",
"Disables automatic style and layout SDK versioning",
&options.noAutoVersion)
+ .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
+ &legacyXFlag)
+ .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
+ &requireLocalization)
.optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
"by -o",
&options.outputToDirectory)
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 48012db..a3f3821 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -87,6 +87,11 @@
}
@Override
+ public void updatePointerIcon(float x, float y) {
+ // pass for now
+ }
+
+ @Override
public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
int localValue, int localChanges) {
// pass for now.
@@ -105,4 +110,5 @@
// pass for now.
return null;
}
+
}