Merge "Fix SecurityException in Editor.onDrop"
diff --git a/Android.mk b/Android.mk
index 53e892f..1d797c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -417,7 +417,6 @@
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \
wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \
@@ -430,6 +429,14 @@
core/java/android/service/quicksettings/IQSService.aidl \
core/java/android/service/quicksettings/IQSTileService.aidl \
+# The following are native binders that need to go with the native component
+# at system/update_engine/binder_bindings/. Use relative path to refer to them.
+LOCAL_SRC_FILES += \
+ ../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \
+ ../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \
+
+LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
+
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += \
$(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
diff --git a/api/current.txt b/api/current.txt
index b7f63bc..9a1595f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -67,7 +67,7 @@
field public static final java.lang.String DUMP = "android.permission.DUMP";
field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
- field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+ field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
@@ -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
@@ -13839,6 +13841,7 @@
field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -20855,6 +20858,7 @@
field public static final int DEFAULT = 0; // 0x0
field public static final int H263 = 1; // 0x1
field public static final int H264 = 2; // 0x2
+ field public static final int HEVC = 5; // 0x5
field public static final int MPEG_4_SP = 3; // 0x3
field public static final int VP8 = 4; // 0x4
}
@@ -22214,6 +22218,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 +22355,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 +22432,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 +22453,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 +22474,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 +22513,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 +22524,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 +22592,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 +31100,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 +31109,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 +32936,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);
@@ -35167,6 +35249,7 @@
method public static boolean hasProperty(int, int);
method public boolean hasProperty(int);
method public static java.lang.String propertiesToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -35307,6 +35390,7 @@
method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
method public final void setVideoState(int);
method public static java.lang.String stateToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36321,10 +36405,15 @@
method public int getActiveSubscriptionInfoCountMax();
method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+ method public static int getDefaultDataSubscriptionId();
+ method public static int getDefaultSmsSubscriptionId();
+ method public static int getDefaultSubscriptionId();
+ method public static int getDefaultVoiceSubscriptionId();
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+ field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -36336,32 +36425,49 @@
method public boolean canChangeDtmfToneLength();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
+ method public int getCallState(int);
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
+ method public int getDataNetworkType(int);
method public int getDataState();
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
+ method public java.lang.String getGroupIdLevel1(int);
method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
+ method public java.lang.String getLine1Number(int);
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
+ method public java.lang.String getNetworkCountryIso(int);
method public java.lang.String getNetworkOperator();
+ method public java.lang.String getNetworkOperator(int);
method public java.lang.String getNetworkOperatorName();
+ method public java.lang.String getNetworkOperatorName(int);
method public int getNetworkType();
+ method public int getNetworkType(int);
method public int getPhoneCount();
method public int getPhoneType();
method public java.lang.String getSimCountryIso();
+ method public java.lang.String getSimCountryIso(int);
method public java.lang.String getSimOperator();
+ method public java.lang.String getSimOperator(int);
method public java.lang.String getSimOperatorName();
+ method public java.lang.String getSimOperatorName(int);
method public java.lang.String getSimSerialNumber();
+ method public java.lang.String getSimSerialNumber(int);
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.lang.String getSubscriberId(int);
method public java.lang.String getVoiceMailAlphaTag();
+ method public java.lang.String getVoiceMailAlphaTag(int);
method public java.lang.String getVoiceMailNumber();
+ method public java.lang.String getVoiceMailNumber(int);
+ method public int getVoiceNetworkType(int);
method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean hasCarrierPrivileges();
method public boolean hasIccCard();
@@ -36372,6 +36478,7 @@
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
+ method public boolean isNetworkRoaming(int);
method public boolean isSmsCapable();
method public boolean isTtyModeSupported();
method public boolean isVoiceCapable();
@@ -36380,9 +36487,11 @@
method public void listen(android.telephony.PhoneStateListener, int);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+ method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+ method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 55f08c8..c2abe38 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -95,7 +95,7 @@
field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
- field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+ field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
@@ -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
@@ -14240,6 +14242,7 @@
field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -22204,6 +22207,7 @@
field public static final int DEFAULT = 0; // 0x0
field public static final int H263 = 1; // 0x1
field public static final int H264 = 2; // 0x2
+ field public static final int HEVC = 5; // 0x5
field public static final int MPEG_4_SP = 3; // 0x3
field public static final int VP8 = 4; // 0x4
}
@@ -23641,6 +23645,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 +23786,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 +23868,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 +23880,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 +23915,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 +23939,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 +24007,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 +24032,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 +24077,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 +24089,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 +24191,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 +24487,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 +24500,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 +24525,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 +24554,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 +33214,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 +33223,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 +35153,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);
@@ -37440,6 +37542,7 @@
method public static boolean hasProperty(int, int);
method public boolean hasProperty(int);
method public static java.lang.String propertiesToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -37591,6 +37694,7 @@
method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
method public final void setVideoState(int);
method public static java.lang.String stateToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -38690,10 +38794,15 @@
method public int getActiveSubscriptionInfoCountMax();
method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+ method public static int getDefaultDataSubscriptionId();
+ method public static int getDefaultSmsSubscriptionId();
+ method public static int getDefaultSubscriptionId();
+ method public static int getDefaultVoiceSubscriptionId();
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+ field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -38714,6 +38823,7 @@
method public boolean endCall();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
+ method public int getCallState(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
method public java.lang.String getCdmaMdn();
@@ -38726,30 +38836,46 @@
method public int getDataActivity();
method public boolean getDataEnabled();
method public boolean getDataEnabled(int);
+ method public int getDataNetworkType(int);
method public int getDataState();
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
+ method public java.lang.String getGroupIdLevel1(int);
method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
+ method public java.lang.String getLine1Number(int);
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
+ method public java.lang.String getNetworkCountryIso(int);
method public java.lang.String getNetworkOperator();
+ method public java.lang.String getNetworkOperator(int);
method public java.lang.String getNetworkOperatorName();
+ method public java.lang.String getNetworkOperatorName(int);
method public int getNetworkType();
+ method public int getNetworkType(int);
method public int getPhoneCount();
method public int getPhoneType();
method public java.lang.String getSimCountryIso();
+ method public java.lang.String getSimCountryIso(int);
method public java.lang.String getSimOperator();
+ method public java.lang.String getSimOperator(int);
method public java.lang.String getSimOperatorName();
+ method public java.lang.String getSimOperatorName(int);
method public java.lang.String getSimSerialNumber();
+ method public java.lang.String getSimSerialNumber(int);
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.lang.String getSubscriberId(int);
method public java.lang.String getVoiceMailAlphaTag();
+ method public java.lang.String getVoiceMailAlphaTag(int);
method public java.lang.String getVoiceMailNumber();
+ method public java.lang.String getVoiceMailNumber(int);
+ method public int getVoiceNetworkType(int);
method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
@@ -38764,6 +38890,7 @@
method public boolean isHearingAidCompatibilitySupported();
method public boolean isIdle();
method public boolean isNetworkRoaming();
+ method public boolean isNetworkRoaming(int);
method public boolean isOffhook();
method public boolean isRadioOn();
method public boolean isRinging();
@@ -38779,11 +38906,13 @@
method public void setDataEnabled(boolean);
method public void setDataEnabled(int, boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+ method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+ method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
method public void silenceRinger();
method public boolean supplyPin(java.lang.String);
method public int[] supplyPinReportResult(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index e320b67..d9e8907 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -67,7 +67,7 @@
field public static final java.lang.String DUMP = "android.permission.DUMP";
field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
- field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+ field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
@@ -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
@@ -13847,6 +13849,7 @@
field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -20863,6 +20866,7 @@
field public static final int DEFAULT = 0; // 0x0
field public static final int H263 = 1; // 0x1
field public static final int H264 = 2; // 0x2
+ field public static final int HEVC = 5; // 0x5
field public static final int MPEG_4_SP = 3; // 0x3
field public static final int VP8 = 4; // 0x4
}
@@ -22222,6 +22226,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 +22363,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 +22440,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 +22461,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 +22482,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 +22521,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 +22532,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 +22600,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 +31112,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 +31121,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 +32950,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);
@@ -35181,6 +35263,7 @@
method public static boolean hasProperty(int, int);
method public boolean hasProperty(int);
method public static java.lang.String propertiesToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -35321,6 +35404,7 @@
method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
method public final void setVideoState(int);
method public static java.lang.String stateToString(int);
+ field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36335,10 +36419,15 @@
method public int getActiveSubscriptionInfoCountMax();
method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+ method public static int getDefaultDataSubscriptionId();
+ method public static int getDefaultSmsSubscriptionId();
+ method public static int getDefaultSubscriptionId();
+ method public static int getDefaultVoiceSubscriptionId();
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+ field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -36350,32 +36439,49 @@
method public boolean canChangeDtmfToneLength();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
+ method public int getCallState(int);
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
+ method public int getDataNetworkType(int);
method public int getDataState();
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
+ method public java.lang.String getGroupIdLevel1(int);
method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
+ method public java.lang.String getLine1Number(int);
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
+ method public java.lang.String getNetworkCountryIso(int);
method public java.lang.String getNetworkOperator();
+ method public java.lang.String getNetworkOperator(int);
method public java.lang.String getNetworkOperatorName();
+ method public java.lang.String getNetworkOperatorName(int);
method public int getNetworkType();
+ method public int getNetworkType(int);
method public int getPhoneCount();
method public int getPhoneType();
method public java.lang.String getSimCountryIso();
+ method public java.lang.String getSimCountryIso(int);
method public java.lang.String getSimOperator();
+ method public java.lang.String getSimOperator(int);
method public java.lang.String getSimOperatorName();
+ method public java.lang.String getSimOperatorName(int);
method public java.lang.String getSimSerialNumber();
+ method public java.lang.String getSimSerialNumber(int);
method public int getSimState();
method public java.lang.String getSubscriberId();
+ method public java.lang.String getSubscriberId(int);
method public java.lang.String getVoiceMailAlphaTag();
+ method public java.lang.String getVoiceMailAlphaTag(int);
method public java.lang.String getVoiceMailNumber();
+ method public java.lang.String getVoiceMailNumber(int);
+ method public int getVoiceNetworkType(int);
method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean hasCarrierPrivileges();
method public boolean hasIccCard();
@@ -36386,6 +36492,7 @@
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
+ method public boolean isNetworkRoaming(int);
method public boolean isSmsCapable();
method public boolean isTtyModeSupported();
method public boolean isVoiceCapable();
@@ -36394,9 +36501,11 @@
method public void listen(android.telephony.PhoneStateListener, int);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+ method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+ method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
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/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 1d9e3bb..35695c4 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -430,47 +430,46 @@
}
/**
- * Lists all accounts of any type registered on the device.
- * Equivalent to getAccountsByType(null).
+ * List every {@link Account} registered on the device that are managed by
+ * applications whose signatures match the caller.
*
- * <p>It is safe to call this method from the main thread.
+ * <p>This method can be called safely from the main thread. It is
+ * equivalent to calling <code>getAccountsByType(null)</code>.
*
- * <p>Clients of this method that have not been granted the
- * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
- * will only see those accounts managed by AbstractAccountAuthenticators whose
- * signature matches the client.
+ * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+ * manifests will continue to behave as they did on devices that support
+ * API level 23. In particular the GET_ACCOUNTS permission is required to
+ * see all the Accounts registered with the AccountManager. See docs for
+ * this function in API level 23 for more information.
*
- * @return An array of {@link Account}, one for each account. Empty
- * (never null) if no accounts have been added.
+ * @return Array of Accounts. The array may be empty if no accounts are
+ * available to the caller.
*/
@NonNull
- @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
- try {
- return mService.getAccounts(null, mContext.getOpPackageName());
- } catch (RemoteException e) {
- // won't ever happen
- throw new RuntimeException(e);
- }
+ return getAccountsByType(null);
}
/**
* @hide
- * Lists all accounts of any type registered on the device for a given
- * user id. Equivalent to getAccountsByType(null).
+ * List every {@link Account} registered on the device for a specific User
+ * that are managed by applications whose signatures match the caller.
*
- * <p>It is safe to call this method from the main thread.
+ * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+ * manifests will continue to behave as they did on devices that support
+ * API level 23. In particular the GET_ACCOUNTS permission is required to
+ * see all the Accounts registered with the AccountManager for the
+ * specified userId. See docs for this function in API level 23 for more
+ * information.
*
- * <p>Clients of this method that have not been granted the
- * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
- * will only see those accounts managed by AbstractAccountAuthenticators whose
- * signature matches the client.
+ * <p>This method can be called safely from the main thread.
*
- * @return An array of {@link Account}, one for each account. Empty
- * (never null) if no accounts have been added.
+ * @param int userId associated with the User whose accounts should be
+ * queried.
+ * @return Array of Accounts. The array may be empty if no accounts are
+ * available to the caller.
*/
@NonNull
- @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName());
@@ -501,10 +500,11 @@
/**
* Returns the accounts visible to the specified package, in an environment where some apps
* are not authorized to view all accounts. This method can only be called by system apps.
+ *
* @param type The type of accounts to return, null to retrieve all accounts
* @param packageName The package name of the app for which the accounts are to be returned
- * @return An array of {@link Account}, one per matching account. Empty
- * (never null) if no accounts of the specified type have been added.
+ * @return Array of Accounts. The array may be empty if no accounts of th
+ * specified type are visible to the caller.
*/
@NonNull
public Account[] getAccountsByTypeForPackage(String type, String packageName) {
@@ -518,29 +518,22 @@
}
/**
- * Lists all accounts of a particular type. The account type is a
- * string token corresponding to the authenticator and useful domain
- * of the account. For example, there are types corresponding to Google
- * and Facebook. The exact string token to use will be published somewhere
- * associated with the authenticator in question.
+ * List every {@link Account} of a specified type managed by applications
+ * whose signatures match the caller.
*
- * <p>It is safe to call this method from the main thread.
+ * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+ * manifests will continue to behave as they did on devices that support
+ * API level 23. See docs for this function in API level 23 for more
+ * information.
*
- * <p>Clients of this method that have not been granted the
- * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
- * will only see those accounts managed by AbstractAccountAuthenticators whose
- * signature matches the client.
+ * <p>This method can be called safely from the main thread.
*
- * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
- * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid
- * or signature match. See docs for this function in API level 22.
- *
- * @param type The type of accounts to return, null to retrieve all accounts
- * @return An array of {@link Account}, one per matching account. Empty
- * (never null) if no accounts of the specified type have been added.
+ * @param type String denoting the type of the accounts to return,
+ * {@code null} to retrieve all accounts visible to the caller.
+ * @return An array of Accounts. Empty (never null) if no accounts
+ * are available to the caller.
*/
@NonNull
- @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
@@ -586,6 +579,7 @@
* @return a future containing the label string
* @hide
*/
+ @NonNull
public AccountManagerFuture<String> getAuthTokenLabel(
final String accountType, final String authTokenType,
AccountManagerCallback<String> callback, Handler handler) {
@@ -618,9 +612,13 @@
* <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread.
*
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature
- * match with the AbstractAccountAuthenticator that manages the account.
+ * <p><b>Note:</b>The specified account must be managed by an application
+ * whose signature matches the caller.
+ *
+ * <p><b>Further note:</b>Apps targeting API level 23 or earlier will continue to
+ * behave as they did on devices that support API level 23. In particular
+ * they may still require the GET_ACCOUNTS permission. See docs for this
+ * function in API level 23.
*
* @param account The {@link Account} to test
* @param features An array of the account features to check
@@ -629,9 +627,11 @@
* @param handler {@link Handler} identifying the callback thread,
* null for the main thread
* @return An {@link AccountManagerFuture} which resolves to a Boolean,
- * true if the account exists and has all of the specified features.
+ * true if the account exists and has all of the specified features.
+ * @throws SecurityException if the specified account is managed by an
+ * application whose signature doesn't match the caller's signature.
*/
- @RequiresPermission(GET_ACCOUNTS)
+ @NonNull
public AccountManagerFuture<Boolean> hasFeatures(final Account account,
final String[] features,
AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -654,9 +654,10 @@
/**
* Lists all accounts of a type which have certain features. The account
- * type identifies the authenticator (see {@link #getAccountsByType}).
- * Account features are authenticator-specific string tokens identifying
- * boolean account properties (see {@link #hasFeatures}).
+ * type identifies the authenticator (see {@link #getAccountsByType}). Said
+ * authenticator must be in a package whose signature matches the callers
+ * package signature. Account features are authenticator-specific string tokens
+ * identifying boolean account properties (see {@link #hasFeatures}).
*
* <p>Unlike {@link #getAccountsByType}, this method calls the authenticator,
* which may contact the server or do other work to check account features,
@@ -665,19 +666,14 @@
* <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread.
*
- * <p>Clients of this method that have not been granted the
- * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
- * will only see those accounts managed by AbstractAccountAuthenticators whose
- * signature matches the client.
+ * <p><b>NOTE:</b> Apps targeting API level 23 or earlier will continue to
+ * behave as they did on devices that support API level 23. In particular
+ * they may still require the GET_ACCOUNTS permission. See docs for this
+ * function in API level 23.
*
* @param type The type of accounts to return, must not be null
* @param features An array of the account features to require,
* may be null or empty
- *
- * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
- * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid
- * or signature match. See docs for this function in API level 22.
- *
* @param callback Callback to invoke when the request completes,
* null for no callback
* @param handler {@link Handler} identifying the callback thread,
@@ -686,7 +682,7 @@
* {@link Account}, one per account of the specified type which
* matches the requested features.
*/
- @RequiresPermission(GET_ACCOUNTS)
+ @NonNull
public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
final String type, final String[] features,
AccountManagerCallback<Account[]> callback, Handler handler) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f3e1fc3..dedc8e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3328,17 +3328,6 @@
}
r.activity.performResume();
- // If there is a pending relaunch that was requested when the activity was paused,
- // it will put the activity into paused state when it finally happens. Since the
- // activity resumed before being relaunched, we don't want that to happen, so we
- // need to clear the request to relaunch paused.
- for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
- final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
- if (relaunching.token == r.token && relaunching.startsNotResumed) {
- relaunching.startsNotResumed = false;
- }
- }
-
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED,
UserHandle.myUserId(), r.activity.getComponentName().getClassName());
@@ -3567,7 +3556,6 @@
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport, int seq) {
ActivityClientRecord r = mActivities.get(token);
- if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
return;
}
@@ -4207,7 +4195,6 @@
synchronized (mResourcesManager) {
for (int i=0; i<mRelaunchingActivities.size(); i++) {
ActivityClientRecord r = mRelaunchingActivities.get(i);
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + this + ", trying: " + r);
if (r.token == token) {
target = r;
if (pendingResults != null) {
@@ -4238,19 +4225,14 @@
}
if (target == null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
- + fromServer);
target = new ActivityClientRecord();
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
target.mPreserveWindow = preserveWindow;
if (!fromServer) {
- final ActivityClientRecord existing = mActivities.get(token);
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
+ ActivityClientRecord existing = mActivities.get(token);
if (existing != null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
- + existing.paused);;
target.startsNotResumed = existing.paused;
target.overrideConfig = existing.overrideConfig;
}
@@ -4273,8 +4255,8 @@
target.pendingConfigChanges |= configChanges;
target.relaunchSeq = getLifecycleSeq();
}
- if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + ", target "
- + target + " operation received seq: " + target.relaunchSeq);
+ if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this
+ + " operation received seq: " + target.relaunchSeq);
}
private void handleRelaunchActivity(ActivityClientRecord tmp) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 368b8ef..3288cd9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,13 +48,12 @@
boolean areNotificationsEnabledForPackage(String pkg, int uid);
ParceledListSlice getTopics(String pkg, int uid);
- void setTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
- int getTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
- void setTopicPriority(String pkg, int uid, in Notification.Topic topic, int priority);
- int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
- void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
- int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
- void setAppImportance(String pkg, int uid, int importance);
+ void setVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
+ int getVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
+ void setPriority(String pkg, int uid, in Notification.Topic topic, int priority);
+ int getPriority(String pkg, int uid, in Notification.Topic topic);
+ void setImportance(String pkg, int uid, in Notification.Topic topic, int importance);
+ int getImportance(String pkg, int uid, in Notification.Topic topic);
boolean doesAppUseTopics(String pkg, int uid);
// TODO: Remove this when callers have been migrated to the equivalent
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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f3b1175..8035c56 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -82,8 +82,6 @@
import android.net.wifi.nan.WifiNanManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
-import android.net.wifi.passpoint.IWifiPasspointManager;
-import android.net.wifi.passpoint.WifiPasspointManager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.DropBoxManager;
@@ -483,15 +481,6 @@
return new WifiManager(ctx.getOuterContext(), service);
}});
- registerService(Context.WIFI_PASSPOINT_SERVICE, WifiPasspointManager.class,
- new CachedServiceFetcher<WifiPasspointManager>() {
- @Override
- public WifiPasspointManager createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
- IWifiPasspointManager service = IWifiPasspointManager.Stub.asInterface(b);
- return new WifiPasspointManager(ctx.getOuterContext(), service);
- }});
-
registerService(Context.WIFI_P2P_SERVICE, WifiP2pManager.class,
new StaticServiceFetcher<WifiP2pManager>() {
@Override
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/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 2bbc54f..999d826 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -155,7 +155,7 @@
}
/**
- * @return whether {@userId} has enabled and configured trust agents. Ignores short-term
+ * @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
* unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
*/
@RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 74cb0f6..9cd7d05 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -104,8 +104,6 @@
void getActivityEnergyInfoFromController();
BluetoothActivityEnergyInfo reportActivityInfo();
- // For dumpsys support
- void dump(in ParcelFileDescriptor fd);
void onLeServiceUp();
void onBrEdrDown();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 48d0196..9df7a28 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2561,7 +2561,6 @@
//@hide: NETWORK_POLICY_SERVICE,
WIFI_SERVICE,
WIFI_NAN_SERVICE,
- WIFI_PASSPOINT_SERVICE,
WIFI_P2P_SERVICE,
WIFI_SCANNING_SERVICE,
//@hide: WIFI_RTT_SERVICE,
@@ -3003,17 +3002,6 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.net.wifi.passpoint.WifiPasspointManager} for handling management of
- * Wi-Fi passpoint access.
- *
- * @see #getSystemService
- * @see android.net.wifi.passpoint.WifiPasspointManager
- * @hide
- */
- public static final String WIFI_PASSPOINT_SERVICE = "wifipasspoint";
-
- /**
- * Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.p2p.WifiP2pManager} for handling management of
* Wi-Fi peer-to-peer connections.
*
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/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2695dfd..2aa6af6 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2787,22 +2787,39 @@
/**
* <p>Generally classifies the overall set of the camera device functionality.</p>
- * <p>Camera devices will come in three flavors: LEGACY, LIMITED and FULL.</p>
- * <p>A FULL device will support below capabilities:</p>
+ * <p>The supported hardware level is a high-level description of the camera device's
+ * capabilities, summarizing several capabilities into one field. Each level adds additional
+ * features to the previous one, and is always a strict superset of the previous level.
+ * The ordering is <code>LEGACY < LIMITED < FULL < LEVEL_3</code>.</p>
+ * <p>Starting from <code>LEVEL_3</code>, the level enumerations are guaranteed to be in increasing
+ * numerical value as well. To check if a given device is at least at a given hardware level,
+ * the following code snippet can be used:</p>
+ * <pre><code>// Returns true if the device supports the required hardware level, or better.
+ * boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+ * int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+ * return requiredLevel == deviceLevel;
+ * }
+ * // deviceLevel is not LEGACY, can use numerical sort
+ * return requiredLevel <= deviceLevel;
+ * }
+ * </code></pre>
+ * <p>At a high level, the levels are:</p>
* <ul>
- * <li>BURST_CAPTURE capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains BURST_CAPTURE)</li>
- * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
- * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
- * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
- * MANUAL_POST_PROCESSING)</li>
- * <li>At least 3 processed (but not stalling) format output streams
- * ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>>=</code> 3)</li>
- * <li>The required stream configurations defined in android.scaler.availableStreamConfigurations</li>
- * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
- * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+ * <li><code>LEGACY</code> devices operate in a backwards-compatibility mode for older
+ * Android devices, and have very limited capabilities.</li>
+ * <li><code>LIMITED</code> devices represent the
+ * baseline feature set, and may also include additional capabilities that are
+ * subsets of <code>FULL</code>.</li>
+ * <li><code>FULL</code> devices additionally support per-frame manual control of sensor, flash, lens and
+ * post-processing settings, and image capture at a high rate.</li>
+ * <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
+ * with additional output stream configurations.</li>
* </ul>
- * <p>A LIMITED device may have some or none of the above characteristics.
- * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+ * <p>See the individual level enums for full descriptions of the supported capabilities. The
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
+ * finer-grain level, if needed. In addition, many controls have their available settings or
+ * ranges defined in individual {@link android.hardware.camera2.CameraCharacteristics } entries.</p>
* <p>Some features are not part of any particular hardware level or capability and must be
* queried separately. These include:</p>
* <ul>
@@ -2813,19 +2830,12 @@
* ({@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization},
* {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes})</li>
* </ul>
- * <p>A LEGACY device does not support per-frame control, manual sensor control, manual
- * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p>
- * <p>Each higher level supports everything the lower level supports
- * in this order: FULL <code>></code> LIMITED <code>></code> LEGACY.</p>
- * <p>Note:
- * Pre-API level 23, FULL devices also supported arbitrary cropping region
- * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM); this requirement was relaxed in API level 23,
- * and FULL devices may only support CENTERED cropping.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}</li>
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
* <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
+ * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -2833,16 +2843,12 @@
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
- * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC
- * @see CameraCharacteristics#SCALER_CROPPING_TYPE
- * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
- * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
* @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
* @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES
- * @see CameraCharacteristics#SYNC_MAX_LATENCY
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3
*/
@PublicKey
public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 63bcb31..28bb22a 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -314,7 +314,7 @@
* </table><br>
* </p>
*
- * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * <p>Limited-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
* {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
* support at least the following stream combinations in addition to those for
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} devices:
@@ -332,13 +332,13 @@
* </table><br>
* </p>
*
- * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
* {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices
* support at least the following stream combinations in addition to those for
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
*
* <table>
- * <tr><th colspan="7">FULL-capability additional guaranteed configurations</th></tr>
+ * <tr><th colspan="7">FULL-level additional guaranteed configurations</th></tr>
* <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
@@ -389,6 +389,22 @@
* </table><br>
* </p>
*
+ * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3})
+ * support at least the following stream combinations in addition to the combinations for
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and for
+ * RAW capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}):
+ *
+ * <table>
+ * <tr><th colspan="11">LEVEL-3 additional guaranteed configurations</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr>
+ * </table><br>
+ * </p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
* by attempting to create a session with such targets.</p>
@@ -501,7 +517,7 @@
* #rb { border-right-width: thick; }
* </style>
*
- * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
* {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
* support at least the following stream combinations for creating a reprocessable capture
* session in addition to those listed in {@link #createCaptureSession createCaptureSession} for
@@ -518,14 +534,14 @@
* </table><br>
* </p>
*
- * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
* {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices
* support at least the following stream combinations for creating a reprocessable capture
* session in addition to those for
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
*
* <table>
- * <tr><th colspan="11">FULL-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+ * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
* <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
* <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion in-app processing with regular preview.</td> </tr>
@@ -555,6 +571,22 @@
* </table><br>
* </p>
*
+ * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3}) devices
+ * support at least the following stream combinations for creating a reprocessable capture
+ * session in addition to those for
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} devices. Note that targets in the "Reprocess-only target" column may only be
+ * used as an output target for a reprocess capture request, not as an output to a regular capture request.
+ *
+ * <table>
+ * <tr><th colspan="13">LEVEL-3 additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is always guaranteed.</th></tr>
+ * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th colspan="2" id="rb">Reprocess-only target</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>In-app viewfinder analysis with ZSL and RAW.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td>In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output.</td> </tr>
+ * </table><br>
+ * </p>
+ *
* @param inputConfig The configuration for the input {@link Surface}
* @param outputs The new set of Surfaces that should be made available as
* targets for captured image data.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index f61892e..d58ad22 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -964,23 +964,102 @@
//
/**
- * <p>This camera device has only limited capabilities.</p>
+ * <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or
+ * better.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
+ * <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic
+ * support for color image capture. The only exception is that the device may
+ * alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth
+ * measurements and not color images.</p>
+ * <p><code>LIMITED</code> devices and above require the use of {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+ * to lock exposure metering (and calculate flash power, for cameras with flash) before
+ * capturing a high-quality still image.</p>
+ * <p>A <code>LIMITED</code> device that only lists the <code>BACKWARDS_COMPATIBLE</code> capability is only
+ * required to support full-automatic operation and post-processing (<code>OFF</code> is not
+ * supported for {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}, or
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode})</p>
+ * <p>Additional capabilities may optionally be supported by a <code>LIMITED</code>-level device, and
+ * can be checked for in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureRequest#CONTROL_AF_MODE
+ * @see CaptureRequest#CONTROL_AWB_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
/**
* <p>This camera device is capable of supporting advanced imaging applications.</p>
+ * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
+ * <p>A <code>FULL</code> device will support below capabilities:</p>
+ * <ul>
+ * <li><code>BURST_CAPTURE</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+ * <code>BURST_CAPTURE</code>)</li>
+ * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
+ * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains <code>MANUAL_SENSOR</code>)</li>
+ * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+ * <code>MANUAL_POST_PROCESSING</code>)</li>
+ * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
+ * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+ * </ul>
+ * <p>Note:
+ * Pre-API level 23, FULL devices also supported arbitrary cropping region
+ * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>); this requirement was relaxed in API level
+ * 23, and <code>FULL</code> devices may only support <code>CENTERED</code> cropping.</p>
+ *
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+ * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+ * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
+ * @see CameraCharacteristics#SYNC_MAX_LATENCY
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
/**
* <p>This camera device is running in backward compatibility mode.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
+ * documentation are supported.</p>
+ * <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
+ * post-processing, arbitrary cropping regions, and has relaxed performance constraints.
+ * No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
+ * <code>LEGACY</code> device in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+ * <p>In addition, the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is not functional on <code>LEGACY</code>
+ * devices. Instead, every request that includes a JPEG-format output target is treated
+ * as triggering a still capture, internally executing a precapture trigger. This may
+ * fire the flash for flash power metering during precapture, and then fire the flash
+ * for the final capture, if a flash is available on the device and the AE mode is set to
+ * enable the flash.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2;
+ /**
+ * <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
+ * FULL-level capabilities.</p>
+ * <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
+ * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
+ * documentation are guaranteed to be supported.</p>
+ * <p>The following additional capabilities are guaranteed to be supported:</p>
+ * <ul>
+ * <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+ * <code>YUV_REPROCESSING</code>)</li>
+ * <li><code>RAW</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+ * <code>RAW</code>)</li>
+ * </ul>
+ *
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3;
+
//
// Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c3cae65..79eff4f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1599,13 +1599,13 @@
* <p>This key is optional. Applications can assume there is no boost applied
* after RAW is captured if this key is not available.
* When this key is available, the sensitivity boost value must be within
- * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p>
+ * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
* <p>If the camera device cannot apply the exact boost requested, it will reduce the
* boost to the nearest supported value.
* The final boost value used will be available in the output capture result.</p>
* <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images
* of such device will have the total sensitivity of
- * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code>
+ * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code>
* The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 7b9d1a3..5748726 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2106,13 +2106,13 @@
* <p>This key is optional. Applications can assume there is no boost applied
* after RAW is captured if this key is not available.
* When this key is available, the sensitivity boost value must be within
- * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p>
+ * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
* <p>If the camera device cannot apply the exact boost requested, it will reduce the
* boost to the nearest supported value.
* The final boost value used will be available in the output capture result.</p>
* <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images
* of such device will have the total sensitivity of
- * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code>
+ * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code>
* The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
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..92c721b 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
*/
@@ -1543,7 +1611,7 @@
if (b != null) {
try {
ITelephony it = ITelephony.Stub.asInterface(b);
- int subId = SubscriptionManager.getDefaultDataSubId();
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
boolean retVal = it.getDataEnabled(subId);
Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
@@ -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/print/PrintJob.java b/core/java/android/print/PrintJob.java
index 777baab..66181e0 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.Objects;
+
/**
* This class represents a print job from the perspective of an
* application. It contains behavior methods for performing operations
@@ -30,11 +32,11 @@
*/
public final class PrintJob {
- private final PrintManager mPrintManager;
+ private final @NonNull PrintManager mPrintManager;
- private PrintJobInfo mCachedInfo;
+ private @NonNull PrintJobInfo mCachedInfo;
- PrintJob(PrintJobInfo info, PrintManager printManager) {
+ PrintJob(@NonNull PrintJobInfo info, @NonNull PrintManager printManager) {
mCachedInfo = info;
mPrintManager = printManager;
}
@@ -44,7 +46,7 @@
*
* @return The id.
*/
- public @NonNull PrintJobId getId() {
+ public @Nullable PrintJobId getId() {
return mCachedInfo.getId();
}
@@ -58,7 +60,7 @@
*
* @return The print job info.
*/
- public @Nullable PrintJobInfo getInfo() {
+ public @NonNull PrintJobInfo getInfo() {
if (isInImmutableState()) {
return mCachedInfo;
}
@@ -193,11 +195,17 @@
return false;
}
PrintJob other = (PrintJob) obj;
- return mCachedInfo.getId().equals(other.mCachedInfo.getId());
+ return Objects.equals(mCachedInfo.getId(), other.mCachedInfo.getId());
}
@Override
public int hashCode() {
- return mCachedInfo.getId().hashCode();
+ PrintJobId printJobId = mCachedInfo.getId();
+
+ if (printJobId == null) {
+ return 0;
+ } else {
+ return printJobId.hashCode();
+ }
}
}
diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java
index a2ee02b..186ae9b 100644
--- a/core/java/android/print/PrintJobId.java
+++ b/core/java/android/print/PrintJobId.java
@@ -19,7 +19,8 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
import java.util.UUID;
@@ -27,7 +28,7 @@
* This class represents the id of a print job.
*/
public final class PrintJobId implements Parcelable {
- private final String mValue;
+ private final @NonNull String mValue;
/**
* Creates a new instance.
@@ -45,7 +46,7 @@
*
* @hide
*/
- public PrintJobId(String value) {
+ public PrintJobId(@NonNull String value) {
mValue = value;
}
@@ -53,7 +54,7 @@
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((mValue != null) ? mValue.hashCode() : 0);
+ result = prime * result + mValue.hashCode();
return result;
}
@@ -69,7 +70,7 @@
return false;
}
PrintJobId other = (PrintJobId) obj;
- if (!TextUtils.equals(mValue, other.mValue)) {
+ if (!mValue.equals(other.mValue)) {
return false;
}
return true;
@@ -104,7 +105,7 @@
*
* @hide
*/
- public static PrintJobId unflattenFromString(String string) {
+ public static @NonNull PrintJobId unflattenFromString(@NonNull String string) {
return new PrintJobId(string);
}
@@ -112,7 +113,7 @@
new Parcelable.Creator<PrintJobId>() {
@Override
public PrintJobId createFromParcel(Parcel parcel) {
- return new PrintJobId(parcel.readString());
+ return new PrintJobId(Preconditions.checkNotNull(parcel.readString()));
}
@Override
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 21836b3..7e3a72f 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -244,7 +244,7 @@
*
* @return The id.
*/
- public @NonNull PrintJobId getId() {
+ public @Nullable PrintJobId getId() {
return mId;
}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 3eb4874..58f260c 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -535,7 +535,10 @@
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
- private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
+ /**
+ * @hide
+ */
+ public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
implements ActivityLifecycleCallbacks {
private final Object mLock = new Object();
@@ -1061,7 +1064,10 @@
}
}
- private static final class PrintJobStateChangeListenerWrapper extends
+ /**
+ * @hide
+ */
+ public static final class PrintJobStateChangeListenerWrapper extends
IPrintJobStateChangeListener.Stub {
private final WeakReference<PrintJobStateChangeListener> mWeakListener;
private final WeakReference<Handler> mWeakHandler;
diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java
index abb441b..c587edd 100644
--- a/core/java/android/print/PrinterDiscoverySession.java
+++ b/core/java/android/print/PrinterDiscoverySession.java
@@ -16,6 +16,8 @@
package android.print;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
@@ -72,7 +74,7 @@
}
}
- public final void startPrinterDiscovery(List<PrinterId> priorityList) {
+ public final void startPrinterDiscovery(@Nullable List<PrinterId> priorityList) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed");
return;
@@ -102,7 +104,7 @@
}
}
- public final void startPrinterStateTracking(PrinterId printerId) {
+ public final void startPrinterStateTracking(@NonNull PrinterId printerId) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed");
return;
@@ -114,7 +116,7 @@
}
}
- public final void stopPrinterStateTracking(PrinterId printerId) {
+ public final void stopPrinterStateTracking(@NonNull PrinterId printerId) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed");
return;
@@ -285,7 +287,7 @@
}
}
- private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
+ public static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
private final WeakReference<PrinterDiscoverySession> mWeakSession;
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 3104492..62d214e 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -16,6 +16,7 @@
package android.printservice;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
import android.content.ComponentName;
@@ -346,7 +347,7 @@
* @param localId A locally unique id in the context of your print service.
* @return Global printer id.
*/
- public final PrinterId generatePrinterId(String localId) {
+ public @NonNull final PrinterId generatePrinterId(String localId) {
throwIfNotCalledOnMainThread();
localId = Preconditions.checkNotNull(localId, "localId cannot be null");
return new PrinterId(new ComponentName(getPackageName(),
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/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 7e3601e..f3272e4 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -16,12 +16,13 @@
package android.security.net.config;
-import java.util.Set;
import java.security.cert.X509Certificate;
+import java.util.Set;
/** @hide */
public interface CertificateSource {
Set<X509Certificate> getCertificates();
X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
X509Certificate findByIssuerAndSignature(X509Certificate cert);
+ Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index ff728ef..742d430 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -17,8 +17,8 @@
package android.security.net.config;
import android.util.ArraySet;
-import java.util.Set;
import java.security.cert.X509Certificate;
+import java.util.Set;
/** @hide */
public final class CertificatesEntryRef {
@@ -60,4 +60,8 @@
return new TrustAnchor(foundCert, mOverridesPins);
}
+
+ public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
+ return mSource.findAllByIssuerAndSignature(cert);
+ }
}
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index bf29efa..b2c068c 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -29,6 +29,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.Collections;
import java.util.Set;
import libcore.io.IoUtils;
@@ -110,10 +111,50 @@
});
}
+ @Override
+ public Set<X509Certificate> findAllByIssuerAndSignature(final X509Certificate cert) {
+ return findCerts(cert.getIssuerX500Principal(), new CertSelector() {
+ @Override
+ public boolean match(X509Certificate ca) {
+ try {
+ cert.verify(ca.getPublicKey());
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ });
+ }
+
private static interface CertSelector {
boolean match(X509Certificate cert);
}
+ private Set<X509Certificate> findCerts(X500Principal subj, CertSelector selector) {
+ String hash = getHash(subj);
+ Set<X509Certificate> certs = null;
+ for (int index = 0; index >= 0; index++) {
+ String fileName = hash + "." + index;
+ if (!new File(mDir, fileName).exists()) {
+ break;
+ }
+ if (isCertMarkedAsRemoved(fileName)) {
+ continue;
+ }
+ X509Certificate cert = readCertificate(fileName);
+ if (!subj.equals(cert.getSubjectX500Principal())) {
+ continue;
+ }
+ if (selector.match(cert)) {
+ if (certs == null) {
+ certs = new ArraySet<X509Certificate>();
+ }
+ certs.add(cert);
+ }
+ }
+ return certs != null ? certs : Collections.<X509Certificate>emptySet();
+ }
+
private X509Certificate findCert(X500Principal subj, CertSelector selector) {
String hash = getHash(subj);
for (int index = 0; index >= 0; index++) {
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index b6105cd..ba5dd83 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -21,6 +21,7 @@
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;
@@ -90,4 +91,18 @@
}
return anchor.getTrustedCert();
}
+
+ @Override
+ public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+ ensureInitialized();
+ Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert);
+ if (anchors.isEmpty()) {
+ return Collections.<X509Certificate>emptySet();
+ }
+ Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size());
+ for (java.security.cert.TrustAnchor anchor : anchors) {
+ certs.add(anchor.getTrustedCert());
+ }
+ return certs;
+ }
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 0a2edff..ebe14691 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -145,6 +145,15 @@
return null;
}
+ /** @hide */
+ public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
+ Set<X509Certificate> certs = new ArraySet<X509Certificate>();
+ for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+ certs.addAll(ref.findAllCertificatesByIssuerAndSignature(cert));
+ }
+ return certs;
+ }
+
/**
* Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
*
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index e489c2c..8803c4b 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -25,6 +25,7 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
import com.android.org.conscrypt.TrustedCertificateIndex;
@@ -100,4 +101,18 @@
}
return anchor.getTrustedCert();
}
+
+ @Override
+ public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+ ensureInitialized();
+ Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert);
+ if (anchors.isEmpty()) {
+ return Collections.<X509Certificate>emptySet();
+ }
+ Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size());
+ for (java.security.cert.TrustAnchor anchor : anchors) {
+ certs.add(anchor.getTrustedCert());
+ }
+ return certs;
+ }
}
diff --git a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
index 4a90f82..c2f29be 100644
--- a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
+++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
@@ -42,6 +42,11 @@
}
@Override
+ public Set<X509Certificate> findAllIssuers(X509Certificate cert) {
+ return mConfig.findAllCertificatesByIssuerAndSignature(cert);
+ }
+
+ @Override
public X509Certificate getTrustAnchor(X509Certificate cert) {
TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
if (anchor == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6c3f308..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>
@@ -5795,8 +5877,11 @@
* <p>A View should call this if it maintains some notion of which part
* of its content is interesting. For example, a text editing view
* should call this when its cursor moves.
+ * <p>The Rectangle passed into this method should be in the View's content coordinate space.
+ * It should not be affected by which part of the View is currently visible or its scroll
+ * position.
*
- * @param rectangle The rectangle.
+ * @param rectangle The rectangle in the View's content coordinate space
* @return Whether any parent scrolled.
*/
public boolean requestRectangleOnScreen(Rect rectangle) {
@@ -5810,11 +5895,13 @@
* <p>A View should call this if it maintains some notion of which part
* of its content is interesting. For example, a text editing view
* should call this when its cursor moves.
- *
+ * <p>The Rectangle passed into this method should be in the View's content coordinate space.
+ * It should not be affected by which part of the View is currently visible or its scroll
+ * position.
* <p>When <code>immediate</code> is set to true, scrolling will not be
* animated.
*
- * @param rectangle The rectangle.
+ * @param rectangle The rectangle in the View's content coordinate space
* @param immediate True to forbid animated scrolling, false otherwise
* @return Whether any parent scrolled.
*/
@@ -5834,24 +5921,16 @@
rectangle.set((int) position.left, (int) position.top,
(int) position.right, (int) position.bottom);
- scrolled |= parent.requestChildRectangleOnScreen(child,
- rectangle, immediate);
-
- if (!child.hasIdentityMatrix()) {
- child.getMatrix().mapRect(position);
- }
-
- position.offset(child.mLeft, child.mTop);
+ scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate);
if (!(parent instanceof View)) {
break;
}
- View parentView = (View) parent;
+ // move it from child's content coordinate space to parent's content coordinate space
+ position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY());
- position.offset(-parentView.getScrollX(), -parentView.getScrollY());
-
- child = parentView;
+ child = (View) parent;
parent = child.getParent();
}
@@ -9718,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
@@ -10582,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:
@@ -10685,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
@@ -10717,7 +10908,6 @@
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
-
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
@@ -14278,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>
@@ -14325,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);
}
}
}
@@ -21338,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;
}
@@ -22609,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/view/ViewParent.java b/core/java/android/view/ViewParent.java
index e9b123b5..1962be8 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -266,14 +266,14 @@
* intercept touch events.
*/
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
-
+
/**
* Called when a child of this group wants a particular rectangle to be
* positioned onto the screen. {@link ViewGroup}s overriding this can trust
* that:
* <ul>
* <li>child will be a direct child of this group</li>
- * <li>rectangle will be in the child's coordinates</li>
+ * <li>rectangle will be in the child's content coordinates</li>
* </ul>
*
* <p>{@link ViewGroup}s overriding this should uphold the contract:</p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0d6f362..5d41477 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6729,16 +6729,19 @@
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ if (rectangle == null) {
+ return scrollToRectOrFocus(null, immediate);
+ }
+ rectangle.offset(child.getLeft() - child.getScrollX(),
+ child.getTop() - child.getScrollY());
final boolean scrolled = scrollToRectOrFocus(rectangle, immediate);
- if (rectangle != null) {
- mTempRect.set(rectangle);
- mTempRect.offset(0, -mCurScrollY);
- mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
- try {
- mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
- } catch (RemoteException re) {
- /* ignore */
- }
+ mTempRect.set(rectangle);
+ mTempRect.offset(0, -mCurScrollY);
+ mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
+ try {
+ mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
+ } catch (RemoteException re) {
+ /* ignore */
}
return scrolled;
}
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 3cc1d69..617d3dd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1083,6 +1083,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) &&
@@ -1098,15 +1112,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();
@@ -4939,6 +4945,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..3f7a07b 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/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 381e71f..2f26e92 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -42,6 +42,23 @@
* @return the string reference that was validated
* @throws IllegalArgumentException if {@code string} is empty
*/
+ public static @NonNull String checkStringNotEmpty(final String string) {
+ if (TextUtils.isEmpty(string)) {
+ throw new IllegalArgumentException();
+ }
+ return string;
+ }
+
+ /**
+ * Ensures that an string reference passed as a parameter to the calling
+ * method is not empty.
+ *
+ * @param string an string reference
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @return the string reference that was validated
+ * @throws IllegalArgumentException if {@code string} is empty
+ */
public static @NonNull String checkStringNotEmpty(final String string,
final Object errorMessage) {
if (TextUtils.isEmpty(string)) {
@@ -301,8 +318,8 @@
*
* @throws NullPointerException if the {@code value} or any of its elements were {@code null}
*/
- public static <T> Collection<T> checkCollectionElementsNotNull(final Collection<T> value,
- final String valueName) {
+ public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
+ final C value, final String valueName) {
if (value == null) {
throw new NullPointerException(valueName + " must not be null");
}
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3d4b8b4..56c3fc8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -383,6 +383,7 @@
<protected-broadcast android:name="android.security.STORAGE_CHANGED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
+ <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
<protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
@@ -1207,7 +1208,8 @@
<eat-comment />
<!-- Allows access to the list of accounts in the Accounts Service.
- <p>Protection level: normal
+ <p>Protection level: dangerous
+ @deprecated Not operative for apps apps with targetSdkVersion >= 24.
-->
<permission android:name="android.permission.GET_ACCOUNTS"
android:permissionGroup="android.permission-group.CONTACTS"
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..2631094
--- /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..5ec65e0
--- /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/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
new file mode 100644
index 0000000..aaa2dbc
--- /dev/null
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/dialog_list_padding_vertical_material"
+ android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
+>
+
+
+ <TextView
+ android:id="@+id/aerr_restart"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/aerr_restart"
+ style="@style/aerr_list_item"
+ />
+
+ <TextView
+ android:id="@+id/aerr_reset"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/aerr_reset"
+ style="@style/aerr_list_item"
+ />
+
+ <TextView
+ android:id="@+id/aerr_report"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/aerr_report"
+ style="@style/aerr_list_item"
+ />
+
+ <TextView
+ android:id="@+id/aerr_close"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/aerr_close"
+ style="@style/aerr_list_item"
+ />
+
+ <TextView
+ android:id="@+id/aerr_mute"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/aerr_mute"
+ style="@style/aerr_list_item"
+ />
+
+
+</LinearLayout>
diff --git a/core/res/res/layout/app_error_dialog_dont_show_again.xml b/core/res/res/layout/app_error_dialog_dont_show_again.xml
deleted file mode 100644
index ba79ecd..0000000
--- a/core/res/res/layout/app_error_dialog_dont_show_again.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="14dp"
- android:paddingEnd="10dp"
- android:gravity="center_vertical"
- >
- <CheckBox
- android:id="@+id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp"
- />
-
-</LinearLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 28c76bb..a276854 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2588,16 +2588,27 @@
<!-- Text to display when there are no activities found to display in the
activity chooser. See the "Select an action" title. -->
<string name="noApplications">No apps can perform this action.</string>
- <!-- Title of the alert when an application has crashed. -->
- <string name="aerr_title"></string>
<!-- Text of the alert that is displayed when an application has crashed. -->
- <string name="aerr_application">Unfortunately, <xliff:g id="application">%1$s</xliff:g> has stopped.</string>
- <!-- Text of the alert that is displayed when an application has crashed. -->
- <string name="aerr_process">Unfortunately, the process <xliff:g id="process">%1$s</xliff:g> has
- stopped.</string>
- <!-- Text of the alert that is displayed when an application has crashed. -->
- <string name="aerr_process_silence">Silence crashes from <xliff:g id="process">%1$s</xliff:g>
- until reboot.</string>
+ <string name="aerr_application"><xliff:g id="application">%1$s</xliff:g> has stopped</string>
+ <!-- Text of the alert that is displayed when a process has crashed. -->
+ <string name="aerr_process"><xliff:g id="process">%1$s</xliff:g> has
+ stopped</string>
+ <!-- Text of the alert that is displayed when an application has crashed repeatedly. -->
+ <string name="aerr_application_repeated"><xliff:g id="application">%1$s</xliff:g> is repeatedly stopping</string>
+ <!-- Text of the alert that is displayed when a process has crashed repeatedly. -->
+ <string name="aerr_process_repeated"><xliff:g id="process">%1$s</xliff:g> is
+ repeatedly stopping</string>
+ <!-- Button that restarts a crashed application -->
+ <string name="aerr_restart">Restart app</string>
+ <!-- Button that clears cache and restarts a crashed application -->
+ <string name="aerr_reset">Reset and restart app</string>
+ <!-- Button that sends feedback about a crashed application -->
+ <string name="aerr_report">Send feedback</string>
+ <!-- Button that closes a crashed application -->
+ <string name="aerr_close">Close</string>
+ <!-- Button that mutes further crashes of the crashed application-->
+ <string name="aerr_mute">Mute</string>
+
<!-- Title of the alert when an application is not responding. -->
<string name="anr_title"></string>
<!-- Text of the alert that is displayed when an application is not responding. -->
@@ -4174,4 +4185,9 @@
<string name="suspended_package_title">%1$s disabled</string>
<!-- Message for dialog displayed when a packge is suspended by device admin. [CHAR LIMIT=NONE] -->
<string name="suspended_package_message">Disabled by %1$s administrator. Contact them to learn more.</string>
+
+ <!-- Notification title shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] -->
+ <string name="new_sms_notification_title">You have new messages</string>
+ <!-- Notification content shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] -->
+ <string name="new_sms_notification_content">Open SMS app to view</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index d5349b2..b660277 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1397,6 +1397,16 @@
<item name="pointerIconGrabbing">@drawable/pointer_grabbing_large_icon</item>
</style>
+ <!-- @hide -->
+ <style name="aerr_list_item" parent="Widget.Material.Light.TextView">
+ <item name="minHeight">?attr/listPreferredItemHeightSmall</item>
+ <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
+ <item name="textColor">?attr/textColorAlertDialogListItem</item>
+ <item name="gravity">center_vertical</item>
+ <item name="paddingStart">?attr/listPreferredItemPaddingStart</item>
+ <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item>
+ </style>
+
<!-- Wifi dialog styles -->
<!-- @hide -->
<style name="wifi_item">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 57ff243..e59c935 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1763,7 +1763,7 @@
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
- <java-symbol type="layout" name="app_error_dialog_dont_show_again" />
+ <java-symbol type="layout" name="app_error_dialog" />
<java-symbol type="plurals" name="wifi_available" />
<java-symbol type="plurals" name="wifi_available_detailed" />
<java-symbol type="string" name="accessibility_binding_label" />
@@ -1777,8 +1777,8 @@
<java-symbol type="string" name="remote_bugreport_progress_notification_message_can_cancel" />
<java-symbol type="string" name="aerr_application" />
<java-symbol type="string" name="aerr_process" />
- <java-symbol type="string" name="aerr_process_silence" />
- <java-symbol type="string" name="aerr_title" />
+ <java-symbol type="string" name="aerr_application_repeated" />
+ <java-symbol type="string" name="aerr_process_repeated" />
<java-symbol type="string" name="android_upgrading_fstrim" />
<java-symbol type="string" name="android_upgrading_apk" />
<java-symbol type="string" name="android_upgrading_complete" />
@@ -2429,6 +2429,12 @@
<java-symbol type="id" name="work_widget_app_icon" />
<java-symbol type="drawable" name="work_widget_mask_view_background" />
+ <java-symbol type="id" name="aerr_report" />
+ <java-symbol type="id" name="aerr_reset" />
+ <java-symbol type="id" name="aerr_restart" />
+ <java-symbol type="id" name="aerr_close" />
+ <java-symbol type="id" name="aerr_mute" />
+
<!-- Framework-private Material.DayNight styles. -->
<java-symbol type="style" name="Theme.Material.DayNight" />
<java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2493,4 +2499,8 @@
<java-symbol type="string" name="work_mode_turn_on" />
<java-symbol type="string" name="suspended_package_title" />
<java-symbol type="string" name="suspended_package_message" />
+
+ <!-- New SMS notification while phone is locked. -->
+ <java-symbol type="string" name="new_sms_notification_title" />
+ <java-symbol type="string" name="new_sms_notification_content" />
</resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 5a08887..cf7978c 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -214,7 +214,7 @@
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
- <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327|654)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:567|578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
<!-- Vietnam -->
<shortcode country="vn" free="5001" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index ee8921e..eb055de 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -32,7 +32,8 @@
littlemock \
android-support-test \
mockito-target \
- espresso-core
+ espresso-core \
+ ub-uiautomator
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c0453f8..bfa2b10 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" />
@@ -1294,6 +1295,26 @@
</intent-filter>
</activity>
+ <activity android:name="android.print.PrintTestActivity"/>
+
+ <service
+ android:name="android.print.mockservice.MockPrintService"
+ android:permission="android.permission.BIND_PRINT_SERVICE">
+ <intent-filter>
+ <action android:name="android.printservice.PrintService" />
+ </intent-filter>
+ <meta-data
+ android:name="android.printservice"
+ android:resource="@xml/printservice">
+ </meta-data>
+ </service>
+
+ <activity
+ android:name="android.print.mockservice.SettingsActivity"
+ android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+ android:exported="true">
+ </activity>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml
new file mode 100644
index 0000000..abbebda
--- /dev/null
+++ b/core/tests/coretests/res/xml/printservice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<print-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="android.print.mockservice.SettingsActivity"/>
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
new file mode 100644
index 0000000..19ce44a
--- /dev/null
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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.print;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.mockservice.PrintServiceCallbacks;
+import android.print.mockservice.PrinterDiscoverySessionCallbacks;
+import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+
+import org.mockito.stubbing.Answer;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is the base class for print tests.
+ */
+public abstract class BasePrintTest extends InstrumentationTestCase {
+
+ private static final long OPERATION_TIMEOUT = 30000;
+ private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+ private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
+ private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
+ private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
+ private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable ";
+ private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
+
+ private PrintTestActivity mActivity;
+ private android.print.PrintJob mPrintJob;
+
+ private LocaleList mOldLocale;
+
+ private CallCounter mStartCallCounter;
+ private CallCounter mStartSessionCallCounter;
+
+ private String[] mEnabledImes;
+
+ private String[] getEnabledImes() throws IOException {
+ List<String> imeList = new ArrayList<>();
+
+ ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+ .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS);
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ imeList.add(line);
+ }
+ }
+
+ String[] imeArray = new String[imeList.size()];
+ imeList.toArray(imeArray);
+
+ return imeArray;
+ }
+
+ private void disableImes() throws Exception {
+ mEnabledImes = getEnabledImes();
+ for (String ime : mEnabledImes) {
+ String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime;
+ runShellCommand(getInstrumentation(), disableImeCommand);
+ }
+ }
+
+ private void enableImes() throws Exception {
+ for (String ime : mEnabledImes) {
+ String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime;
+ runShellCommand(getInstrumentation(), enableImeCommand);
+ }
+ mEnabledImes = null;
+ }
+
+ @Override
+ protected void runTest() throws Throwable {
+ // Do nothing if the device does not support printing.
+ if (supportsPrinting()) {
+ super.runTest();
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ if (!supportsPrinting()) {
+ return;
+ }
+
+ // Make sure we start with a clean slate.
+ clearPrintSpoolerData();
+ disableImes();
+
+ // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+ // Dexmaker is used by mockito.
+ System.setProperty("dexmaker.dexcache", getInstrumentation()
+ .getTargetContext().getCacheDir().getPath());
+
+ // Set to US locale.
+ Resources resources = getInstrumentation().getTargetContext().getResources();
+ Configuration oldConfiguration = resources.getConfiguration();
+ if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) {
+ mOldLocale = oldConfiguration.getLocales();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ Configuration newConfiguration = new Configuration(oldConfiguration);
+ newConfiguration.setLocale(Locale.US);
+ resources.updateConfiguration(newConfiguration, displayMetrics);
+ }
+
+ // Initialize the latches.
+ mStartCallCounter = new CallCounter();
+ mStartSessionCallCounter = new CallCounter();
+
+ // Create the activity for the right locale.
+ createActivity();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+
+ // Done with the activity.
+ getActivity().finish();
+ enableImes();
+
+ // Restore the locale if needed.
+ if (mOldLocale != null) {
+ Resources resources = getInstrumentation().getTargetContext().getResources();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ Configuration newConfiguration = new Configuration(resources.getConfiguration());
+ newConfiguration.setLocales(mOldLocale);
+ mOldLocale = null;
+ resources.updateConfiguration(newConfiguration, displayMetrics);
+ }
+
+ // Make sure the spooler is cleaned, this also un-approves all services
+ clearPrintSpoolerData();
+
+ super.tearDown();
+ }
+
+ protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
+ final PrintAttributes attributes) {
+ // Initiate printing as if coming from the app.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ PrintManager printManager = (PrintManager) getActivity()
+ .getSystemService(Context.PRINT_SERVICE);
+ mPrintJob = printManager.print("Print job", adapter, attributes);
+ }
+ });
+
+ return mPrintJob;
+ }
+
+ protected void onStartCalled() {
+ mStartCallCounter.call();
+ }
+
+ protected void onPrinterDiscoverySessionStartCalled() {
+ mStartSessionCallCounter.call();
+ }
+
+ protected void waitForPrinterDiscoverySessionStartCallbackCalled() {
+ waitForCallbackCallCount(mStartSessionCallCounter, 1,
+ "Did not get expected call to onStartPrinterDiscoverySession.");
+ }
+
+ protected void waitForStartAdapterCallbackCalled() {
+ waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start.");
+ }
+
+ private void waitForCallbackCallCount(CallCounter counter, int count, String message) {
+ try {
+ counter.waitForCount(count, OPERATION_TIMEOUT);
+ } catch (TimeoutException te) {
+ fail(message);
+ }
+ }
+
+ protected PrintTestActivity getActivity() {
+ return mActivity;
+ }
+
+ protected void createActivity() {
+ mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+ PrintTestActivity.class, null);
+ }
+
+ public static String runShellCommand(Instrumentation instrumentation, String cmd)
+ throws IOException {
+ ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ StringBuffer stdout = new StringBuffer();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.append(new String(buf, 0, bytesRead));
+ }
+ fis.close();
+ return stdout.toString();
+ }
+
+ protected void clearPrintSpoolerData() throws Exception {
+ assertTrue("failed to clear print spooler data",
+ runShellCommand(getInstrumentation(), String.format(
+ "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
+ .contains(PM_CLEAR_SUCCESS_OUTPUT));
+ }
+
+ @SuppressWarnings("unchecked")
+ protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+ Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+ Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+ Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
+ Answer<Void> onDestroy) {
+ PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+ doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+ when(callbacks.getSession()).thenCallRealMethod();
+
+ if (onStartPrinterDiscovery != null) {
+ doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+ any(List.class));
+ }
+ if (onStopPrinterDiscovery != null) {
+ doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+ }
+ if (onValidatePrinters != null) {
+ doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+ any(List.class));
+ }
+ if (onStartPrinterStateTracking != null) {
+ doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+ any(PrinterId.class));
+ }
+ if (onRequestCustomPrinterIcon != null) {
+ doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
+ any(PrinterId.class), any(CustomPrinterIconCallback.class));
+ }
+ if (onStopPrinterStateTracking != null) {
+ doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+ any(PrinterId.class));
+ }
+ if (onDestroy != null) {
+ doAnswer(onDestroy).when(callbacks).onDestroy();
+ }
+
+ return callbacks;
+ }
+
+ protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+ Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+ Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+ final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+ doCallRealMethod().when(service).setService(any(PrintService.class));
+ when(service.getService()).thenCallRealMethod();
+
+ if (onCreatePrinterDiscoverySessionCallbacks != null) {
+ doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+ .onCreatePrinterDiscoverySessionCallbacks();
+ }
+ if (onPrintJobQueued != null) {
+ doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+ }
+ if (onRequestCancelPrintJob != null) {
+ doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+ any(PrintJob.class));
+ }
+
+ return service;
+ }
+
+ protected final class CallCounter {
+ private final Object mLock = new Object();
+
+ private int mCallCount;
+
+ public void call() {
+ synchronized (mLock) {
+ mCallCount++;
+ mLock.notifyAll();
+ }
+ }
+
+ public int getCallCount() {
+ synchronized (mLock) {
+ return mCallCount;
+ }
+ }
+
+ public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
+ synchronized (mLock) {
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (mCallCount < count) {
+ try {
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
+ if (remainingTimeMillis <= 0) {
+ throw new TimeoutException();
+ }
+ mLock.wait(timeoutMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ protected boolean supportsPrinting() {
+ return getInstrumentation().getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_PRINTING);
+ }
+}
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
new file mode 100644
index 0000000..5179b42
--- /dev/null
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -0,0 +1,669 @@
+/*
+ * 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.print;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.print.IPrintDocumentAdapter;
+import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintManager;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintJob;
+import android.print.PrintJobId;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterDiscoverySession;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintServiceInfo;
+
+import android.print.mockservice.MockPrintService;
+import android.print.mockservice.PrintServiceCallbacks;
+import android.print.mockservice.PrinterDiscoverySessionCallbacks;
+import android.print.mockservice.StubbablePrinterDiscoverySession;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * tests feeding all possible parameters to the IPrintManager Binder.
+ */
+public class IPrintManagerParametersTest extends BasePrintTest {
+
+ private final int BAD_APP_ID = 0xffffffff;
+
+ private final int mAppId;
+ private final int mUserId;
+ private final PrintJobId mBadPrintJobId;
+
+ private PrintJob mGoodPrintJob;
+ private PrinterId mBadPrinterId;
+ private PrinterId mGoodPrinterId;
+ private ComponentName mGoodComponentName;
+ private ComponentName mBadComponentName;
+
+ private IPrintManager mIPrintManager;
+
+ /**
+ * Create a new IPrintManagerParametersTest and setup basic fields.
+ */
+ public IPrintManagerParametersTest() {
+ super();
+
+ mAppId = UserHandle.getAppId(Process.myUid());
+ mUserId = UserHandle.myUserId();
+ mBadPrintJobId = new PrintJobId();
+ mBadComponentName = new ComponentName("bad", "bad");
+ }
+
+ /**
+ * Create a mock PrintDocumentAdapter.
+ *
+ * @return The adapter
+ */
+ private @NonNull PrintDocumentAdapter createMockAdapter() {
+ return new PrintDocumentAdapter() {
+ @Override
+ public void onStart() {
+ onStartCalled();
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle extras) {
+ }
+
+ @Override
+ public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
+ }
+ };
+ }
+
+ /**
+ * Create mock print service callbacks.
+ *
+ * @return the callbacks
+ */
+ private PrintServiceCallbacks createMockCallbacks() {
+ return createMockPrintServiceCallbacks(
+ new Answer<PrinterDiscoverySessionCallbacks>() {
+ @Override
+ public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+ return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ // Get the session.
+ StubbablePrinterDiscoverySession session =
+ ((PrinterDiscoverySessionCallbacks) invocation
+ .getMock()).getSession();
+
+ if (session.getPrinters().isEmpty()) {
+ final String PRINTER_NAME = "good printer";
+ List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+ // Add the printer.
+ mGoodPrinterId = session.getService()
+ .generatePrinterId(PRINTER_NAME);
+
+ PrinterCapabilitiesInfo capabilities =
+ new PrinterCapabilitiesInfo.Builder(mGoodPrinterId)
+ .setMinMargins(
+ new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A4, true)
+ .addResolution(new Resolution("300x300",
+ "300x300", 300, 300),
+ true)
+ .setColorModes(
+ PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+
+ PrinterInfo printer = new PrinterInfo.Builder(
+ mGoodPrinterId,
+ PRINTER_NAME,
+ PrinterInfo.STATUS_IDLE)
+ .setCapabilities(capabilities)
+ .build();
+ printers.add(printer);
+
+ session.addPrinters(printers);
+ }
+ onPrinterDiscoverySessionStartCalled();
+ return null;
+ }
+ }, null, null, null, null, null, null);
+ }
+ },
+ null, null);
+ }
+
+ /**
+ * Create a IPrintJobStateChangeListener object.
+ *
+ * @return the object
+ * @throws Exception if the object could not be created.
+ */
+ private IPrintJobStateChangeListener createMockIPrintJobStateChangeListener() throws Exception {
+ return new PrintManager.PrintJobStateChangeListenerWrapper(null,
+ new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Create a IPrinterDiscoveryObserver object.
+ *
+ * @return the object
+ * @throws Exception if the object could not be created.
+ */
+ private IPrinterDiscoveryObserver createMockIPrinterDiscoveryObserver() throws Exception {
+ return new PrinterDiscoverySession.PrinterDiscoveryObserver(null);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockPrintService.setCallbacks(createMockCallbacks());
+
+ mGoodComponentName = getActivity().getComponentName();
+
+ mGoodPrintJob = print(createMockAdapter(), null);
+
+ mIPrintManager = IPrintManager.Stub
+ .asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
+
+ // Generate dummy printerId which is a valid PrinterId object, but does not correspond to a
+ // printer
+ mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer");
+
+ // Wait for PrintActivity to be ready
+ waitForStartAdapterCallbackCalled();
+
+ // Wait for printer discovery session to be ready
+ waitForPrinterDiscoverySessionStartCallbackCalled();
+ }
+
+ /**
+ * {@link Runnable} that can throw and {@link Exception}
+ */
+ private interface Invokable {
+ /**
+ * Execute the {@link Invokable}
+ *
+ * @throws Exception
+ */
+ public void run() throws Exception;
+ }
+
+ /**
+ * Assert that the invokable throws an expectedException
+ *
+ * @param invokable The {@link Invokable} to run
+ * @param expectedClass The {@link Exception} that is supposed to be thrown
+ */
+ public void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
+ throws Exception {
+ try {
+ invokable.run();
+ } catch (Exception e) {
+ if (e.getClass().isAssignableFrom(expectedClass)) {
+ return;
+ } else {
+ throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
+ + e.getClass().getName());
+ }
+ }
+
+ throw new AssertionError("No exception thrown");
+ }
+
+ /**
+ * test IPrintManager.getPrintJobInfo
+ */
+ public void testGetPrintJobInfo() throws Exception {
+ assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
+ mAppId, mUserId).getId());
+ assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId));
+ assertEquals(null, mIPrintManager.getPrintJobInfo(null, mAppId, mUserId));
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getPrintJobInfos
+ */
+ public void testGetPrintJobInfos() throws Exception {
+ List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
+
+ boolean foundPrintJob = false;
+ for (PrintJobInfo info : infos) {
+ if (info.getId().equals(mGoodPrintJob.getId())) {
+ assertEquals(PrintJobInfo.STATE_CREATED, info.getState());
+ foundPrintJob = true;
+ }
+ }
+ assertTrue(foundPrintJob);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.getPrintJobInfos(BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.print
+ */
+ public void testPrint() throws Exception {
+ final String name = "dummy print job";
+
+ final IPrintDocumentAdapter adapter = new PrintManager
+ .PrintDocumentAdapterDelegate(getActivity(), createMockAdapter());
+
+ // Valid parameters are tested in setUp()
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.print(null, adapter, null, mGoodComponentName.getPackageName(),
+ mAppId, mUserId);
+ }
+ }, IllegalArgumentException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.print(name, null, null, mGoodComponentName.getPackageName(),
+ mAppId, mUserId);
+ }
+ }, NullPointerException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.print(name, adapter, null, null, mAppId, mUserId);
+ }
+ }, IllegalArgumentException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.print(name, adapter, null, mBadComponentName.getPackageName(),
+ mAppId, mUserId);
+ }
+ }, IllegalArgumentException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.print(name, adapter, null, mGoodComponentName.getPackageName(),
+ BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.cancelPrintJob
+ */
+ public void testCancelPrintJob() throws Exception {
+ // Invalid print jobs IDs do not produce an exception
+ mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId);
+ mIPrintManager.cancelPrintJob(null, mAppId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+
+ // Must be last as otherwise mGoodPrintJob will not be good anymore
+ mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+ }
+
+ /**
+ * test IPrintManager.restartPrintJob
+ */
+ public void testRestartPrintJob() throws Exception {
+ mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+
+ // Invalid print jobs IDs do not produce an exception
+ mIPrintManager.restartPrintJob(mBadPrintJobId, mAppId, mUserId);
+ mIPrintManager.restartPrintJob(null, mAppId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.addPrintJobStateChangeListener
+ */
+ public void testAddPrintJobStateChangeListener() throws Exception {
+ final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
+
+ mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.addPrintJobStateChangeListener(null, mAppId, mUserId);
+ }
+ }, NullPointerException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.addPrintJobStateChangeListener(listener, BAD_APP_ID, mUserId);
+ }
+ }, SecurityException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.removePrintJobStateChangeListener
+ */
+ public void testRemovePrintJobStateChangeListener() throws Exception {
+ final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
+
+ mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+ mIPrintManager.removePrintJobStateChangeListener(listener, mUserId);
+
+ // Removing unknown listeners is a no-op
+ mIPrintManager.removePrintJobStateChangeListener(listener, mUserId);
+
+ mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.removePrintJobStateChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getInstalledPrintServices
+ */
+ public void testGetInstalledPrintServices() throws Exception {
+ List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId);
+ assertTrue(printServices.size() >= 2);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getEnabledPrintServices
+ */
+ public void testGetEnabledPrintServices() throws Exception {
+ List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId);
+ assertTrue(printServices.size() >= 2);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.createPrinterDiscoverySession
+ */
+ public void testCreatePrinterDiscoverySession() throws Exception {
+ final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+ mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
+
+ try {
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.createPrinterDiscoverySession(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ } finally {
+ // Remove discovery session so that the next test create a new one. Usually a leaked
+ // session is removed on the next call from the print service. But in this case we want
+ // to force a new call to onPrinterDiscoverySessionStart in the next test.
+ mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+ }
+ }
+
+ /**
+ * test IPrintManager.startPrinterDiscovery
+ */
+ public void testStartPrinterDiscovery() throws Exception {
+ final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+ final List<PrinterId> goodPrinters = new ArrayList<>();
+ goodPrinters.add(mGoodPrinterId);
+
+ final List<PrinterId> badPrinters = new ArrayList<>();
+ badPrinters.add(mBadPrinterId);
+
+ final List<PrinterId> emptyPrinters = new ArrayList<>();
+
+ final List<PrinterId> nullPrinters = new ArrayList<>();
+ nullPrinters.add(null);
+
+ mIPrintManager.startPrinterDiscovery(listener, goodPrinters, mUserId);
+
+ // Bad or no printers do no cause exceptions
+ mIPrintManager.startPrinterDiscovery(listener, badPrinters, mUserId);
+ mIPrintManager.startPrinterDiscovery(listener, emptyPrinters, mUserId);
+ mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.startPrinterDiscovery(listener, nullPrinters, mUserId);
+ }
+ }, NullPointerException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.startPrinterDiscovery(null, goodPrinters, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.stopPrinterDiscovery
+ */
+ public void testStopPrinterDiscovery() throws Exception {
+ final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+ mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+ mIPrintManager.stopPrinterDiscovery(listener, mUserId);
+
+ // Stopping an already stopped session is a no-op
+ mIPrintManager.stopPrinterDiscovery(listener, mUserId);
+
+ mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.stopPrinterDiscovery(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.validatePrinters
+ */
+ public void testValidatePrinters() throws Exception {
+ final List<PrinterId> goodPrinters = new ArrayList<>();
+ goodPrinters.add(mGoodPrinterId);
+
+ final List<PrinterId> badPrinters = new ArrayList<>();
+ badPrinters.add(mBadPrinterId);
+
+ final List<PrinterId> emptyPrinters = new ArrayList<>();
+
+ final List<PrinterId> nullPrinters = new ArrayList<>();
+ nullPrinters.add(null);
+
+ mIPrintManager.validatePrinters(goodPrinters, mUserId);
+
+ // Bad or empty list of printers do no cause exceptions
+ mIPrintManager.validatePrinters(badPrinters, mUserId);
+ mIPrintManager.validatePrinters(emptyPrinters, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.validatePrinters(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.validatePrinters(nullPrinters, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.startPrinterStateTracking
+ */
+ public void testStartPrinterStateTracking() throws Exception {
+ mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
+
+ // Bad printers do no cause exceptions
+ mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.startPrinterStateTracking(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getCustomPrinterIcon
+ */
+ public void testGetCustomPrinterIcon() throws Exception {
+ mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
+
+ // Bad printers do no cause exceptions
+ mIPrintManager.getCustomPrinterIcon(mBadPrinterId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.getCustomPrinterIcon(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.stopPrinterStateTracking
+ */
+ public void testStopPrinterStateTracking() throws Exception {
+ mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
+ mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
+
+ // Stop to track a non-tracked printer is a no-op
+ mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
+
+ // Bad printers do no cause exceptions
+ mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId);
+ mIPrintManager.stopPrinterStateTracking(mBadPrinterId, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.stopPrinterStateTracking(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.destroyPrinterDiscoverySession
+ */
+ public void testDestroyPrinterDiscoverySession() throws Exception {
+ final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+ mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
+ mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+
+ // Destroying already destroyed session is a no-op
+ mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.destroyPrinterDiscoverySession(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+}
diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java
new file mode 100644
index 0000000..86074a6
--- /dev/null
+++ b/core/tests/coretests/src/android/print/PrintTestActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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.print;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class PrintTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
new file mode 100644
index 0000000..9c11c22
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.print.mockservice;
+
+public class MockPrintService extends StubbablePrintService {
+
+ private static final Object sLock = new Object();
+
+ private static PrintServiceCallbacks sCallbacks;
+
+ public static void setCallbacks(PrintServiceCallbacks callbacks) {
+ synchronized (sLock) {
+ sCallbacks = callbacks;
+ }
+ }
+
+ @Override
+ protected PrintServiceCallbacks getCallbacks() {
+ synchronized (sLock) {
+ if (sCallbacks != null) {
+ sCallbacks.setService(this);
+ }
+ return sCallbacks;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
new file mode 100644
index 0000000..4e89207
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.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 android.print.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+public abstract class PrintServiceCallbacks {
+
+ private PrintService mService;
+
+ public PrintService getService() {
+ return mService;
+ }
+
+ public void setService(PrintService service) {
+ mService = service;
+ }
+
+ public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+ public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+ public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..26b7cae
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,50 @@
+/*
+ * 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.print.mockservice;
+
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+ private StubbablePrinterDiscoverySession mSession;
+
+ public void setSession(StubbablePrinterDiscoverySession session) {
+ mSession = session;
+ }
+
+ public StubbablePrinterDiscoverySession getSession() {
+ return mSession;
+ }
+
+ public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+ public abstract void onStopPrinterDiscovery();
+
+ public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+ public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+ public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
+ CustomPrinterIconCallback callback);
+
+ public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+ public abstract void onDestroy();
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
new file mode 100644
index 0000000..fb76e67
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.print.mockservice;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
new file mode 100644
index 0000000..b58b2735
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
@@ -0,0 +1,52 @@
+/*
+ * 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.print.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+public abstract class StubbablePrintService extends PrintService {
+
+ @Override
+ public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ return new StubbablePrinterDiscoverySession(this,
+ getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+ }
+ return null;
+ }
+
+ @Override
+ public void onRequestCancelPrintJob(PrintJob printJob) {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ callbacks.onRequestCancelPrintJob(printJob);
+ }
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJob printJob) {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ callbacks.onPrintJobQueued(printJob);
+ }
+ }
+
+ protected abstract PrintServiceCallbacks getCallbacks();
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..04683f2
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,92 @@
+/*
+ * 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.print.mockservice;
+
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+ private final PrintService mService;
+ private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+ public StubbablePrinterDiscoverySession(PrintService service,
+ PrinterDiscoverySessionCallbacks callbacks) {
+ mService = service;
+ mCallbacks = callbacks;
+ if (mCallbacks != null) {
+ mCallbacks.setSession(this);
+ }
+ }
+
+ public PrintService getService() {
+ return mService;
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+ if (mCallbacks != null) {
+ mCallbacks.onStartPrinterDiscovery(priorityList);
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() {
+ if (mCallbacks != null) {
+ mCallbacks.onStopPrinterDiscovery();
+ }
+ }
+
+ @Override
+ public void onValidatePrinters(List<PrinterId> printerIds) {
+ if (mCallbacks != null) {
+ mCallbacks.onValidatePrinters(printerIds);
+ }
+ }
+
+ @Override
+ public void onStartPrinterStateTracking(PrinterId printerId) {
+ if (mCallbacks != null) {
+ mCallbacks.onStartPrinterStateTracking(printerId);
+ }
+ }
+
+ @Override
+ public void onRequestCustomPrinterIcon(PrinterId printerId,
+ CustomPrinterIconCallback callback) {
+ if (mCallbacks != null) {
+ mCallbacks.onRequestCustomPrinterIcon(printerId, callback);
+ }
+ }
+
+ @Override
+ public void onStopPrinterStateTracking(PrinterId printerId) {
+ if (mCallbacks != null) {
+ mCallbacks.onStopPrinterStateTracking(printerId);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mCallbacks != null) {
+ mCallbacks.onDestroy();
+ }
+ }
+}
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/docs/html/preview/support.jd b/docs/html/preview/support.jd
index cfd9467..8c392aa 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -160,20 +160,21 @@
still perform BTLE and WiFi scans, but only when they are in the foreground. While in the background, those apps will get no results from BTLE and WiFi scans.</li>
</ul>
</li>
- <li>Permission changes
+ <li>Accessing accounts
<ul>
- <li>Updated the user interface for permissions and enhanced some of the permissions
- behaviors.</li>
- <li>The {@link android.Manifest.permission#GET_ACCOUNTS} permission is now a member of the
- {@link android.Manifest.permission_group#CONTACTS} permission group and it has a
- {@code android:protectionLevel} of {@code dangerous}. This change means that when
- targeting Android 6.0 (API level 23), you must check for and request this permission if
- your app requires it.
+ <li>Updated the behavior of {@link android.accounts.AccountManager} account
+ discovery methods.
</li>
-
- <li>The {@code android.permission.READ_PROFILE} and {@code android.permission.WRITE_PROFILE}
- permissions have been removed from the {@link android.Manifest.permission_group#CONTACTS}
- permission group.
+ <li>The GET_ACCOUNTS permission has been deprecated.
+ </li>
+ <li>Apps targeting API level 24 should start the intent returned by
+ newChooseAccountIntent(...) and await the result to acquire a reference
+ to the user's selected account. AccountManager methods like getAccounts and
+ related methods will only return those accounts managed by
+ authenticators that match the signatures of the calling app.
+ </li>
+ <li>Apps targeting API level 23 or earlier will continue to behave as
+ before.
</li>
</ul>
</li>
diff --git a/docs/html/training/id-auth/identify.jd b/docs/html/training/id-auth/identify.jd
index db9ab3a..4c399f9 100644
--- a/docs/html/training/id-auth/identify.jd
+++ b/docs/html/training/id-auth/identify.jd
@@ -15,8 +15,7 @@
<ol>
<li><a href="#ForYou">Determine if AccountManager is for You</a></li>
<li><a href="#TaskTwo">Decide What Type of Account to Use</a></li>
- <li><a href="#GetPermission">Request GET_ACCOUNT permission</a></li>
- <li><a href="#TaskFive">Query AccountManager for a List of Accounts</a></li>
+ <li><a href="#QueryAccounts">Query the user for an Account</a></li>
<li><a href="#IdentifyUser">Use the Account Object to Personalize Your App</a></li>
<li><a href="#IdIsEnough">Decide Whether an Account Name is Enough</a></li>
</ol>
@@ -71,48 +70,46 @@
<h2 id="TaskTwo">Decide What Type of Account to Use</h2>
<p>Android devices can store multiple accounts from many different providers.
-When you query {@link android.accounts.AccountManager} for account names, you can choose to filter
-by
-account type. The account type is a string that uniquely identifies the entity
-that issued the account. For instance, Google accounts have type "com.google,"
-while Twitter uses "com.twitter.android.auth.login."</p>
+When you query {@link android.accounts.AccountManager} for account names, you
+can choose to filter by account type. The account type is a string that
+uniquely identifies the entity that issued the account. For instance, Google
+accounts have type "com.google," while Twitter uses
+"com.twitter.android.auth.login."</p>
+<h2 id="QueryAccounts">Query the user for an Account</h2>
-<h2 id="GetPermission">Request GET_ACCOUNT permission</h2>
-
-<p>In order to get a list of accounts on the device, your app needs the {@link
-android.Manifest.permission#GET_ACCOUNTS}
-permission. Add a <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code
-<uses-permission>}</a> tag in your manifest file to request
-this permission:</p>
+<p>Once an account type has been determined, you can prompt the user with an
+account chooser as follows:
<pre>
-<manifest ... >
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
- ...
-</manifest>
+AccountManager am = AccountManager.get(this); // "this" reference the current Context
+Intent chooserIntent = am.newChooseAccountIntent(
+ null, // currently select account
+ null, // list of accounts that are allowed to be shown
+ new String[] { "com.google" }, // Only allow the user to select Google accounts
+ false,
+ null, // description text
+ null, // add account auth token type
+ null, // required features for added accounts
+ null); // options for adding an account
+this.startActivityForResult(chooserIntent, MY_REQUEST_CODE);
</pre>
-
-<h2 id="TaskFive">Query AccountManager for a List of Accounts</h2>
-
-<p>Once you decide what account type you're interested in, you need to query for accounts of that
-type. Get an instance of {@link android.accounts.AccountManager} by calling {@link
-android.accounts.AccountManager#get(android.content.Context) AccountManager.get()}. Then use that
-instance to call {@link android.accounts.AccountManager#getAccountsByType(java.lang.String)
-getAccountsByType()}.</p>
+<p>Once the chooser intent is started, the user will be presented with a list of
+appropriately typed accounts. From this list they will select one which will be
+returned to your app upon onActivityResult as follows:
<pre>
-AccountManager am = AccountManager.get(this); // "this" references the current Context
-
-Account[] accounts = am.getAccountsByType("com.google");
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == MY_REQUEST_CODE && resultCode == RESULT_OK) {
+ String name = data.getStringExtra(AccountManage.KEY_ACCOUNT_NAME);
+ String type = data.getStringExtra(AccountManage.KEY_ACCOUNT_TYPE);
+ Account selectedAccount = new Account(name, type);
+ doSomethingWithSelectedAccount(selectedAccount);
+ }
+}
</pre>
-<p>This returns an array of {@link android.accounts.Account} objects. If there's more than one
-{@link android.accounts.Account} in
-the array, you should present a dialog asking the user to select one.</p>
-
-
<h2 id="IdentifyUser">Use the Account Object to Personalize Your App</h2>
<p>The {@link android.accounts.Account} object contains an account name, which for Google accounts
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 84ca546..1857345 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,7 +19,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.Animator.AnimatorListener;
-import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
@@ -141,15 +140,6 @@
/** Local, mutable animator set. */
private final AnimatorSet mAnimatorSet = new AnimatorSet();
-
- private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidateSelf();
- }
- };
-
/**
* The resources against which this drawable was created. Used to attempt
* to inflate animators if applyTheme() doesn't get called.
@@ -211,6 +201,9 @@
@Override
public void draw(Canvas canvas) {
mAnimatedVectorState.mVectorDrawable.draw(canvas);
+ if (isStarted()) {
+ invalidateSelf();
+ }
}
@Override
@@ -493,7 +486,6 @@
* animators, or {@code null} if not available
*/
public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
- @NonNull ValueAnimator.AnimatorUpdateListener updateListener,
@Nullable Resources res) {
// Check for uninflated animators. We can remove this after we add
// support for Animator.applyTheme(). See comments in inflate().
@@ -519,17 +511,6 @@
final Animator nextAnim = prepareLocalAnimator(i);
builder.with(nextAnim);
}
-
- // Setup a value animator to get animation update callbacks.
- long totalDuration = animatorSet.getTotalDuration();
- ValueAnimator updateAnim = ValueAnimator.ofFloat(0f, 1f);
- if (totalDuration == ValueAnimator.DURATION_INFINITE) {
- updateAnim.setRepeatCount(ValueAnimator.INFINITE);
- } else {
- updateAnim.setDuration(totalDuration);
- }
- updateAnim.addUpdateListener(updateListener);
- builder.with(updateAnim);
}
}
@@ -622,7 +603,7 @@
@NonNull
private void ensureAnimatorSet() {
if (!mHasAnimatorSet) {
- mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mUpdateListener, mRes);
+ mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
mHasAnimatorSet = true;
mRes = null;
}
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/BufferPool.h b/libs/hwui/BufferPool.h
index 9bda233..005b399 100644
--- a/libs/hwui/BufferPool.h
+++ b/libs/hwui/BufferPool.h
@@ -16,8 +16,9 @@
#pragma once
-#include <utils/RefBase.h>
-#include <utils/Log.h>
+#include "utils/RefBase.h"
+#include "utils/Log.h"
+#include "utils/Macros.h"
#include <atomic>
#include <stdint.h>
@@ -37,6 +38,7 @@
class BufferPool : public VirtualLightRefBase {
public:
class Buffer {
+ PREVENT_COPY_AND_ASSIGN(Buffer);
public:
int64_t* getBuffer() { return mBuffer.get(); }
size_t getSize() { return mSize; }
@@ -57,14 +59,17 @@
return refs - 1;
}
+ bool isUniqueRef() {
+ return mRefs.load() == 1;
+ }
+
private:
friend class BufferPool;
- Buffer(BufferPool* pool, size_t size) {
+ Buffer(BufferPool* pool, size_t size) : mRefs(1) {
mSize = size;
mBuffer.reset(new int64_t[size]);
mPool = pool;
- mRefs++;
}
void setPool(BufferPool* pool) {
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 cf2726b..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..a457212 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>
@@ -33,6 +33,15 @@
FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
+ : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightCenter,
+ Rect(0, 0, 0, 0)) {
+}
+
+
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter,
+ const Rect &contentDrawBounds)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
@@ -72,14 +81,56 @@
}
}
- // Defer Fbo0
+ // It there are multiple render nodes, they are laid out as follows:
+ // #0 - backdrop (content + caption)
+ // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+ // #2 - additional overlay nodes
+ // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+ // resizing however it might become partially visible. The following render loop will crop the
+ // backdrop against the content and draw the remaining part of it. It will then draw the content
+ // cropped to the backdrop (since that indicates a shrinking of the window).
+ //
+ // Additional nodes will be drawn on top with no particular clipping semantics.
+
+ // The bounds of the backdrop against which the content should be clipped.
+ Rect backdropBounds = contentDrawBounds;
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ // If there is no content bounds we ignore the layering as stated above and start with 2.
+ int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
+
for (const sp<RenderNode>& node : nodes) {
if (node->nothingToDraw()) continue;
node->computeOrdering();
+ int count = mCanvasState.save(SaveFlags::MatrixClip);
- int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ if (layer == 0) {
+ const RenderProperties& properties = node->properties();
+ Rect targetBounds(properties.getLeft(), properties.getTop(),
+ properties.getRight(), properties.getBottom());
+ // Move the content bounds towards the fixed corner of the backdrop.
+ const int x = targetBounds.left;
+ const int y = targetBounds.top;
+ // Remember the intersection of the target bounds and the intersection bounds against
+ // which we have to crop the content.
+ backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+ backdropBounds.doIntersect(targetBounds);
+ } else if (layer == 1) {
+ // We shift and clip the content to match its final location in the window.
+ const float left = contentDrawBounds.left;
+ const float top = contentDrawBounds.top;
+ const float dx = backdropBounds.left - left;
+ const float dy = backdropBounds.top - top;
+ const float width = backdropBounds.getWidth();
+ const float height = backdropBounds.getHeight();
+ mCanvasState.translate(dx, dy);
+ // It gets cropped against the bounds of the backdrop to stay inside.
+ mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
+ }
+
deferNodePropsAndOps(*node);
mCanvasState.restoreToCount(count);
+ layer++;
}
}
@@ -327,7 +378,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 +399,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 +443,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,
@@ -450,6 +501,10 @@
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
+
+ // TODO: Fix this ( b/26569206 )
+/*
// Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
// Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
// MergingDrawBatch::canMergeWith()
@@ -464,6 +519,7 @@
} else {
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
+*/
}
void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
@@ -597,7 +653,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..dea9934 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -59,6 +59,11 @@
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
+ FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+ uint32_t viewportWidth, uint32_t viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter,
+ const Rect &contentDrawBounds);
+
virtual ~FrameBuilder() {}
/**
@@ -100,30 +105,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..e7cf3ec 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);
@@ -344,7 +345,7 @@
#if HWUI_NEW_OPS
FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
- mRenderNodes, mLightCenter);
+ mRenderNodes, mLightCenter, mContentDrawBounds);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
mOpaque, mLightInfo);
@@ -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 0cba344..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/BufferPoolTests.cpp b/libs/hwui/tests/unit/BufferPoolTests.cpp
index 09bd302..44e6d3a 100644
--- a/libs/hwui/tests/unit/BufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/BufferPoolTests.cpp
@@ -36,6 +36,7 @@
ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
acquiredBuffers[i] = pool->acquire();
ASSERT_NE(nullptr, acquiredBuffers[i]);
+ ASSERT_TRUE(acquiredBuffers[i]->isUniqueRef());
}
for (size_t i = 0; i < bufferCount; i++) {
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..618df14 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());
@@ -215,7 +215,8 @@
<< "Expect number of ops = 2 * loop count";
}
-TEST(FrameBuilder, clippedMerging) {
+// TODO: Disabled due to b/26793764
+TEST(FrameBuilder, DISABLED_clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -336,7 +337,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 +381,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 +449,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 +513,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 +535,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 +584,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 +633,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 +696,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 +850,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 +988,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 +1072,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 +1112,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/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 504c6d0..9517387 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,6 +389,7 @@
public static final int H264 = 2;
public static final int MPEG_4_SP = 3;
public static final int VP8 = 4;
+ public static final int HEVC = 5;
}
/**
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 e936810..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/layout/item_dir_grid.xml b/packages/DocumentsUI/res/layout/item_dir_grid.xml
index a08e029..b0331be 100644
--- a/packages/DocumentsUI/res/layout/item_dir_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_dir_grid.xml
@@ -17,7 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_item_margin"
android:background="@color/item_doc_background"
android:elevation="5dp"
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/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index f43b81c..ad8b0d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -107,6 +107,8 @@
? icicle.<State>getParcelable(EXTRA_STATE)
: buildState();
+ Metrics.logActivityLaunch(this, mState, getIntent());
+
setContentView(mLayoutId);
mRoots = DocumentsApplication.getRootsCache(this);
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/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 58537ee..5abe7f6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -282,6 +282,7 @@
}
private void createNewWindow() {
+ Metrics.logMultiWindow(this);
Intent intent = LauncherActivity.createLaunchIntent(this);
intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
startActivity(intent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
new file mode 100644
index 0000000..eb90b75
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -0,0 +1,251 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.Shared.DEBUG;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import com.android.documentsui.model.RootInfo;
+import com.android.internal.logging.MetricsLogger;
+
+/** @hide */
+public final class Metrics {
+ private static final String TAG = "Metrics";
+
+ // These are the native provider authorities that the metrics code is capable of recognizing and
+ // explicitly counting.
+ private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
+ private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
+ private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
+
+ // These strings have to be whitelisted in tron. Do not change them.
+ private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
+ private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
+ private static final String COUNT_OPEN_MIME = "docsui_open_mime";
+ private static final String COUNT_CREATE_MIME = "docsui_create_mime";
+ private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
+ private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
+ private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
+ private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
+
+ // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
+ // root that is not explicitly recognized by the Metrics code (see {@link
+ // #getSanitizedRootIndex}). Apps are also bucketed in this histogram using negative indices
+ // (see below).
+ private static final int ROOT_NONE = 0;
+ private static final int ROOT_OTHER = 1;
+ private static final int ROOT_AUDIO = 2;
+ private static final int ROOT_DEVICE_STORAGE = 3;
+ private static final int ROOT_DOWNLOADS = 4;
+ private static final int ROOT_HOME = 5;
+ private static final int ROOT_IMAGES = 6;
+ private static final int ROOT_RECENTS = 7;
+ private static final int ROOT_VIDEOS = 8;
+ // Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they
+ // are logged analogously to roots. Use negative numbers to identify apps.
+ private static final int ROOT_THIRD_PARTY_APP = -1;
+
+ // Indices for bucketing mime types.
+ private static final int MIME_OTHER = -2; // anything not enumerated below
+ private static final int MIME_NONE = -1; // null mime
+ private static final int MIME_ANY = 0; // */*
+ private static final int MIME_APPLICATION = 1; // application/*
+ private static final int MIME_AUDIO = 2; // audio/*
+ private static final int MIME_IMAGE = 3; // image/*
+ private static final int MIME_MESSAGE = 4; // message/*
+ private static final int MIME_MULTIPART = 5; // multipart/*
+ private static final int MIME_TEXT = 6; // text/*
+ private static final int MIME_VIDEO = 7; // video/*
+
+ /**
+ * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
+ *
+ * @param context
+ * @param state
+ * @param intent
+ */
+ public static void logActivityLaunch(Context context, State state, Intent intent) {
+ // Log the launch action.
+ logHistogram(context, COUNT_LAUNCH_ACTION, state.action);
+ // Then log auxiliary data (roots/mime types) associated with some actions.
+ Uri uri = intent.getData();
+ switch (state.action) {
+ case State.ACTION_OPEN:
+ logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
+ break;
+ case State.ACTION_CREATE:
+ logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
+ break;
+ case State.ACTION_GET_CONTENT:
+ logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
+ break;
+ case State.ACTION_MANAGE:
+ logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri));
+ break;
+ case State.ACTION_BROWSE:
+ logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Logs a root visited event. Call this when the user clicks on a root in the RootsFragment.
+ *
+ * @param context
+ * @param info
+ */
+ public static void logRootVisited(Context context, RootInfo info) {
+ logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
+ }
+
+ /**
+ * Logs an app visited event. Call this when the user clicks on an app in the RootsFragment.
+ *
+ * @param context
+ * @param info
+ */
+ public static void logAppVisited(Context context, ResolveInfo info) {
+ logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
+ }
+
+ /**
+ * Logs a multi-window start. Call this when the user spawns a new DocumentsUI window.
+ *
+ * @param context
+ */
+ public static void logMultiWindow(Context context) {
+ logCount(context, COUNT_MULTI_WINDOW);
+ }
+
+ /**
+ * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
+ *
+ * @param context
+ * @param name The counter to increment.
+ */
+ private static void logCount(Context context, String name) {
+ if (DEBUG) Log.d(TAG, name + ": " + 1);
+ MetricsLogger.count(context, name, 1);
+ }
+
+ /**
+ * Internal method for making a MetricsLogger.histogram call.
+ *
+ * @param context
+ * @param name The name of the histogram.
+ * @param bucket The bucket to increment.
+ */
+ private static void logHistogram(Context context, String name, int bucket) {
+ if (DEBUG) Log.d(TAG, name + ": " + bucket);
+ MetricsLogger.histogram(context, name, bucket);
+ }
+
+ /**
+ * Generates an integer identifying the given root. For privacy, this function only recognizes a
+ * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
+ * a single ROOT_OTHER bucket.
+ */
+ private static int sanitizeRoot(Uri uri) {
+ if (LauncherActivity.isLaunchUri(uri)) {
+ return ROOT_NONE;
+ }
+
+ switch (uri.getAuthority()) {
+ case AUTHORITY_MEDIA:
+ switch (DocumentsContract.getRootId(uri)) {
+ case "audio_root":
+ return ROOT_AUDIO;
+ case "images_root":
+ return ROOT_IMAGES;
+ case "videos_root":
+ return ROOT_VIDEOS;
+ default:
+ return ROOT_OTHER;
+ }
+ case AUTHORITY_STORAGE:
+ if ("home".equals(DocumentsContract.getRootId(uri))) {
+ return ROOT_HOME;
+ } else {
+ return ROOT_DEVICE_STORAGE;
+ }
+ case AUTHORITY_DOWNLOADS:
+ return ROOT_DOWNLOADS;
+ default:
+ return ROOT_OTHER;
+ }
+ }
+
+ /** @see #sanitizeRoot(Uri) */
+ private static int sanitizeRoot(RootInfo root) {
+ if (root.isRecents()) {
+ // Recents root is special and only identifiable via this method call. Other roots are
+ // identified by URI.
+ return ROOT_RECENTS;
+ } else {
+ return sanitizeRoot(root.getUri());
+ }
+ }
+
+ /** @see #sanitizeRoot(Uri) */
+ private static int sanitizeRoot(ResolveInfo info) {
+ // Log all apps under a single bucket in the roots histogram.
+ return ROOT_THIRD_PARTY_APP;
+ }
+
+ /**
+ * Generates an int identifying a mime type. For privacy, this function only recognizes a small
+ * set of hard-coded types. For any other type, this function returns "other".
+ *
+ * @param mimeType
+ * @return
+ */
+ private static int sanitizeMime(String mimeType) {
+ if (mimeType == null) {
+ return MIME_NONE;
+ } else if ("*/*".equals(mimeType)) {
+ return MIME_ANY;
+ } else {
+ String type = mimeType.substring(0, mimeType.indexOf('/'));
+ switch (type) {
+ case "application":
+ return MIME_APPLICATION;
+ case "audio":
+ return MIME_AUDIO;
+ case "image":
+ return MIME_IMAGE;
+ case "message":
+ return MIME_MESSAGE;
+ case "multipart":
+ return MIME_MULTIPART;
+ case "text":
+ return MIME_TEXT;
+ case "video":
+ return MIME_VIDEO;
+ }
+ }
+ // Bucket all other types into one bucket.
+ return MIME_OTHER;
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index beff196..c2aeb86 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -180,10 +180,14 @@
Item item = mAdapter.getItem(position);
if (item instanceof RootItem) {
BaseActivity activity = BaseActivity.get(RootsFragment.this);
- activity.onRootPicked(((RootItem) item).root);
+ RootInfo info = ((RootItem) item).root;
+ Metrics.logRootVisited(getActivity(), info);
+ activity.onRootPicked(info);
} else if (item instanceof AppItem) {
DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
- activity.onAppPicked(((AppItem) item).info);
+ ResolveInfo info = ((AppItem) item).info;
+ Metrics.logAppVisited(getActivity(), info);
+ activity.onAppPicked(info);
} else if (item instanceof SpacerItem) {
if (DEBUG) Log.d(TAG, "Ignoring click on spacer item.");
} else {
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/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index cd30e26..46a2098 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -16,6 +16,7 @@
package com.android.printspooler.ui;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Loader;
@@ -452,7 +453,7 @@
return mPersistenceManager.mReadHistoryCompleted;
}
- public void setTrackedPrinter(PrinterId printerId) {
+ public void setTrackedPrinter(@Nullable PrinterId printerId) {
if (isStarted() && mDiscoverySession != null
&& mDiscoverySession.isPrinterDiscoveryStarted()) {
if (mTrackedPrinter != null) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 811adda..743df99 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -1003,7 +1003,9 @@
if (currMediaSize == null) {
attributes.setMediaSize(defaults.getMediaSize());
} else {
- boolean foundCurrentMediaSize = false;
+ MediaSize newMediaSize = null;
+ boolean isPortrait = currMediaSize.isPortrait();
+
// Try to find the current media size in the capabilities as
// it may be in a different orientation.
MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
@@ -1011,14 +1013,21 @@
for (int i = 0; i < mediaSizeCount; i++) {
MediaSize mediaSize = sortedMediaSizes.get(i);
if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
- attributes.setMediaSize(currMediaSize);
- foundCurrentMediaSize = true;
+ newMediaSize = mediaSize;
break;
}
}
// If we did not find the current media size fall back to default.
- if (!foundCurrentMediaSize) {
- attributes.setMediaSize(defaults.getMediaSize());
+ if (newMediaSize == null) {
+ newMediaSize = defaults.getMediaSize();
+ }
+
+ if (newMediaSize != null) {
+ if (isPortrait) {
+ attributes.setMediaSize(newMediaSize.asPortrait());
+ } else {
+ attributes.setMediaSize(newMediaSize.asLandscape());
+ }
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
index cbc568a..6d60bb8 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -16,6 +16,7 @@
package com.android.printspooler.ui;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Loader;
@@ -89,7 +90,7 @@
return false;
}
- public void setTrackedPrinter(PrinterId printerId) {
+ public void setTrackedPrinter(@Nullable PrinterId printerId) {
FusedPrintersProvider provider = getPrinterProvider();
if (provider != null) {
provider.setTrackedPrinter(printerId);
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index 46267a2..3e1fc4a 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -16,6 +16,12 @@
<resources>
<declare-styleable name="RestrictedPreference">
- <attr name="userRestriction" format="string"/>
+ <attr name="userRestriction" format="string" />
+ <attr name="useAdminDisabledSummary" format="boolean" />
</declare-styleable>
-</resources>
\ No newline at end of file
+ <declare-styleable name="WifiEncryptionState">
+ <attr name="state_encrypted" format="boolean" />
+ </declare-styleable>
+ <attr name="wifi_signal" format="reference" />
+
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9a1d6a4..811751c 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -35,4 +35,7 @@
<!-- Lock icon for preferences locked by admin -->
<dimen name="restricted_lock_icon_size">16dp</dimen>
<dimen name="restricted_lock_icon_padding">4dp</dimen>
+
+ <dimen name="wifi_preference_badge_padding">8dip</dimen>
+
</resources>
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..d3c1364 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.pm.UserInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -36,6 +37,8 @@
import android.view.MenuItem;
import android.widget.TextView;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.List;
/**
@@ -60,15 +63,18 @@
*
* @param userRestriction Restriction to check
* @param userId User which we need to check if restriction is enforced on.
- * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
- * {@code null} If the restriction is not set. If the restriction is set by both device owner
- * and profile owner, then the admin will be set to {@code null} and userId to
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} If the restriction is not set. If the restriction is set by both device owner
+ * and profile owner, then the admin component will be set to {@code null} and userId to
* {@link UserHandle#USER_NULL}.
*/
public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
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;
@@ -109,45 +115,80 @@
}
/**
- * Checks if lock screen notification features are disabled by policy. This should be
- * only used for keyguard notification features but not the keyguard features
- * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user
- * as it won't work for that case.
+ * Checks if keyguard features are disabled by policy.
*
- * @param keyguardNotificationFeatures Could be any of notification features that can be
+ * @param keyguardFeatures Could be any of keyguard features that can be
* disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
- * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
- * {@code null} If the notification features are not disabled. If the restriction is set by
- * multiple admins, then the admin will be set to {@code null} and userId to
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} If the notification features are not disabled. If the restriction is set by
+ * multiple admins, then the admin component will be set to {@code null} and userId to
* {@link UserHandle#USER_NULL}.
*/
- public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context,
- int keyguardNotificationFeatures) {
+ public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
+ int keyguardFeatures) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- boolean isDisabledByMultipleAdmins = false;
- ComponentName adminComponent = null;
- List<ComponentName> admins = dpm.getActiveAdmins();
- if (admins != null) {
- int disabledKeyguardFeatures;
+ if (dpm == null) {
+ return null;
+ }
+ final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+ EnforcedAdmin enforcedAdmin = null;
+ final int userId = UserHandle.myUserId();
+ if (um.getUserInfo(userId).isManagedProfile()) {
+ final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+ if (admins == null) {
+ return null;
+ }
for (ComponentName admin : admins) {
- disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin);
- if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) {
- if (adminComponent == null) {
- adminComponent = admin;
+ if ((dpm.getKeyguardDisabledFeatures(admin, userId) & keyguardFeatures) != 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userId);
} else {
- isDisabledByMultipleAdmins = true;
- break;
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
}
}
- }
- EnforcedAdmin enforcedAdmin = null;
- if (adminComponent != null) {
- if (!isDisabledByMultipleAdmins) {
- enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
- } else {
- enforcedAdmin = new EnforcedAdmin();
+ } else {
+ // Consider all admins for this user and the profiles that are visible from this
+ // user that do not use a separate work challenge.
+ for (UserInfo userInfo : um.getProfiles(userId)) {
+ final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
+ if (admins == null) {
+ return null;
+ }
+ final boolean isSeparateProfileChallengeEnabled =
+ lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+ for (ComponentName admin : admins) {
+ if (!isSeparateProfileChallengeEnabled) {
+ if ((dpm.getKeyguardDisabledFeatures(admin, userInfo.id)
+ & keyguardFeatures) != 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ }
+ // This same admins could have set policies both on the managed profile
+ // and on the parent. So, if the admin has set the policy on the
+ // managed profile here, we don't need to further check if that admin
+ // has set policy on the parent admin.
+ continue;
+ }
+ }
+ if (userInfo.isManagedProfile()) {
+ // If userInfo.id is a managed profile, we also need to look at
+ // the policies set on the parent.
+ DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+ if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id)
+ & keyguardFeatures) != 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ }
+ }
+ }
+ }
}
}
return enforcedAdmin;
@@ -170,6 +211,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 +241,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 +268,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 +287,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();
@@ -268,9 +318,84 @@
return enforcedAdmin;
}
+ /**
+ * Checks if any admin has set maximum time to lock.
+ *
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} if no admin has set this restriction. If multiple admins has set this, then
+ * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL}
+ */
+ public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) {
+ final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+ EnforcedAdmin enforcedAdmin = null;
+ final int userId = UserHandle.myUserId();
+ if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ // If the user has a separate challenge, only consider the admins in that user.
+ final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+ if (admins == null) {
+ return null;
+ }
+ for (ComponentName admin : admins) {
+ if (dpm.getMaximumTimeToLock(admin, userId) > 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userId);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ }
+ }
+ }
+ } else {
+ // Return all admins for this user and the profiles that are visible from this
+ // user that do not use a separate work challenge.
+ final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ for (UserInfo userInfo : um.getProfiles(userId)) {
+ final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
+ if (admins == null) {
+ return null;
+ }
+ final boolean isSeparateProfileChallengeEnabled =
+ lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+ for (ComponentName admin : admins) {
+ if (!isSeparateProfileChallengeEnabled) {
+ if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ }
+ // This same admins could have set policies both on the managed profile
+ // and on the parent. So, if the admin has set the policy on the
+ // managed profile here, we don't need to further check if that admin
+ // has set policy on the parent admin.
+ continue;
+ }
+ }
+ if (userInfo.isManagedProfile()) {
+ // If userInfo.id is a managed profile, we also need to look at
+ // the policies set on the parent.
+ DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+ if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ }
+ }
+ }
+ }
+ }
+ }
+ return enforcedAdmin;
+ }
+
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());
@@ -391,6 +516,9 @@
public ComponentName component = null;
public int userId = UserHandle.USER_NULL;
+ // We use this to represent the case where a policy is enforced by multiple admins.
+ public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin();
+
public EnforcedAdmin(ComponentName component, int userId) {
this.component = component;
this.userId = userId;
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/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
index 58a477e..1859207 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -38,14 +39,39 @@
private static final String TAG = "SuggestionParser";
+ // If defined, only returns this suggestion if the feature is supported.
+ public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
+
+ /**
+ * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
+ * For instance:
+ * 0,10
+ * Will appear immediately, but if the user removes it, it will come back after 10 days.
+ *
+ * Another example:
+ * 10,30
+ * Will only show up after 10 days, and then again after 30.
+ */
+ public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
+
+ // Shared prefs keys for storing dismissed state.
+ // Index into current dismissed state.
+ private static final String DISMISS_INDEX = "_dismiss_index";
+ private static final String SETUP_TIME = "_setup_time";
+ private static final String IS_DISMISSED = "_is_dismissed";
+
+ private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
+
private final Context mContext;
private final List<SuggestionCategory> mSuggestionList;
private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>();
+ private final SharedPreferences mSharedPrefs;
- public SuggestionParser(Context context, int orderXml) {
+ public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
mContext = context;
mSuggestionList = (List<SuggestionCategory>) new SuggestionOrderInflater(mContext)
.parse(orderXml);
+ mSharedPrefs = sharedPrefs;
}
public List<Tile> getSuggestions() {
@@ -57,6 +83,23 @@
return suggestions;
}
+ /**
+ * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
+ * be disabled.
+ */
+ public boolean dismissSuggestion(Tile suggestion) {
+ String keyBase = suggestion.intent.getComponent().flattenToShortString();
+ int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
+ String dismissControl = suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
+ if (dismissControl == null || parseDismissString(dismissControl).length == index) {
+ return true;
+ }
+ mSharedPrefs.edit()
+ .putBoolean(keyBase + IS_DISMISSED, true)
+ .commit();
+ return false;
+ }
+
private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
int countBefore = suggestions.size();
Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -66,6 +109,11 @@
}
TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
addCache, null, suggestions, true, false);
+ for (int i = countBefore; i < suggestions.size(); i++) {
+ if (!isAvailable(suggestions.get(i)) || isDismissed(suggestions.get(i))) {
+ suggestions.remove(i--);
+ }
+ }
if (!category.multiple && suggestions.size() > (countBefore + 1)) {
// If there are too many, remove them all and only re-add the one with the highest
// priority.
@@ -80,6 +128,59 @@
}
}
+ private boolean isAvailable(Tile suggestion) {
+ String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
+ if (featureRequired != null) {
+ return mContext.getPackageManager().hasSystemFeature(featureRequired);
+ }
+ return true;
+ }
+
+ private boolean isDismissed(Tile suggestion) {
+ Object dismissObj = suggestion.metaData.get(META_DATA_DISMISS_CONTROL);
+ if (dismissObj == null) {
+ return false;
+ }
+ String dismissControl = String.valueOf(dismissObj);
+ String keyBase = suggestion.intent.getComponent().flattenToShortString();
+ if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
+ mSharedPrefs.edit()
+ .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
+ .commit();
+ }
+ // Default to dismissed, so that we can have suggestions that only first appear after
+ // some number of days.
+ if (!mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, true)) {
+ return false;
+ }
+ int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
+ int currentDismiss = parseDismissString(dismissControl)[index];
+ long time = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0), currentDismiss);
+ if (System.currentTimeMillis() >= time) {
+ // Dismiss timeout has passed, undismiss it.
+ mSharedPrefs.edit()
+ .putBoolean(keyBase + IS_DISMISSED, false)
+ .putInt(keyBase + DISMISS_INDEX, index + 1)
+ .commit();
+ return false;
+ }
+ return true;
+ }
+
+ private long getEndTime(long startTime, int daysDelay) {
+ long days = daysDelay * MILLIS_IN_DAY;
+ return startTime + days;
+ }
+
+ private int[] parseDismissString(String dismissControl) {
+ String[] dismissStrs = dismissControl.split(",");
+ int[] dismisses = new int[dismissStrs.length];
+ for (int i = 0; i < dismissStrs.length; i++) {
+ dismisses[i] = Integer.parseInt(dismissStrs[i]);
+ }
+ return dismisses;
+ }
+
private static class SuggestionCategory {
public String category;
public String pkg;
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/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 5d8668b..e53dd2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -228,7 +228,7 @@
private static String getActiveSubscriberId(Context context) {
final TelephonyManager tele = TelephonyManager.from(context);
final String actualSubscriberId = tele.getSubscriberId(
- SubscriptionManager.getDefaultDataSubId());
+ SubscriptionManager.getDefaultDataSubscriptionId());
return actualSubscriberId;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
new file mode 100644
index 0000000..284827b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -0,0 +1,224 @@
+/*
+ * 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.settingslib.wifi;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
+import android.net.wifi.WifiConfiguration;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+public class AccessPointPreference extends Preference {
+
+ private static final int[] STATE_SECURED = {
+ R.attr.state_encrypted
+ };
+ private static final int[] STATE_NONE = {};
+
+ private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
+
+ private final StateListDrawable mWifiSld;
+ private final int mBadgePadding;
+ private final UserBadgeCache mBadgeCache;
+
+ private TextView mTitleView;
+ private boolean mForSavedNetworks = false;
+ private AccessPoint mAccessPoint;
+ private Drawable mBadge;
+ private int mLevel;
+ private CharSequence mContentDescription;
+
+ static final int[] WIFI_CONNECTION_STRENGTH = {
+ R.string.accessibility_wifi_one_bar,
+ R.string.accessibility_wifi_two_bars,
+ R.string.accessibility_wifi_three_bars,
+ R.string.accessibility_wifi_signal_full
+ };
+
+ // Used for dummy pref.
+ public AccessPointPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mWifiSld = null;
+ mBadgePadding = 0;
+ mBadgeCache = null;
+ }
+
+ public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+ boolean forSavedNetworks) {
+ super(context);
+ mBadgeCache = cache;
+ mAccessPoint = accessPoint;
+ mForSavedNetworks = forSavedNetworks;
+ mAccessPoint.setTag(this);
+ mLevel = -1;
+
+ mWifiSld = (StateListDrawable) context.getTheme()
+ .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+
+ // Distance from the end of the title at which this AP's user badge should sit.
+ mBadgePadding = context.getResources()
+ .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+ refresh();
+ }
+
+ public AccessPoint getAccessPoint() {
+ return mAccessPoint;
+ }
+
+ @Override
+ public void onBindViewHolder(final PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ if (mAccessPoint == null) {
+ // Used for dummy pref.
+ return;
+ }
+ Drawable drawable = getIcon();
+ if (drawable != null) {
+ drawable.setLevel(mLevel);
+ }
+
+ mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
+ if (mTitleView != null) {
+ // Attach to the end of the title view
+ mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
+ mTitleView.setCompoundDrawablePadding(mBadgePadding);
+ }
+ view.itemView.setContentDescription(mContentDescription);
+ }
+
+ protected void updateIcon(int level, Context context) {
+ if (level == -1) {
+ setIcon(null);
+ } else {
+ if (getIcon() == null) {
+ // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
+ // set the icon (drawable) to that state's drawable.
+ // If sld is null then we are indexing and therefore do not have access to
+ // (nor need to display) the drawable.
+ if (mWifiSld != null) {
+ mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
+ ? STATE_SECURED
+ : STATE_NONE);
+ Drawable drawable = mWifiSld.getCurrent();
+ if (!mForSavedNetworks) {
+ setIcon(drawable);
+ } else {
+ setIcon(null);
+ }
+ }
+ }
+ }
+ }
+
+ protected void updateBadge(Context context) {
+ WifiConfiguration config = mAccessPoint.getConfig();
+ if (config != null) {
+ // Fetch badge (may be null)
+ // Get the badge using a cache since the PM will ask the UserManager for the list
+ // of profiles every time otherwise.
+ mBadge = mBadgeCache.getUserBadge(config.creatorUid);
+ }
+ }
+
+ /**
+ * Updates the title and summary; may indirectly call notifyChanged().
+ */
+ public void refresh() {
+ if (mForSavedNetworks) {
+ setTitle(mAccessPoint.getConfigName());
+ } else {
+ setTitle(mAccessPoint.getSsid());
+ }
+
+ final Context context = getContext();
+ int level = mAccessPoint.getLevel();
+ if (level != mLevel) {
+ mLevel = level;
+ updateIcon(mLevel, context);
+ notifyChanged();
+ }
+ updateBadge(context);
+
+ setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
+ : mAccessPoint.getSettingsSummary());
+
+ mContentDescription = getTitle();
+ if (getSummary() != null) {
+ mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary());
+ }
+ if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
+ mContentDescription = TextUtils.concat(mContentDescription, ",",
+ getContext().getString(WIFI_CONNECTION_STRENGTH[level]));
+ }
+ }
+
+ @Override
+ protected void notifyChanged() {
+ if (Looper.getMainLooper() != Looper.myLooper()) {
+ // Let our BG thread callbacks call setTitle/setSummary.
+ postNotifyChanged();
+ } else {
+ super.notifyChanged();
+ }
+ }
+
+ public void onLevelChanged() {
+ postNotifyChanged();
+ }
+
+ private void postNotifyChanged() {
+ if (mTitleView != null) {
+ mTitleView.post(mNotifyChanged);
+ } // Otherwise we haven't been bound yet, and don't need to update.
+ }
+
+ private final Runnable mNotifyChanged = new Runnable() {
+ @Override
+ public void run() {
+ notifyChanged();
+ }
+ };
+
+ public static class UserBadgeCache {
+ private final SparseArray<Drawable> mBadges = new SparseArray<>();
+ private final PackageManager mPm;
+
+ public UserBadgeCache(PackageManager pm) {
+ mPm = pm;
+ }
+
+ private Drawable getUserBadge(int userId) {
+ int index = mBadges.indexOfKey(userId);
+ if (index < 0) {
+ Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
+ mBadges.put(userId, badge);
+ return badge;
+ }
+ return mBadges.valueAt(index);
+ }
+ }
+}
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index fadfe63..e901e40 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -16,9 +16,4 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/system_primary_color" />
- <corners
- android:topLeftRadius="@dimen/notification_material_rounded_rect_radius"
- android:topRightRadius="@dimen/notification_material_rounded_rect_radius"
- android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
- android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/ic_data_saver.xml b/packages/SystemUI/res/drawable/ic_data_saver.xml
new file mode 100644
index 0000000..426238c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_saver.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="
+ M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
+ m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
+ m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
+ m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_saver_off.xml b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
new file mode 100644
index 0000000..0713548
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="
+ M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
+ m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
+ m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
+ m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 6a0277f..ae45663 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -20,7 +20,6 @@
<item>
<shape>
<solid android:color="@color/notification_material_background_color" />
- <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
</shape>
</item>
</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
index 6581942..b6a8b70 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
@@ -18,7 +18,6 @@
<item>
<shape>
<solid android:color="@color/notification_material_background_dimmed_color" />
- <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 686df2c..1bf7d4c 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -13,10 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="@dimen/notification_material_rounded_rect_radius_negative">
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
<solid android:color="@color/system_primary_color"/>
- <corners android:radius="@dimen/notification_material_rounded_rect_radius"/>
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_background_secondary.xml b/packages/SystemUI/res/drawable/qs_background_secondary.xml
index 3662e5a..31c0162 100644
--- a/packages/SystemUI/res/drawable/qs_background_secondary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_secondary.xml
@@ -17,7 +17,5 @@
<solid android:color="@color/system_secondary_color" />
<corners
android:topLeftRadius="0dp"
- android:topRightRadius="0dp"
- android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
- android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
+ android:topRightRadius="0dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index f09c01b..e98bfb8 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -14,9 +14,5 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-
<solid android:color="@color/system_primary_color" />
-
- <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
-
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/apps_bar.xml b/packages/SystemUI/res/layout/apps_bar.xml
deleted file mode 100644
index e226805..0000000
--- a/packages/SystemUI/res/layout/apps_bar.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
--->
-
-<!-- Container for the app shelf. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/app_shelf"
- android:orientation="horizontal"
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1">
- <com.android.systemui.statusbar.phone.NavigationBarApps
- android:id="@+id/navigation_bar_apps"
- android:layout_width="wrap_content"
- android:layout_height="match_parent" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 9ef743d..6e4b213 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -33,7 +33,8 @@
android:paddingTop="@dimen/car_lockscreen_disclaimer_title_padding_top" />
<TextView
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
android:text="@string/car_lockscreen_disclaimer_text"
android:textSize="@dimen/car_lockscreen_disclaimer_text_size"
android:paddingStart="@dimen/car_lockscreen_disclaimer_text_padding_start"
@@ -44,7 +45,6 @@
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/car_lockscreen_user_grid_view_padding_top"
android:stretchMode="columnWidth">
</com.android.systemui.statusbar.car.UserGridView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
index 479f18d..7677646 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -26,9 +26,10 @@
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/car_nav_button_icon"
android:layout_height="match_parent"
- android:layout_width="wrap_content"
+ android:layout_width="@dimen/car_navigation_button_width"
android:layout_centerInParent="true"
- android:animateLayoutChanges="true">
+ android:animateLayoutChanges="true"
+ android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
<com.android.keyguard.AlphaOptimizedImageButton
@@ -37,6 +38,7 @@
android:layout_width="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/car_nav_button_icon"
- android:animateLayoutChanges="true">
+ android:animateLayoutChanges="true"
+ android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
</com.android.systemui.statusbar.car.CarNavigationButton>
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index 53273cf..dad7cea 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -19,5 +19,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_more_divider"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_children_divider_height"
+ android:layout_height="@dimen/notification_divider_height"
android:background="#61000000" />
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index f430fa5..e56431b 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -87,9 +87,7 @@
android:id="@+id/quick_settings_panel"
android:background="#0000"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/notification_side_padding"
- android:layout_marginRight="@dimen/notification_side_padding" />
+ android:layout_height="wrap_content" />
</com.android.systemui.tuner.AutoScrollView>
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..6784695 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -23,8 +23,6 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="@dimen/status_bar_header_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:paddingStart="@dimen/notification_side_padding"
- android:paddingEnd="@dimen/notification_side_padding"
android:clipChildren="false"
android:clipToPadding="false"
android:baselineAligned="false"
@@ -70,6 +68,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/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 12cf137..89abe2d 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,20 +55,13 @@
layout="@layout/qs_panel"
android:layout_marginTop="@dimen/status_bar_header_height_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/notification_side_padding"
- android:layout_marginRight="@dimen/notification_side_padding"/>
+ android:layout_height="wrap_content" />
<!-- A view to reserve space for the collapsed stack -->
<!-- Layout height: notification_min_height + bottom_stack_peek_amount -->
<View
android:id="@+id/reserve_notification_space"
android:layout_height="@dimen/min_stack_height"
- android:layout_width="match_parent"
- android:layout_marginTop="@dimen/notifications_top_padding" />
-
- <View
- android:layout_height="@dimen/notification_side_padding"
android:layout_width="match_parent" />
</LinearLayout>
</com.android.systemui.statusbar.phone.ObservableScrollView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 5eca471..dd75dbf 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -23,8 +23,6 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="@dimen/status_bar_header_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:paddingStart="@dimen/notification_side_padding"
- android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
android:elevation="4dp"
android:background="@drawable/notification_header_bg"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
deleted file mode 100644
index e220a16..0000000
--- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<!-- Extends FrameLayout -->
-<com.android.systemui.statusbar.SpeedBumpView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/speed_bump_height"
- android:visibility="gone"
- >
- <com.android.systemui.statusbar.AlphaOptimizedView
- android:id="@+id/speedbump_line"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="#6fdddddd"
- android:layout_gravity="center_vertical"/>
-</com.android.systemui.statusbar.SpeedBumpView>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 4c80b48..3dca77d 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -65,8 +65,6 @@
android:layout_width="@dimen/notification_panel_width"
android:layout_height="wrap_content"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:paddingLeft="@dimen/notification_side_padding"
- android:paddingRight="@dimen/notification_side_padding"
android:visibility="invisible">
<FrameLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 7cbc55c..34796cd 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,8 +19,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
- android:layout_marginLeft="@dimen/notification_side_padding"
- android:layout_marginRight="@dimen/notification_side_padding"
android:background="@drawable/volume_dialog_background"
android:translationZ="4dp" >
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b2190ec..5e25d2c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -78,14 +78,13 @@
<color name="notification_material_background_color">#ffffffff</color>
<!-- The color of the material notification background when dimmed -->
- <color name="notification_material_background_dimmed_color">#f2ffffff</color>
+ <color name="notification_material_background_dimmed_color">#ccffffff</color>
<!-- The color of the material notification background when low priority -->
<color name="notification_material_background_low_priority_color">#fff5f5f5</color>
- <!-- The color of the material notification background for media notifications when no custom
- color is specified -->
- <color name="notification_material_background_media_default_color">#ff424242</color>
+ <!-- The background color of the notification shade -->
+ <color name="notification_shade_background_color">#ffeeeeee</color>
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#28000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 46a0f2a..e245c24 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -87,9 +87,6 @@
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_padding">0dp</dimen>
- <!-- half the distance between notifications in the panel -->
- <dimen name="notification_divider_height">2dp</dimen>
-
<!-- The padding on the global screenshot background image -->
<dimen name="global_screenshot_bg_padding">20dp</dimen>
@@ -137,7 +134,7 @@
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
- <dimen name="standard_notification_panel_width">416dp</dimen><!-- includes notification_side_padding on each side -->
+ <dimen name="standard_notification_panel_width">416dp</dimen>
<dimen name="notification_panel_width">@dimen/match_parent</dimen>
<!-- Gravity for the notification panel -->
@@ -298,17 +295,14 @@
<!-- The height of the area before the top stack in which the notifications slow down -->
<dimen name="top_stack_slow_down_length">12dp</dimen>
- <!-- The side padding of the notifications-->
- <dimen name="notification_side_padding">8dp</dimen>
-
<!-- Z distance between notifications if they are in the stack -->
- <dimen name="z_distance_between_notifications">1dp</dimen>
+ <dimen name="z_distance_between_notifications">0.5dp</dimen>
- <!-- The padding between the individual notification cards when dimmed. -->
- <dimen name="notification_padding_dimmed">0dp</dimen>
+ <!-- The height of the divider between the individual notifications. -->
+ <dimen name="notification_divider_height">0.5dp</dimen>
- <!-- The padding between the individual notification cards. -->
- <dimen name="notification_padding">2dp</dimen>
+ <!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups -->
+ <dimen name="notification_divider_height_increased">6dp</dimen>
<!-- The minimum amount of top overscroll to go to the quick settings. -->
<dimen name="min_top_overscroll_to_qs">36dp</dimen>
@@ -328,8 +322,6 @@
<!-- Falsing threshold used when dismissing notifications from the lockscreen. -->
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>
- <dimen name="notifications_top_padding">4dp</dimen>
-
<!-- Minimum distance the user has to drag down to go to the full shade. -->
<dimen name="keyguard_drag_down_min_distance">100dp</dimen>
@@ -374,21 +366,12 @@
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
- <!-- radius of the corners of the material rounded rect background -->
- <dimen name="notification_material_rounded_rect_radius">2dp</dimen>
-
- <!-- radius of the corners of the material rounded rect background but negative-->
- <dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
-
<!-- The padding between notification children when collapsed -->
<dimen name="notification_children_padding">4dp</dimen>
<!-- The padding on top of the first notification to the children container -->
<dimen name="notification_children_container_top_padding">8dp</dimen>
- <!-- The height of the divider between the notfication children -->
- <dimen name="notification_children_divider_height">1dp</dimen>
-
<!-- The vertical distance from which the notification appear when children are expanded -->
<dimen name="notification_appear_distance">140dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index ecdccee..04402b7 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -18,15 +18,15 @@
<resources>
<dimen name="car_lockscreen_disclaimer_title_size">48sp</dimen>
<dimen name="car_lockscreen_disclaimer_title_padding_start">96dp</dimen>
- <dimen name="car_lockscreen_disclaimer_title_padding_top">96dp</dimen>
+ <dimen name="car_lockscreen_disclaimer_title_padding_top">82dp</dimen>
<dimen name="car_lockscreen_disclaimer_text_size">28sp</dimen>
<dimen name="car_lockscreen_disclaimer_text_padding_start">96dp</dimen>
<dimen name="car_lockscreen_disclaimer_text_padding_end">96dp</dimen>
- <dimen name="car_lockscreen_disclaimer_text_padding_top">32dp</dimen>
+ <dimen name="car_lockscreen_disclaimer_text_padding_top">8dp</dimen>
<dimen name="car_lockscreen_user_grid_view_padding_start">10dp</dimen>
<dimen name="car_lockscreen_user_grid_view_padding_end">10dp</dimen>
- <dimen name="car_lockscreen_user_grid_view_padding_top">128dp</dimen>
<dimen name="car_fullscreen_user_pod_image_avatar_width">128dp</dimen>
<dimen name="car_fullscreen_user_pod_image_avatar_height">128dp</dimen>
<dimen name="car_fullscreen_user_pod_text_size">24sp</dimen>
+ <dimen name="car_navigation_button_width">64dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 0bd350b..87aedab 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -18,22 +18,22 @@
<resources>
<item type="id" name="translation_y_animator_tag"/>
<item type="id" name="translation_z_animator_tag"/>
- <item type="id" name="scale_animator_tag"/>
<item type="id" name="alpha_animator_tag"/>
<item type="id" name="top_inset_animator_tag"/>
<item type="id" name="height_animator_tag"/>
+ <item type="id" name="shadow_alpha_animator_tag"/>
<item type="id" name="translation_y_animator_end_value_tag"/>
<item type="id" name="translation_z_animator_end_value_tag"/>
- <item type="id" name="scale_animator_end_value_tag"/>
<item type="id" name="alpha_animator_end_value_tag"/>
<item type="id" name="top_inset_animator_end_value_tag"/>
<item type="id" name="height_animator_end_value_tag"/>
+ <item type="id" name="shadow_alpha_animator_end_value_tag"/>
<item type="id" name="translation_y_animator_start_value_tag"/>
<item type="id" name="translation_z_animator_start_value_tag"/>
- <item type="id" name="scale_animator_start_value_tag"/>
<item type="id" name="alpha_animator_start_value_tag"/>
<item type="id" name="top_inset_animator_start_value_tag"/>
<item type="id" name="height_animator_start_value_tag"/>
+ <item type="id" name="shadow_alpha_animator_start_value_tag"/>
<item type="id" name="doze_saved_filter_tag"/>
<item type="id" name="qs_icon_tag"/>
<item type="id" name="scrim"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 814e7ea..faf36ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1333,4 +1333,13 @@
<!-- Explanation of the status bar section of the tuner [CHAR LIMIT=NONE] -->
<string name="tuner_status_bar_explanation">Enable or disable icons from being shown in the status bar.</string>
+ <!-- Label for quick settings tile for data saver [CHAR LIMIT=30] -->
+ <string name="data_saver">Data Saver</string>
+
+ <!-- Accessibility description for data saver being on [CHAR LIMIT=NONE] -->
+ <string name="accessibility_data_saver_on">Data Saver is on</string>
+
+ <!-- Accessibility description for data saver being off [CHAR LIMIT=NONE] -->
+ <string name="accessibility_data_saver_off">Data Saver is off</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index fd4161f..5b955a4 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -30,11 +30,9 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.Interpolators;
public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
@@ -48,9 +46,6 @@
public static final int X = 0;
public static final int Y = 1;
- private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
- private final Interpolator mFastOutLinearInInterpolator;
-
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
@@ -97,8 +92,6 @@
mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press!
- mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.fast_out_linear_in);
mFalsingThreshold = context.getResources().getDimensionPixelSize(
R.dimen.swipe_helper_falsing_threshold);
mFalsingManager = FalsingManager.getInstance(context);
@@ -357,9 +350,9 @@
}
ObjectAnimator anim = createTranslationAnimation(animView, newPos);
if (useAccelerateInterpolator) {
- anim.setInterpolator(mFastOutLinearInInterpolator);
+ anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
} else {
- anim.setInterpolator(sLinearInterpolator);
+ anim.setInterpolator(Interpolators.LINEAR);
}
anim.setDuration(duration);
if (delay > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index c9ba885..25f8bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -24,8 +24,8 @@
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
+
+import com.android.systemui.statusbar.Interpolators;
import java.util.ArrayList;
@@ -35,7 +35,6 @@
public class ViewInvertHelper {
private final Paint mDarkPaint = new Paint();
- private final Interpolator mLinearOutSlowInInterpolator;
private final ColorMatrix mMatrix = new ColorMatrix();
private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
private final long mFadeDuration;
@@ -46,8 +45,6 @@
addTarget(v);
}
public ViewInvertHelper(Context context, long fadeDuration) {
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
mFadeDuration = fadeDuration;
}
@@ -89,7 +86,7 @@
}
});
animator.setDuration(mFadeDuration);
- animator.setInterpolator(mLinearOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setStartDelay(delay);
animator.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index c3a8f2e..92cd027 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -16,8 +16,6 @@
package com.android.systemui.assist;
-import com.android.systemui.R;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -35,6 +33,9 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+
/**
* Visually discloses that contextual data was provided to an assistant.
*/
@@ -120,13 +121,11 @@
R.interpolator.assist_disclosure_trace));
mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION);
mAlphaInAnimator.addUpdateListener(this);
- mAlphaInAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in));
+ mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration(
ALPHA_OUT_ANIMATION_DURATION);
mAlphaOutAnimator.addUpdateListener(this);
- mAlphaOutAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in));
+ mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
mAnimator = new AnimatorSet();
mAnimator.play(mAlphaInAnimator).with(mTracingAnimator);
mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
index 67017db..34770c4 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
@@ -20,19 +20,15 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
public class AssistOrbContainer extends FrameLayout {
private static final long EXIT_START_DELAY = 150;
- private final Interpolator mLinearOutSlowInInterpolator;
- private final Interpolator mFastOutLinearInInterpolator;
-
private View mScrim;
private View mNavbarScrim;
private AssistOrbView mOrb;
@@ -49,10 +45,6 @@
public AssistOrbContainer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
- mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.fast_out_slow_in);
}
@Override
@@ -109,12 +101,12 @@
.alpha(1f)
.setDuration(300)
.setStartDelay(0)
- .setInterpolator(mLinearOutSlowInInterpolator);
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
mNavbarScrim.animate()
.alpha(1f)
.setDuration(300)
.setStartDelay(0)
- .setInterpolator(mLinearOutSlowInInterpolator);
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
}
});
}
@@ -132,12 +124,12 @@
.alpha(0f)
.setDuration(250)
.setStartDelay(EXIT_START_DELAY)
- .setInterpolator(mFastOutLinearInInterpolator);
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mNavbarScrim.animate()
.alpha(0f)
.setDuration(250)
.setStartDelay(EXIT_START_DELAY)
- .setInterpolator(mFastOutLinearInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(endRunnable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
index a3372a8..2d933f6 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
@@ -27,13 +27,13 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
public class AssistOrbView extends FrameLayout {
@@ -43,8 +43,6 @@
private final Paint mBackgroundPaint = new Paint();
private final Rect mCircleRect = new Rect();
private final Rect mStaticRect = new Rect();
- private final Interpolator mAppearInterpolator;
- private final Interpolator mDisappearInterpolator;
private final Interpolator mOvershootInterpolator = new OvershootInterpolator();
private boolean mClipToOutline;
@@ -117,10 +115,6 @@
R.dimen.assist_orb_travel_distance);
mMaxElevation = context.getResources().getDimensionPixelSize(
R.dimen.assist_orb_elevation);
- mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in);
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setColor(getResources().getColor(R.color.assist_orb_color));
}
@@ -256,8 +250,8 @@
}
public void startExitAnimation(long delay) {
- animateCircleSize(0, 200, delay, mDisappearInterpolator);
- animateOffset(0, 200, delay, mDisappearInterpolator);
+ animateCircleSize(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
+ animateOffset(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
}
public void startEnterAnimation() {
@@ -266,7 +260,7 @@
@Override
public void run() {
animateCircleSize(mCircleMinSize, 300, 0 /* delay */, mOvershootInterpolator);
- animateOffset(mStaticOffset, 400, 0 /* delay */, mAppearInterpolator);
+ animateOffset(mStaticOffset, 400, 0 /* delay */, Interpolators.LINEAR_OUT_SLOW_IN);
}
});
}
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 6e22dde..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";
+ + ",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/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
new file mode 100644
index 0000000..1aeb0fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -0,0 +1,73 @@
+/*
+ * 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.qs.tiles;
+
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.DataSaverController;
+
+public class DataSaverTile extends QSTile<QSTile.BooleanState> implements
+ DataSaverController.Listener{
+
+ private final DataSaverController mDataSaverController;
+
+ public DataSaverTile(Host host) {
+ super(host);
+ mDataSaverController = host.getNetworkController().getDataSaverController();
+ }
+
+ @Override
+ protected BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (listening) {
+ mDataSaverController.addListener(this);
+ } else {
+ mDataSaverController.remListener(this);
+ }
+ }
+
+ @Override
+ protected void handleClick() {
+ mState.value = !mDataSaverController.isDataSaverEnabled();
+ mDataSaverController.setDataSaverEnabled(mState.value);
+ refreshState(mState.value);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.value = arg instanceof Boolean ? (Boolean) arg
+ : mDataSaverController.isDataSaverEnabled();
+ state.label = mContext.getString(R.string.data_saver);
+ state.contentDescription = mContext.getString(state.value ?
+ R.string.accessibility_data_saver_on : R.string.accessibility_data_saver_off);
+ state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
+ : R.drawable.ic_data_saver_off);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_DATA_SAVER;
+ }
+
+ @Override
+ public void onDataSaverChanged(boolean isDataSaving) {
+ refreshState(isDataSaving);
+ }
+}
\ No newline at end of file
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..87cfcff 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 827ee40..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/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 38d24ce..01bfcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -28,13 +29,12 @@
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -107,11 +107,8 @@
private OnActivatedListener mOnActivatedListener;
- private final Interpolator mLinearOutSlowInInterpolator;
- protected final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mSlowOutFastInInterpolator;
private final Interpolator mSlowOutLinearInInterpolator;
- private final Interpolator mLinearInterpolator;
private Interpolator mCurrentAppearInterpolator;
private Interpolator mCurrentAlphaInterpolator;
@@ -132,16 +129,37 @@
private FalsingManager mFalsingManager;
private boolean mTrackTouch;
+ private float mNormalBackgroundVisibilityAmount;
+ private ValueAnimator mFadeInFromDarkAnimator;
+ private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
+ = new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
+ }
+ };
+ private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mFadeInFromDarkAnimator = null;
+ updateOutlineAlpha();
+ }
+ };
+ private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
+ = new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateOutlineAlpha();
+ }
+ };
+ private float mShadowAlpha = 1.0f;
+
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mFastOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
- mLinearOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
- mLinearInterpolator = new LinearInterpolator();
setClipChildren(false);
setClipToPadding(false);
mLegacyColor = context.getColor(R.color.notification_legacy_background_color);
@@ -166,6 +184,7 @@
mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
updateBackground();
updateBackgroundTint();
+ updateOutlineAlpha();
}
private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -272,7 +291,7 @@
}
}
- private void startActivateAnimation(boolean reverse) {
+ private void startActivateAnimation(final boolean reverse) {
if (!isAttachedToWindow()) {
return;
}
@@ -291,8 +310,8 @@
Interpolator interpolator;
Interpolator alphaInterpolator;
if (!reverse) {
- interpolator = mLinearOutSlowInInterpolator;
- alphaInterpolator = mLinearOutSlowInInterpolator;
+ interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+ alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
} else {
interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
@@ -317,6 +336,16 @@
mBackgroundNormal.animate()
.alpha(reverse ? 0f : 1f)
.setInterpolator(alphaInterpolator)
+ .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float animatedFraction = animation.getAnimatedFraction();
+ if (reverse) {
+ animatedFraction = 1.0f - animatedFraction;
+ }
+ setNormalBackgroundVisibilityAmount(animatedFraction);
+ }
+ })
.setDuration(ACTIVATE_ANIMATION_LENGTH);
}
@@ -377,8 +406,27 @@
} else {
updateBackground();
}
- setOutlineAlpha(dark ? 0f : 1f);
- }
+ updateOutlineAlpha();
+ }
+
+ private void updateOutlineAlpha() {
+ if (mDark) {
+ setOutlineAlpha(0f);
+ return;
+ }
+ float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
+ alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
+ alpha *= mShadowAlpha;
+ if (mFadeInFromDarkAnimator != null) {
+ alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
+ }
+ setOutlineAlpha(alpha);
+ }
+
+ public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
+ mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
+ updateOutlineAlpha();
+ }
public void setShowingLegacyBackground(boolean showing) {
mShowingLegacyBackground = showing;
@@ -431,7 +479,7 @@
.scaleY(1f)
.setDuration(DARK_ANIMATION_LENGTH)
.setStartDelay(delay)
- .setInterpolator(mLinearOutSlowInInterpolator)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
@@ -441,7 +489,15 @@
background.setAlpha(1f);
}
})
+ .setUpdateListener(mBackgroundVisibilityUpdater)
.start();
+ mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
+ mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
+ mFadeInFromDarkAnimator.setStartDelay(delay);
+ mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
+ mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
+ mFadeInFromDarkAnimator.start();
}
/**
@@ -474,7 +530,7 @@
mBackgroundNormal.setAlpha(startAlpha);
mBackgroundAnimator =
ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
- mBackgroundAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mBackgroundAnimator.setDuration(duration);
mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -487,6 +543,7 @@
mBackgroundAnimator = null;
}
});
+ mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
mBackgroundAnimator.start();
}
@@ -504,6 +561,8 @@
mBackgroundNormal.setAlpha(1f);
removeCallbacks(mTapTimeoutRunnable);
}
+ setNormalBackgroundVisibilityAmount(
+ mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
}
protected boolean shouldHideBackground() {
@@ -577,16 +636,16 @@
float targetValue;
if (isAppearing) {
mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
- mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
+ mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
targetValue = 1.0f;
} else {
- mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
+ mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
targetValue = 0.0f;
}
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
targetValue);
- mAppearAnimator.setInterpolator(mLinearInterpolator);
+ mAppearAnimator.setInterpolator(Interpolators.LINEAR);
mAppearAnimator.setDuration(
(long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -772,6 +831,19 @@
return getBgColor() == otherView.getBgColor();
}
+ @Override
+ public float getShadowAlpha() {
+ return mShadowAlpha;
+ }
+
+ @Override
+ public void setShadowAlpha(float shadowAlpha) {
+ if (shadowAlpha != mShadowAlpha) {
+ mShadowAlpha = shadowAlpha;
+ updateOutlineAlpha();
+ }
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2592486..efa56bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -186,7 +186,7 @@
protected DevicePolicyManager mDevicePolicyManager;
protected IDreamManager mDreamManager;
- PowerManager mPowerManager;
+ protected PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
// public mode, private notifications, etc
@@ -219,8 +219,6 @@
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
- private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
-
private KeyboardShortcuts mKeyboardShortcuts;
/**
@@ -638,11 +636,6 @@
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in);
-
// Connect in to the status bar manager service
mCommandQueue = new CommandQueue(this);
@@ -1016,7 +1009,7 @@
final Animator a
= ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(mLinearOutSlowIn);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
a.start();
guts.setExposed(true);
mStackScroller.onHeightChanged(null, true /* needsAnimation */);
@@ -1048,7 +1041,7 @@
final Animator a = ViewAnimationUtils.createCircularReveal(v,
x, y, r, 0);
a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(mFastOutLinearIn);
+ a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index f71f092..24cd948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -31,7 +31,7 @@
view.animate()
.alpha(0f)
.setDuration(ANIMATION_DURATION_LENGTH)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -56,7 +56,7 @@
view.animate()
.alpha(1f)
.setDuration(ANIMATION_DURATION_LENGTH)
- .setInterpolator(PhoneStatusBar.ALPHA_IN)
+ .setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(null);
if (view.hasOverlappingRendering()) {
view.animate().withLayer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 8570198..5c83f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -24,8 +24,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
+
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -53,7 +52,6 @@
private final int[] mTemp2 = new int[2];
private boolean mDraggedFarEnough;
private ExpandableView mStartingChild;
- private Interpolator mInterpolator;
private float mLastHeight;
private FalsingManager mFalsingManager;
@@ -61,8 +59,6 @@
DragDownCallback dragDownCallback) {
mMinDragDistance = context.getResources().getDimensionPixelSize(
R.dimen.keyguard_drag_down_min_distance);
- mInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mCallback = callback;
mDragDownCallback = dragDownCallback;
@@ -187,7 +183,7 @@
}
ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
child.getActualHeight(), child.getMinHeight());
- anim.setInterpolator(mInterpolator);
+ anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -200,7 +196,7 @@
private void cancelExpansion() {
ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
- anim.setInterpolator(mInterpolator);
+ anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 93be009..6fae3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1126,6 +1126,11 @@
}
@Override
+ public boolean needsIncreasedPadding() {
+ return mIsSummaryWithChildren && isGroupExpanded();
+ }
+
+ @Override
protected boolean disallowSingleClick(MotionEvent event) {
float x = event.getX();
float y = event.getY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index a6fc4bb..44c6a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -24,22 +24,17 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.R;
-
/**
* Like {@link ExpandableView}, but setting an outline for the height and clipping.
*/
public abstract class ExpandableOutlineView extends ExpandableView {
private final Rect mOutlineRect = new Rect();
- protected final int mRoundedRectCornerRadius;
private boolean mCustomOutline;
- private float mOutlineAlpha = 1f;
+ private float mOutlineAlpha = -1f;
public ExpandableOutlineView(Context context, AttributeSet attrs) {
super(context, attrs);
- mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.notification_material_rounded_rect_radius);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
@@ -49,7 +44,7 @@
getWidth(),
Math.max(getActualHeight(), mClipTopAmount));
} else {
- outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius);
+ outline.setRect(mOutlineRect);
}
outline.setAlpha(mOutlineAlpha);
}
@@ -69,8 +64,10 @@
}
protected void setOutlineAlpha(float alpha) {
- mOutlineAlpha = alpha;
- invalidateOutline();
+ if (alpha != mOutlineAlpha) {
+ mOutlineAlpha = alpha;
+ invalidateOutline();
+ }
}
protected void setOutlineRect(RectF rect) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index c190864..a0fb34a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -388,6 +388,17 @@
return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
}
+ public float getShadowAlpha() {
+ return 0.0f;
+ }
+
+ public void setShadowAlpha(float shadowAlpha) {
+ }
+
+ public boolean needsIncreasedPadding() {
+ return false;
+ }
+
/**
* A listener notifying when {@link #getActualHeight} changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 0fa088b..c4ffd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.content.Context;
import android.view.ViewPropertyAnimator;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -41,8 +40,6 @@
private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1.0f / LINEAR_OUT_SLOW_IN_X2;
private Interpolator mLinearOutSlowIn;
- private Interpolator mFastOutSlowIn;
- private Interpolator mFastOutLinearIn;
private float mMinVelocityPxPerSecond;
private float mMaxLengthSeconds;
@@ -53,10 +50,6 @@
public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
mMaxLengthSeconds = maxLengthSeconds;
mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
- mFastOutSlowIn
- = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
- mFastOutLinearIn
- = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_linear_in);
mMinVelocityPxPerSecond
= MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
mHighVelocityPxPerSecond
@@ -150,7 +143,7 @@
// Just use a normal interpolator which doesn't take the velocity into account.
durationSeconds = maxLengthSeconds;
- mAnimatorProperties.interpolator = mFastOutSlowIn;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
}
mAnimatorProperties.duration = (long) (durationSeconds * 1000);
return mAnimatorProperties;
@@ -223,7 +216,7 @@
// Just use a normal interpolator which doesn't take the velocity into account.
durationSeconds = maxLengthSeconds;
- mAnimatorProperties.interpolator = mFastOutLinearIn;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
}
mAnimatorProperties.duration = (long) (durationSeconds * 1000);
return mAnimatorProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java
new file mode 100644
index 0000000..5979468
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java
@@ -0,0 +1,35 @@
+/*
+ * 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.statusbar;
+
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to receive interpolators from
+ */
+public class Interpolators {
+ public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+ public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ public static final Interpolator LINEAR = new LinearInterpolator();
+ public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 8058933..841b9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -33,13 +33,11 @@
import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewAnimationUtils;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
/**
* An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -55,8 +53,6 @@
private final int mMinBackgroundRadius;
private final Paint mCirclePaint;
- private final Interpolator mAppearInterpolator;
- private final Interpolator mDisappearInterpolator;
private final int mInverseColor;
private final int mNormalColor;
private final ArgbEvaluator mColorInterpolator;
@@ -136,10 +132,6 @@
mInverseColor = 0xff000000;
mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_affordance_min_background_radius);
- mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in);
mColorInterpolator = new ArgbEvaluator();
mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
}
@@ -261,7 +253,7 @@
RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
RenderNodeAnimator.PAINT_ALPHA, 255);
animator.setTarget(this);
- animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+ animator.setInterpolator(Interpolators.ALPHA_IN);
animator.setDuration(250);
animator.start();
}
@@ -282,7 +274,7 @@
RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
animator.setDuration(duration);
- animator.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+ animator.setInterpolator(Interpolators.ALPHA_OUT);
animator.setTarget(this);
animator.start();
}
@@ -352,8 +344,8 @@
cancelAnimator(mPreviewClipper);
ValueAnimator animator = getAnimatorToRadius(circleRadius);
Interpolator interpolator = circleRadius == 0.0f
- ? mDisappearInterpolator
- : mAppearInterpolator;
+ ? Interpolators.FAST_OUT_LINEAR_IN
+ : Interpolators.LINEAR_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
long duration = 250;
if (!slowAnimation) {
@@ -438,8 +430,8 @@
animator.addListener(mScaleEndListener);
if (interpolator == null) {
interpolator = imageScale == 0.0f
- ? mDisappearInterpolator
- : mAppearInterpolator;
+ ? Interpolators.FAST_OUT_LINEAR_IN
+ : Interpolators.LINEAR_OUT_SLOW_IN;
}
animator.setInterpolator(interpolator);
if (duration == -1) {
@@ -501,8 +493,8 @@
animator.addListener(mAlphaEndListener);
if (interpolator == null) {
interpolator = alpha == 0.0f
- ? mDisappearInterpolator
- : mAppearInterpolator;
+ ? Interpolators.FAST_OUT_LINEAR_IN
+ : Interpolators.LINEAR_OUT_SLOW_IN;
}
animator.setInterpolator(interpolator);
if (duration == -1) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 36cf906..635e66d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,8 +52,6 @@
private static final int VISIBLE_TYPE_SINGLELINE = 3;
private final Rect mClipBounds = new Rect();
- private final int mRoundRectRadius;
- private final boolean mRoundRectClippingEnabled;
private final int mMinContractedHeight;
private final OnLayoutChangeListener mLayoutUpdater = new OnLayoutChangeListener() {
@Override
@@ -100,13 +98,6 @@
}
};
- private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), mUnrestrictedContentHeight,
- mRoundRectRadius);
- }
- };
private OnClickListener mExpandClickListener;
private boolean mBeforeN;
private boolean mExpandable;
@@ -116,14 +107,9 @@
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
- mRoundRectRadius = getResources().getDimensionPixelSize(
- R.dimen.notification_material_rounded_rect_radius);
- mRoundRectClippingEnabled = getResources().getBoolean(
- R.bool.config_notifications_round_rect_clipping);
mMinContractedHeight = getResources().getDimensionPixelSize(
R.dimen.min_notification_layout_height);
reset(true);
- setOutlineProvider(mOutlineProvider);
}
public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
@@ -253,7 +239,6 @@
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
- updateRoundRectClipping();
}
public void setExpandedChild(View child) {
@@ -267,7 +252,6 @@
mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater);
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
- updateRoundRectClipping();
}
public void setHeadsUpChild(View child) {
@@ -281,7 +265,6 @@
mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater);
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
- updateRoundRectClipping();
}
@Override
@@ -344,27 +327,6 @@
updateClipping();
}
- private void updateRoundRectClipping() {
- boolean enabled = needsRoundRectClipping();
- setClipToOutline(enabled);
- }
-
- private boolean needsRoundRectClipping() {
- if (!mRoundRectClippingEnabled) {
- return false;
- }
- boolean needsForContracted = mContractedChild != null
- && mContractedChild.getVisibility() == View.VISIBLE
- && mContractedWrapper.needsRoundRectClipping();
- boolean needsForExpanded = mExpandedChild != null
- && mExpandedChild.getVisibility() == View.VISIBLE
- && mExpandedWrapper.needsRoundRectClipping();
- boolean needsForHeadsUp = mExpandedChild != null
- && mExpandedChild.getVisibility() == View.VISIBLE
- && mExpandedWrapper.needsRoundRectClipping();
- return needsForContracted || needsForExpanded || needsForHeadsUp;
- }
-
private void updateClipping() {
if (mClipToActualHeight) {
mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
@@ -412,7 +374,6 @@
boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
mSingleLineView.setVisible(singleLineVisible);
}
- updateRoundRectClipping();
}
private void animateToVisibleType(int visibleType) {
@@ -426,7 +387,6 @@
hiddenView.setVisible(false);
}
});
- updateRoundRectClipping();
}
/**
@@ -553,7 +513,6 @@
if (mHeadsUpChild != null) {
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
}
- updateRoundRectClipping();
}
private void updateSingleLineView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index e4cd7d9..5abd1d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -206,13 +206,8 @@
void saveImportance(final StatusBarNotification sbn) {
int progress = mSeekBar.getProgress();
try {
- if (mApplyToTopic.isChecked()) {
- mINotificationManager.setTopicImportance(sbn.getPackageName(), sbn.getUid(), mTopic,
- progress);
- } else {
- mINotificationManager.setAppImportance(
- sbn.getPackageName(), sbn.getUid(), progress);
- }
+ mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(),
+ mApplyToTopic.isChecked() ? mTopic : null, progress);
} catch (RemoteException e) {
// :(
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 682676b..4e3ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -22,7 +22,10 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
@@ -32,11 +35,14 @@
*/
public class ScrimView extends View
{
+ private final Paint mPaint = new Paint();
private int mScrimColor;
private boolean mIsEmpty = true;
private boolean mDrawAsSrc;
private float mViewAlpha = 1.0f;
private ValueAnimator mAlphaAnimator;
+ private Rect mExcludedRect = new Rect();
+ private boolean mHasExcludedArea;
private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -51,6 +57,7 @@
mAlphaAnimator = null;
}
};
+ private Runnable mChangeRunnable;
public ScrimView(Context context) {
this(context, null);
@@ -72,15 +79,43 @@
protected void onDraw(Canvas canvas) {
if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) {
PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER;
- int color = mScrimColor;
- color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color),
- Color.green(color), Color.blue(color));
- canvas.drawColor(color, mode);
+ int color = getScrimColorWithAlpha();
+ if (!mHasExcludedArea) {
+ canvas.drawColor(color, mode);
+ } else {
+ mPaint.setColor(color);
+ if (mExcludedRect.top > 0) {
+ canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
+ }
+ if (mExcludedRect.left > 0) {
+ canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
+ mPaint);
+ }
+ if (mExcludedRect.right < getWidth()) {
+ canvas.drawRect(mExcludedRect.right,
+ mExcludedRect.top,
+ getWidth(),
+ mExcludedRect.bottom,
+ mPaint);
+ }
+ if (mExcludedRect.bottom < getHeight()) {
+ canvas.drawRect(0, mExcludedRect.bottom, getWidth(), getHeight(), mPaint);
+ }
+ }
}
}
+ public int getScrimColorWithAlpha() {
+ int color = mScrimColor;
+ color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color),
+ Color.green(color), Color.blue(color));
+ return color;
+ }
+
public void setDrawAsSrc(boolean asSrc) {
mDrawAsSrc = asSrc;
+ mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC
+ : PorterDuff.Mode.SRC_OVER));
invalidate();
}
@@ -89,6 +124,9 @@
mIsEmpty = Color.alpha(color) == 0;
mScrimColor = color;
invalidate();
+ if (mChangeRunnable != null) {
+ mChangeRunnable.run();
+ }
}
}
@@ -105,8 +143,13 @@
if (mAlphaAnimator != null) {
mAlphaAnimator.cancel();
}
- mViewAlpha = alpha;
- invalidate();
+ if (alpha != mViewAlpha) {
+ mViewAlpha = alpha;
+ invalidate();
+ if (mChangeRunnable != null) {
+ mChangeRunnable.run();
+ }
+ }
}
public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) {
@@ -120,4 +163,23 @@
mAlphaAnimator.setDuration(durationOut);
mAlphaAnimator.start();
}
+
+ public void setExcludedArea(Rect area) {
+ if (area == null) {
+ mHasExcludedArea = false;
+ invalidate();
+ return;
+ }
+ area.left = Math.max(area.left, 0);
+ area.top = Math.max(area.top, 0);
+ area.right = Math.min(area.right, getWidth());
+ area.bottom = Math.min(area.bottom, getHeight());
+ mExcludedRect.set(area);
+ mHasExcludedArea = area.left < area.right && area.top < area.bottom;
+ invalidate();
+ }
+
+ public void setChangeRunnable(Runnable changeRunnable) {
+ mChangeRunnable = changeRunnable;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
deleted file mode 100644
index 1fc8744..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import com.android.systemui.R;
-
-/**
- * The view representing the separation between important and less important notifications
- */
-public class SpeedBumpView extends ExpandableView {
-
- private final int mSpeedBumpHeight;
- private AlphaOptimizedView mLine;
- private boolean mIsVisible = true;
- private final Interpolator mFastOutSlowInInterpolator;
-
- public SpeedBumpView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mSpeedBumpHeight = getResources()
- .getDimensionPixelSize(R.dimen.speed_bump_height);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
- android.R.interpolator.fast_out_slow_in);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line);
- }
-
- @Override
- protected int getInitialHeight() {
- return mSpeedBumpHeight;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mSpeedBumpHeight;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mLine.setPivotX(mLine.getWidth() / 2);
- mLine.setPivotY(mLine.getHeight() / 2);
- setOutlineProvider(null);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- int height = mSpeedBumpHeight;
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
- }
-
- @Override
- public boolean isTransparent() {
- return true;
- }
-
- public void performVisibilityAnimation(boolean nowVisible, long delay) {
- animateDivider(nowVisible, delay, null /* onFinishedRunnable */);
- }
-
- /**
- * Animate the divider to a new visibility.
- *
- * @param nowVisible should it now be visible
- * @param delay the delay after the animation should start
- * @param onFinishedRunnable A runnable which should be run when the animation is
- * finished.
- */
- public void animateDivider(boolean nowVisible, long delay, Runnable onFinishedRunnable) {
- if (nowVisible != mIsVisible) {
- // Animate dividers
- float endValue = nowVisible ? 1.0f : 0.0f;
- mLine.animate()
- .alpha(endValue)
- .setStartDelay(delay)
- .scaleX(endValue)
- .scaleY(endValue)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withEndAction(onFinishedRunnable);
- mIsVisible = nowVisible;
- } else {
- if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
- }
- }
- }
-
- public void setInvisible() {
- mLine.setAlpha(0.0f);
- mLine.setScaleX(0.0f);
- mLine.setScaleY(0.0f);
- mIsVisible = false;
- }
-
- @Override
- public void performRemoveAnimation(long duration, float translationDirection,
- Runnable onFinishedRunnable) {
- // TODO: Use duration
- performVisibilityAnimation(false, 0 /* delay */);
- }
-
- @Override
- public void performAddAnimation(long delay, long duration) {
- // TODO: Use duration
- performVisibilityAnimation(true, delay);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 2f66c41..c836637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -21,8 +21,6 @@
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
/**
* A common base class for all views in the notification stack scroller which don't have a
* background.
@@ -80,9 +78,9 @@
float endValue = nowVisible ? 1.0f : 0.0f;
Interpolator interpolator;
if (nowVisible) {
- interpolator = PhoneStatusBar.ALPHA_IN;
+ interpolator = Interpolators.ALPHA_IN;
} else {
- interpolator = PhoneStatusBar.ALPHA_OUT;
+ interpolator = Interpolators.ALPHA_OUT;
}
mAnimating = true;
mContent.animate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 3e2c4c6..bed64a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -39,6 +39,10 @@
* the navigation buttons by updating arrays_car.xml appropriately in an overlay.
*/
class CarNavigationBarController {
+ private static final String EXTRA_FACET_CATEGORIES = "categories";
+ private static final String EXTRA_FACET_PACKAGES = "packages";
+ private static final String EXTRA_FACET_ID = "filter_id";
+ private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
// Each facet of the navigation bar maps to a set of package names or categories defined in
// arrays_car.xml. Package names for a given facet are delimited by ";"
@@ -64,6 +68,7 @@
private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
private int mCurrentFacetIndex;
+ private String mCurrentPackageName;
public CarNavigationBarController(Context context,
CarNavigationBarView navBar,
@@ -75,6 +80,7 @@
}
public void taskChanged(String packageName) {
+ mCurrentPackageName = packageName;
// If the package name belongs to a filter, then highlight appropriate button in
// the navigation bar.
if (mFacetPackageMap.containsKey(packageName)) {
@@ -121,7 +127,8 @@
CarNavigationButton button = createNavButton(icon, i, hasLongpress);
mNavButtons.add(button);
- mNavBar.addButton(button, createNavButton(icon, i, hasLongpress));
+ mNavBar.addButton(button,
+ createNavButton(icon, i, hasLongpress) /* lightsOutButton */);
initFacetFilterMaps(i,
facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER),
@@ -132,7 +139,7 @@
}
}
- private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){
+ private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
mFacetCategories.add(categories);
for (int i = 0; i < categories.length; i++) {
mFacetCategoryMap.put(categories[i], id);
@@ -234,7 +241,6 @@
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- setCurrentFacet(id);
onFacetClicked(id);
}
});
@@ -245,13 +251,13 @@
@Override
public boolean onLongClick(View v) {
onFacetLongClicked(id);
- setCurrentFacet(id);
return true;
}
});
} else {
button.setLongClickable(false);
}
+
return button;
}
@@ -262,14 +268,35 @@
}
private void onFacetClicked(int index) {
- // TODO: determine what data to pass to the trampoline, so it can start
- // the default app or the lens picker.
- startActivity(mIntents.get(index));
+ Intent intent = mIntents.get(index);
+ String packageName = intent.getPackage();
+
+ if (packageName == null) {
+ return;
+ }
+
+ // Don't launch the lens picker if it's already running and the
+ // user clicks the same facet
+ if (packageName.equals(mCurrentPackageName) && index == mCurrentFacetIndex) {
+ return;
+ }
+
+ intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
+ intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
+ // The facet is identified by the index in which it was added to the nav bar.
+ // This value can be used to determine which facet was selected
+ intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
+
+ // If the current facet is clicked, we want to launch the picker by default
+ // rather than the "preferred/last run" app.
+ intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
+
+ setCurrentFacet(index);
+ startActivity(intent);
}
private void onFacetLongClicked(int index) {
- // TODO: determine what data to pass to the trampoline, so it can start
- // the default app or the lens picker.
+ setCurrentFacet(index);
startActivity(mLongPressIntents.get(index));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 36b3a8a..504f059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
@@ -43,13 +41,11 @@
super.onFinishInflate();
mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon);
mIcon.setClickable(false);
- mIcon.setScaleType(ImageView.ScaleType.CENTER);
mIcon.setBackgroundColor(android.R.color.transparent);
mIcon.setAlpha(UNSELECTED_ALPHA);
mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon);
mMoreIcon.setClickable(false);
- mMoreIcon.setScaleType(ImageView.ScaleType.CENTER);
mMoreIcon.setBackgroundColor(android.R.color.transparent);
mMoreIcon.setVisibility(INVISIBLE);
mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
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/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index fd65aac..97bf4b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -41,9 +41,4 @@
mInvertHelper.update(dark);
}
}
-
- @Override
- public boolean needsRoundRectClipping() {
- return true;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 4fd4cab..f43a5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -38,6 +38,7 @@
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -55,7 +56,6 @@
0, PorterDuff.Mode.SRC_ATOP);
private final int mIconDarkAlpha;
private final int mIconDarkColor = 0xffffffff;
- protected final Interpolator mLinearOutSlowInInterpolator;
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
@@ -69,8 +69,6 @@
protected NotificationHeaderViewWrapper(Context ctx, View view) {
super(view);
mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
- android.R.interpolator.linear_out_slow_in);
mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
mTransformationHelper = new ViewTransformationHelper();
resolveHeaderViews();
@@ -185,7 +183,7 @@
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
animator.addUpdateListener(updateListener);
animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
- animator.setInterpolator(mLinearOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setStartDelay(delay);
if (listener != null) {
animator.addListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index a959e07..3475d13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -26,6 +26,7 @@
import android.widget.TextView;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -64,7 +65,7 @@
- ownPosition[1]) * 0.33f)
.setDuration(
StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -103,7 +104,7 @@
.translationY(0)
.setDuration(
StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 0ceba78..a1cf07e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -65,14 +65,6 @@
public void notifyContentUpdated(StatusBarNotification notification) {};
/**
- * @return true if this template might need to be clipped with a round rect to make it look
- * nice, false otherwise
- */
- public boolean needsRoundRectClipping() {
- return false;
- }
-
- /**
* Update the appearance of the expand button.
*
* @param expandable should this view be expandable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 388ba0e..3e1c40a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -22,8 +22,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -31,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
@@ -44,7 +43,6 @@
private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
- public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
protected View mTransformedView;
@@ -110,7 +108,7 @@
}
}
transformedView.animate()
- .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
.withEndAction(new Runnable() {
@Override
@@ -168,7 +166,7 @@
.translationY(otherStablePosition[1] - ownPosition[1]);
}
transformedView.animate()
- .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
.withEndAction(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index a3f404a..37e5558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -32,6 +32,7 @@
import android.view.animation.LinearInterpolator;
import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
public class BarTransitions {
private static final boolean DEBUG = false;
@@ -124,7 +125,6 @@
private final int mTransparent;
private final int mWarning;
private final Drawable mGradient;
- private final TimeInterpolator mInterpolator;
private int mMode = -1;
private boolean mAnimating;
@@ -152,7 +152,6 @@
mWarning = context.getColor(com.android.internal.R.color.battery_saver_mode_color);
}
mGradient = context.getDrawable(gradientResourceId);
- mInterpolator = new LinearInterpolator();
}
@Override
@@ -222,7 +221,8 @@
mGradientAlpha = targetGradientAlpha;
} else {
final float t = (now - mStartTime) / (float)(mEndTime - mStartTime);
- final float v = Math.max(0, Math.min(mInterpolator.getInterpolation(t), 1));
+ final float v = Math.max(0, Math.min(
+ Interpolators.LINEAR.getInterpolation(t), 1));
mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
mColor = Color.argb(
(int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 3ff69c9..b5dba18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -23,11 +23,11 @@
import android.content.Context;
import android.os.Handler;
import android.util.Log;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.statusbar.Interpolators;
/**
* Controller which handles all the doze animations of the scrims.
@@ -37,10 +37,6 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final DozeParameters mDozeParameters;
- private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
- private final Interpolator mPulseInInterpolatorPickup;
- private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
- private final Interpolator mDozeAnimationInterpolator;
private final Handler mHandler = new Handler();
private final ScrimController mScrimController;
@@ -55,8 +51,6 @@
public DozeScrimController(ScrimController scrimController, Context context) {
mScrimController = scrimController;
mDozeParameters = new DozeParameters(context);
- mDozeAnimationInterpolator = mPulseInInterpolatorPickup =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
}
public void setDozing(boolean dozing, boolean animate) {
@@ -70,9 +64,11 @@
cancelPulsing();
if (animate) {
startScrimAnimation(false /* inFront */, 0f /* target */,
- NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
+ NotificationPanelView.DOZE_ANIMATION_DURATION,
+ Interpolators.LINEAR_OUT_SLOW_IN);
startScrimAnimation(true /* inFront */, 0f /* target */,
- NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
+ NotificationPanelView.DOZE_ANIMATION_DURATION,
+ Interpolators.LINEAR_OUT_SLOW_IN);
} else {
abortAnimations();
mScrimController.setDozeBehindAlpha(0f);
@@ -116,7 +112,7 @@
final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
startScrimAnimation(true /* inFront */, 0f,
mDozeParameters.getPulseInDuration(pickup),
- pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
+ pickup ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
mPulseInFinished);
}
}
@@ -266,7 +262,7 @@
if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
- mPulseOutInterpolator, mPulseOutFinished);
+ Interpolators.ALPHA_IN, mPulseOutFinished);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
deleted file mode 100644
index d2bec7c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.AppGlobals;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.util.Slog;
-import android.widget.ImageView;
-
-/**
- * Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
- * is hidden if the activity isn't recognized or if there is no icon.
- */
-class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> {
- private final static String TAG = "GetActivityIconTask";
-
- private final PackageManager mPackageManager;
-
- // The ImageView that will receive the icon.
- private final ImageView mImageView;
-
- public GetActivityIconTask(PackageManager packageManager, ImageView imageView) {
- mPackageManager = packageManager;
- mImageView = imageView;
- }
-
- @Override
- protected Drawable doInBackground(AppButtonData... params) {
- if (params.length != 1) {
- throw new IllegalArgumentException("Expected one parameter");
- }
- AppButtonData buttonData = params[0];
- AppInfo appInfo = buttonData.appInfo;
- try {
- IPackageManager mPM = AppGlobals.getPackageManager();
- ActivityInfo ai = mPM.getActivityInfo(
- appInfo.getComponentName(),
- 0,
- appInfo.getUser().getIdentifier());
-
- if (ai == null) {
- Slog.w(TAG, "Icon not found for " + appInfo);
- return null;
- }
-
- Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
- Drawable badgedIcon =
- mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
-
- if (NavigationBarApps.DEBUG) {
- // Draw pinned indicator and number of running tasks.
- Bitmap bitmap = Bitmap.createBitmap(
- badgedIcon.getIntrinsicWidth(),
- badgedIcon.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- badgedIcon.setBounds(
- 0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight());
- badgedIcon.draw(canvas);
- Paint paint = new Paint();
- paint.setStyle(Paint.Style.FILL);
- if (buttonData.pinned) {
- paint.setColor(Color.WHITE);
- canvas.drawCircle(10, 10, 10, paint);
- }
- if (buttonData.tasks != null && buttonData.tasks.size() > 0) {
- paint.setColor(Color.BLACK);
- canvas.drawCircle(60, 30, 30, paint);
- paint.setColor(Color.WHITE);
- paint.setTextSize(50);
- paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
- canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint);
- }
- badgedIcon = new BitmapDrawable(null, bitmap);
- }
-
- return badgedIcon;
- } catch (RemoteException e) {
- Slog.w(TAG, "Icon not found for " + appInfo, e);
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Drawable icon) {
- mImageView.setImageDrawable(icon);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 26abc48..e7064e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -43,7 +43,6 @@
private boolean mCollapseSnoozes;
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- private final int mNotificationsTopPadding;
public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
NotificationStackScrollLayout stackScroller,
@@ -54,8 +53,6 @@
Context context = stackScroller.getContext();
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
- mNotificationsTopPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.notifications_top_padding);
}
public boolean isTrackingHeadsUp() {
@@ -80,10 +77,6 @@
mInitialTouchX = x;
setTrackingHeadsUp(false);
ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
- if (child == null && y < mNotificationsTopPadding) {
- // We should also allow drags from the margin above the heads up
- child = mStackScroller.getChildAtRawPosition(x, y + mNotificationsTopPadding);
- }
mTouchingHeadsUpView = false;
if (child instanceof ExpandableNotificationRow) {
mPickedChild = (ExpandableNotificationRow) child;
@@ -113,8 +106,7 @@
mHeadsUpManager.unpinAll();
mPanel.setPanelScrimMinFraction((float) expandedHeight
/ mPanel.getMaxPanelHeight());
- mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight
- + mNotificationsTopPadding);
+ mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
mPanel.clearNotificattonEffects();
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 41adeb5..c220efe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -24,12 +24,11 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.KeyguardAffordanceView;
/**
@@ -60,8 +59,6 @@
private KeyguardAffordanceView mLeftIcon;
private KeyguardAffordanceView mCenterIcon;
private KeyguardAffordanceView mRightIcon;
- private Interpolator mAppearInterpolator;
- private Interpolator mDisappearInterpolator;
private Animator mSwipeAnimator;
private FalsingManager mFalsingManager;
private int mMinBackgroundRadius;
@@ -107,10 +104,6 @@
mHintGrowAmount =
mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
- mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in);
mFalsingManager = FalsingManager.getInstance(mContext);
}
@@ -272,7 +265,7 @@
}
}
});
- animator.setInterpolator(mAppearInterpolator);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setDuration(HINT_PHASE1_DURATION);
animator.start();
mSwipeAnimator = animator;
@@ -292,7 +285,7 @@
onFinishedListener.run();
}
});
- animator.setInterpolator(mDisappearInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
animator.setDuration(HINT_PHASE2_DURATION);
animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
animator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 14176a6..94d3829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -58,6 +58,7 @@
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -111,7 +112,6 @@
private AccessibilityController mAccessibilityController;
private PhoneStatusBar mPhoneStatusBar;
- private final Interpolator mLinearOutSlowInInterpolator;
private boolean mUserSetupComplete;
private boolean mPrewarmBound;
private Messenger mPrewarmMessenger;
@@ -146,8 +146,6 @@
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mLinearOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
}
private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@@ -600,7 +598,7 @@
mIndicationText.setAlpha(0f);
mIndicationText.animate()
.alpha(1f)
- .setInterpolator(mLinearOutSlowInInterpolator)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
}
@@ -610,7 +608,7 @@
element.animate()
.alpha(1f)
.translationY(0f)
- .setInterpolator(mLinearOutSlowInInterpolator)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setStartDelay(delay)
.setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index f41e47b..e67aa84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -23,14 +23,13 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -58,7 +57,6 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private int mSystemIconsSwitcherHiddenExpandedMargin;
- private Interpolator mFastOutSlowInInterpolator;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -73,8 +71,6 @@
mBatteryLevel = (TextView) findViewById(R.id.battery_level);
mCarrierLabel = (TextView) findViewById(R.id.keyguard_carrier_text);
loadDimens();
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
- android.R.interpolator.fast_out_slow_in);
updateUserSwitcher();
}
@@ -199,7 +195,7 @@
.translationX(0)
.setDuration(400)
.setStartDelay(userSwitcherHiding ? 300 : 0)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
if (userSwitcherHiding) {
getOverlay().add(mMultiUserSwitch);
@@ -207,7 +203,7 @@
.alpha(0f)
.setDuration(300)
.setStartDelay(0)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -223,7 +219,7 @@
.alpha(1f)
.setDuration(300)
.setStartDelay(200)
- .setInterpolator(PhoneStatusBar.ALPHA_IN);
+ .setInterpolator(Interpolators.ALPHA_IN);
}
return true;
}
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
deleted file mode 100644
index d0c14f1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ /dev/null
@@ -1,1119 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.animation.LayoutTransition;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
-import android.app.IActivityManager;
-import android.app.ITaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.DragEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.PopupMenu;
-import android.widget.Toast;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Container for application icons that appear in the navigation bar. Their appearance is similar
- * to the launcher hotseat. Clicking an icon launches or activates the associated activity. A long
- * click will trigger a drag to allow the icons to be reordered. As an icon is dragged the other
- * icons shift to make space for it to be dropped. These layout changes are animated.
- * Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the
- * right part, with no separator in between.
- */
-class NavigationBarApps extends LinearLayout
- implements NavigationBarAppsModel.OnAppsChangedListener {
- public final static boolean DEBUG = false;
- private final static String TAG = "NavigationBarApps";
-
- /**
- * Intent extra to store user serial number.
- */
- static final String EXTRA_PROFILE = "profile";
-
- // There are separate NavigationBarApps view instances for landscape vs. portrait, but they
- // share the data model.
- private static NavigationBarAppsModel sAppsModel;
-
- private final PackageManager mPackageManager;
- private final UserManager mUserManager;
- private final LayoutInflater mLayoutInflater;
- private final AppPackageMonitor mAppPackageMonitor;
- private final WindowManager mWindowManager;
-
-
- // This view has two roles:
- // 1) If the drag started outside the pinned apps list, it is a placeholder icon with a null
- // tag.
- // 2) If the drag started inside the pinned apps list, it is the icon for the app being dragged
- // with the associated AppInfo tag.
- // The icon is set invisible for the duration of the drag, creating a visual space for a drop.
- // When the user is not dragging this member is null.
- private ImageView mDragView;
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- int currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- onUserSwitched(currentUserId);
- } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
- UserHandle removedProfile = intent.getParcelableExtra(Intent.EXTRA_USER);
- onManagedProfileRemoved(removedProfile);
- }
- }
- };
-
- // Layout params for the window that contains the anchor for the popup menus.
- // We need to create a window for a popup menu because the NavBar window is too narrow and can't
- // contain the menu.
- private final WindowManager.LayoutParams mPopupAnchorLayoutParams;
- // View that contains the anchor for popup menus. The view occupies the whole screen, and
- // has a child that will be moved to make the menu to appear where we need it.
- private final ViewGroup mPopupAnchor;
- private final PopupMenu mPopupMenu;
-
- /**
- * True if popup menu code is busy with a popup operation.
- * Attempting to show a popup menu or to add menu items while it's returning true will
- * corrupt/crash the app.
- */
- private boolean mIsPopupInUse = false;
- private final int [] mClickedIconLocation = new int[2];
-
- public NavigationBarApps(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (sAppsModel == null) {
- sAppsModel = new NavigationBarAppsModel(context);
- }
- mPackageManager = context.getPackageManager();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mLayoutInflater = LayoutInflater.from(context);
- mAppPackageMonitor = new AppPackageMonitor();
-
- // Dragging an icon removes and adds back the dragged icon. Use the layout transitions to
- // trigger animation. By default all transitions animate, so turn off the unneeded ones.
- LayoutTransition transition = new LayoutTransition();
- // Don't trigger on disappear. Adding the view will trigger the layout animation.
- transition.disableTransitionType(LayoutTransition.DISAPPEARING);
- // Don't animate the dragged icon itself.
- transition.disableTransitionType(LayoutTransition.APPEARING);
- // When an icon is dragged off the shelf, start sliding the other icons over immediately
- // to match the parent view's animation.
- transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
- transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 0);
- setLayoutTransition(transition);
-
- TaskStackListener taskStackListener = new TaskStackListener();
- IActivityManager iam = ActivityManagerNative.getDefault();
- try {
- iam.registerTaskStackListener(taskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "registerTaskStackListener failed", e);
- }
-
- mPopupAnchorLayoutParams =
- new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
- PixelFormat.TRANSLUCENT);
- mPopupAnchorLayoutParams.setTitle("ShelfMenuAnchor");
-
- mPopupAnchor = (ViewGroup) mLayoutInflater.inflate(R.layout.shelf_menu_anchor, null);
-
- ImageView anchorButton =
- (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
- mPopupMenu = new PopupMenu(context, anchorButton);
- }
-
- // Monitor that catches events like "app uninstalled".
- private class AppPackageMonitor extends PackageMonitor {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
- super.onPackageRemoved(packageName, uid);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
- super.onPackageModified(packageName);
- }
-
- @Override
- public void onPackagesAvailable(String[] packages) {
- if (isReplacing()) {
- UserHandle user = new UserHandle(getChangingUserId());
-
- for (String packageName : packages) {
- postUnpinIfUnlauncheable(packageName, user);
- }
- }
- super.onPackagesAvailable(packages);
- }
-
- @Override
- public void onPackagesUnavailable(String[] packages) {
- if (!isReplacing()) {
- UserHandle user = new UserHandle(getChangingUserId());
-
- for (String packageName : packages) {
- postUnpinIfUnlauncheable(packageName, user);
- }
- }
- super.onPackagesUnavailable(packages);
- }
- }
-
- private void postUnpinIfUnlauncheable(final String packageName, final UserHandle user) {
- // This method doesn't necessarily get called in the main thread. Redirect the call into
- // the main thread.
- post(new Runnable() {
- @Override
- public void run() {
- if (!isAttachedToWindow()) return;
- unpinIfUnlauncheable(packageName, user);
- }
- });
- }
-
- private void unpinIfUnlauncheable(String packageName, UserHandle user) {
- // Unpin icons for all apps that match a package that perhaps became unlauncheable.
- boolean appsWereUnpinned = false;
- for(int i = getChildCount() - 1; i >= 0; --i) {
- View child = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)child.getTag();
- if (appButtonData == null) continue; // Skip the drag placeholder.
-
- if (!appButtonData.pinned) continue;
-
- AppInfo appInfo = appButtonData.appInfo;
- if (!appInfo.getUser().equals(user)) continue;
-
- ComponentName appComponentName = appInfo.getComponentName();
- if (!appComponentName.getPackageName().equals(packageName)) continue;
-
- if (sAppsModel.resolveApp(appInfo) != null) {
- continue;
- }
-
- appButtonData.pinned = false;
- appsWereUnpinned = true;
-
- if (appButtonData.isEmpty()) {
- removeViewAt(i);
- }
- }
- if (appsWereUnpinned) {
- savePinnedApps();
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- // When an icon is dragged out of the pinned area this view's width changes, which causes
- // the parent container's layout to change and the divider and recents icons to shift left.
- // Animate the parent's CHANGING transition.
- ViewGroup parent = (ViewGroup) getParent();
- LayoutTransition transition = new LayoutTransition();
- transition.disableTransitionType(LayoutTransition.APPEARING);
- transition.disableTransitionType(LayoutTransition.DISAPPEARING);
- transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
- transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- transition.enableTransitionType(LayoutTransition.CHANGING);
- parent.setLayoutTransition(transition);
-
- sAppsModel.setCurrentUser(ActivityManager.getCurrentUser());
- recreatePinnedAppButtons();
- updateRecentApps();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
- mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
- sAppsModel.addOnAppsChangedListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mContext.unregisterReceiver(mBroadcastReceiver);
- mAppPackageMonitor.unregister();
- sAppsModel.removeOnAppsChangedListener(this);
- }
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
- if (mIsPopupInUse && !isShown()) {
- // Hide the popup if current view became invisible.
- shutdownPopupMenu();
- }
- }
-
- private void addAppButton(AppButtonData appButtonData) {
- ImageView button = createAppButton();
- updateApp(button, appButtonData);
- addView(button);
- }
-
- private List<AppInfo> getPinnedApps() {
- List<AppInfo> apps = new ArrayList<AppInfo>();
- int childCount = getChildCount();
- for (int i = 0; i != childCount; ++i) {
- View child = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)child.getTag();
- if (appButtonData == null) continue; // Skip the drag placeholder.
- if(!appButtonData.pinned) continue;
- apps.add(appButtonData.appInfo);
- }
- return apps;
- }
-
- /**
- * Creates an ImageView icon for each pinned app. Removes any existing icons. May be called
- * to synchronize the current view with the shared data mode.
- */
- private void recreatePinnedAppButtons() {
- // Remove any existing icon buttons.
- removeAllViews();
-
- List<AppInfo> apps = sAppsModel.getApps();
- int appCount = apps.size();
- for (int i = 0; i < appCount; i++) {
- AppInfo app = apps.get(i);
- addAppButton(new AppButtonData(app, true /* pinned */));
- }
- }
-
- /**
- * Saves pinned apps stored in app icons into the data model.
- */
- private void savePinnedApps() {
- sAppsModel.setApps(getPinnedApps());
- }
-
- /**
- * Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item.
- */
- private ImageView createAppButton() {
- ImageView button = (ImageView) mLayoutInflater.inflate(
- R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
- button.setOnHoverListener(new AppHoverListener());
- button.setOnClickListener(new AppClickListener());
- button.setOnContextClickListener(new AppContextClickListener());
- // TODO: Ripple effect. Use either KeyButtonRipple or the default ripple background.
- button.setOnLongClickListener(new AppLongClickListener());
- button.setOnDragListener(new AppIconDragListener());
- return button;
- }
-
- private class AppLongClickListener implements View.OnLongClickListener {
- @Override
- public boolean onLongClick(View v) {
- mDragView = (ImageView) v;
- AppButtonData appButtonData = (AppButtonData) v.getTag();
- startAppDrag(mDragView, appButtonData.appInfo);
- return true;
- }
- }
-
- /**
- * Returns the human-readable name for an activity's package or null.
- * TODO: Cache the labels, perhaps in an LruCache.
- */
- @Nullable
- private CharSequence getAppLabel(AppInfo appInfo) {
- NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
- if (resolvedApp == null) return null;
-
- CharSequence unbadgedLabel = resolvedApp.ri.loadLabel(mPackageManager);
- return mUserManager.getBadgedLabelForUser(unbadgedLabel, appInfo.getUser());
- }
-
- /** Helper function to start dragging an app icon (either pinned or recent). */
- static void startAppDrag(ImageView icon, AppInfo appInfo) {
- // The drag data is an Intent to launch the activity.
- Intent mainIntent = Intent.makeMainActivity(appInfo.getComponentName());
- UserManager userManager =
- (UserManager) icon.getContext().getSystemService(Context.USER_SERVICE);
- long userSerialNumber = userManager.getSerialNumberForUser(appInfo.getUser());
- mainIntent.putExtra(EXTRA_PROFILE, userSerialNumber);
- ClipData dragData = ClipData.newIntent("", mainIntent);
- // Use the ImageView to create the shadow.
- View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon);
- // Use a global drag because the icon might be dragged into the launcher.
- icon.startDrag(dragData, shadow, null /* myLocalState */, View.DRAG_FLAG_GLOBAL);
- }
-
- @Override
- public boolean dispatchDragEvent(DragEvent event) {
- // ACTION_DRAG_ENTERED is handled by each individual app icon drag listener.
- boolean childHandled = super.dispatchDragEvent(event);
-
- // Other drag types are handled once per drag by this view. This is handled explicitly
- // because attaching a DragListener to this ViewGroup does not work -- the DragListener in
- // the children consumes the drag events.
- boolean handled = false;
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED:
- handled = onDragStarted(event);
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- handled = onDragEnded();
- break;
- case DragEvent.ACTION_DROP:
- handled = onDrop(event);
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- handled = onDragExited();
- break;
- }
-
- return handled || childHandled;
- }
-
- /** Returns true if a drag should be handled. */
- private static boolean canAcceptDrag(DragEvent event) {
- // Poorly behaved apps might not provide a clip description.
- if (event.getClipDescription() == null) {
- return false;
- }
- // The event must contain an intent.
- return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT);
- }
-
- /**
- * Sets up for a drag. Runs once per drag operation. Returns true if the data represents
- * an app shortcut and will be accepted for a drop.
- */
- private boolean onDragStarted(DragEvent event) {
- if (DEBUG) Slog.d(TAG, "onDragStarted");
-
- // Ensure that an app shortcut is being dragged.
- if (!canAcceptDrag(event)) {
- return false;
- }
-
- // If there are no pinned apps this view will be collapsed, but the user still needs some
- // empty space to use as a drag target.
- if (getChildCount() == 0) {
- mDragView = createPlaceholderDragView(0);
- }
-
- // If this is an existing icon being reordered, hide the app icon. The drag shadow will
- // continue to draw.
- if (mDragView != null) {
- mDragView.setVisibility(View.INVISIBLE);
- }
-
- // Listen for the drag end event.
- return true;
- }
-
- /**
- * Creates a blank icon-sized View to create an empty space during a drag.
- */
- private ImageView createPlaceholderDragView(int index) {
- ImageView button = createAppButton();
- addView(button, index);
- return button;
- }
-
- /**
- * Returns initial index for a new app that doesn't exist in Shelf.
- * Such apps get created by dragging them into Shelf from other apps or by dragging from Shelf
- * and then back, or by removing from shelf as an intermediate step of pinning an app via menu.
- * @param indexHint Initial proposed position for the item.
- * @param isAppPinned True if the app being dragged is pinned.
- */
- int getNewAppIndex(int indexHint, boolean isAppPinned) {
- int i;
- if (isAppPinned) {
- // For a pinned app, find the rightmost position to the left of the target that has a
- // pinned app. We'll insert to the right of that position.
- for (i = indexHint; i > 0; --i) {
- View v = getChildAt(i - 1);
- AppButtonData targetButtonData = (AppButtonData) v.getTag();
- if (targetButtonData.pinned) break;
- }
- } else {
- // For an unpinned app, find the leftmost position to the right of the target that has
- // an unpinned app. We'll insert to the left of that position.
- int childCount = getChildCount();
- for (i = indexHint; i < childCount; ++i) {
- View v = getChildAt(i);
- AppButtonData targetButtonData = (AppButtonData) v.getTag();
- if (!targetButtonData.pinned) break;
- }
- }
- return i;
- }
-
- /**
- * Handles a drag entering an existing icon. Not implemented in the drag listener because it
- * needs to use LinearLayout/ViewGroup methods.
- */
- private void onDragEnteredIcon(View target) {
- if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target));
-
- int targetIndex = indexOfChild(target);
-
- // If the drag didn't start from an existing shelf icon, add an invisible placeholder to
- // create empty space for the user to drag into.
- if (mDragView == null) {
- mDragView = createPlaceholderDragView(getNewAppIndex(targetIndex, true));
- return;
- }
-
- // If the user is dragging on top of the original icon location, do nothing.
- if (target == mDragView) {
- return;
- }
-
- // "Move" the dragged app by removing it and adding it back at the target location.
- AppButtonData targetButtonData = (AppButtonData) target.getTag();
- int dragViewIndex = indexOfChild(mDragView);
- AppButtonData dragViewButtonData = (AppButtonData) mDragView.getTag();
- // Calculating whether the dragged app is pinned. If the app came from outside if the shelf,
- // in which case dragViewButtonData == null, it's a new app that we'll pin. Otherwise, the
- // button data is defined, and we look whether that existing app is pinned.
- boolean isAppPinned = dragViewButtonData == null || dragViewButtonData.pinned;
-
- if (dragViewIndex == -1) {
- // Drag view exists, but is not a child, which means that the drag has started at or
- // already visited shelf, then left it, and now is entering it again.
- targetIndex = getNewAppIndex(targetIndex, isAppPinned);
- } else if (dragViewIndex < targetIndex) {
- // The dragged app is currently at the left of the view where the drag is.
- // We shouldn't allow moving a pinned app to the right of the unpinned app.
- if (!targetButtonData.pinned && isAppPinned) return;
- } else {
- // The dragged app is currently at the right of the view where the drag is.
- // We shouldn't allow moving a unpinned app to the left of the pinned app.
- if (targetButtonData.pinned && !isAppPinned) return;
- }
-
- // This works, but is subtle:
- // * If dragViewIndex > targetIndex then the dragged app is moving from right to left and
- // the dragged app will be added in front of the target.
- // * If dragViewIndex < targetIndex then the dragged app is moving from left to right.
- // Removing the drag view will shift the later views one position to the left. Adding
- // the view at targetIndex will therefore place the app *after* the target.
- removeView(mDragView);
- addView(mDragView, targetIndex);
- }
-
- private boolean onDrop(DragEvent event) {
- if (DEBUG) Slog.d(TAG, "onDrop");
-
- // An earlier drag event might have canceled the drag. If so, there is nothing to do.
- if (mDragView == null) {
- return true;
- }
-
- boolean dragResult = true;
- AppInfo appInfo = getAppFromDragEvent(event);
- if (appInfo == null) {
- // This wasn't a valid drop. Clean up the placeholder.
- removePlaceholderDragViewIfNeeded();
- dragResult = false;
- } else if (mDragView.getTag() == null) {
- // This is a drag that adds a new app. Convert the placeholder to a real icon.
- updateApp(mDragView, new AppButtonData(appInfo, true /* pinned */));
- }
- endDrag();
- return dragResult;
- }
-
- /** Cleans up at the end of a drag. */
- private void endDrag() {
- // An earlier drag event might have canceled the drag. If so, there is nothing to do.
- if (mDragView == null) return;
-
- mDragView.setVisibility(View.VISIBLE);
- mDragView = null;
- savePinnedApps();
- // Add recent tasks to the info of the potentially added app.
- updateRecentApps();
- }
-
- /** Returns an app info from a DragEvent, or null if the data wasn't valid. */
- private AppInfo getAppFromDragEvent(DragEvent event) {
- ClipData data = event.getClipData();
- if (data == null) {
- return null;
- }
- if (data.getItemCount() != 1) {
- return null;
- }
- ClipData.Item item = data.getItemAt(0);
- if (item == null) {
- return null;
- }
- Intent intent = item.getIntent();
- if (intent == null) {
- return null;
- }
- long userSerialNumber = intent.getLongExtra(EXTRA_PROFILE, -1);
- if (userSerialNumber == -1) {
- return null;
- }
- UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
- if (appUser == null) {
- return null;
- }
- ComponentName componentName = intent.getComponent();
- if (componentName == null) {
- return null;
- }
- AppInfo appInfo = new AppInfo(componentName, appUser);
- if (sAppsModel.resolveApp(appInfo) == null) {
- return null;
- }
- return appInfo;
- }
-
- /** Updates the app at a given view index. */
- private void updateApp(ImageView button, AppButtonData appButtonData) {
- CharSequence appLabel = getAppLabel(appButtonData.appInfo);
- button.setContentDescription(appLabel);
-
- button.setTag(appButtonData);
- new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
- }
-
- /** Removes the empty placeholder view. */
- private void removePlaceholderDragViewIfNeeded() {
- // If the drag has ended already there is nothing to do.
- if (mDragView == null) {
- return;
- }
- removeView(mDragView);
- }
-
- /** Cleans up at the end of the drag. */
- private boolean onDragEnded() {
- if (DEBUG) Slog.d(TAG, "onDragEnded");
- // If the icon wasn't already dropped into the app list then remove the placeholder.
- removePlaceholderDragViewIfNeeded();
- endDrag();
- return true;
- }
-
- /** Handles the dragged icon exiting the bounds of this view during the drag. */
- private boolean onDragExited() {
- if (DEBUG) Slog.d(TAG, "onDragExited");
- // Remove the placeholder. It will be added again if the user drags the icon back over
- // the shelf.
- removePlaceholderDragViewIfNeeded();
- return true;
- }
-
- /** Drag listener for individual app icons. */
- private class AppIconDragListener implements View.OnDragListener {
- @Override
- public boolean onDrag(View v, DragEvent event) {
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED: {
- // Every button listens for drag events in order to detect enter/exit.
- return canAcceptDrag(event);
- }
- case DragEvent.ACTION_DRAG_ENTERED: {
- // Forward to NavigationBarApps.
- onDragEnteredIcon(v);
- return false;
- }
- }
- return false;
- }
- }
-
- /**
- * Brings the menu popup to closed state.
- * Can be called at any stage of the asynchronous process of showing a menu.
- */
- private void shutdownPopupMenu() {
- mWindowManager.removeView(mPopupAnchor);
- mPopupMenu.dismiss();
- }
-
- /**
- * Shows already prepopulated popup menu using appIcon for anchor location.
- */
- private void showPopupMenu(ImageView appIcon) {
- // Movable view inside the popup anchor view. It serves as the actual anchor for the
- // menu.
- final ImageView anchorButton =
- (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
- // Set same drawable as for the clicked button to have same size.
- anchorButton.setImageDrawable(appIcon.getDrawable());
-
- // Move the anchor button to the position of the app button.
- appIcon.getLocationOnScreen(mClickedIconLocation);
- anchorButton.setTranslationX(mClickedIconLocation[0]);
- anchorButton.setTranslationY(mClickedIconLocation[1]);
-
- final OnAttachStateChangeListener onAttachStateChangeListener =
- new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- mPopupMenu.show();
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {}
- };
- anchorButton.addOnAttachStateChangeListener(onAttachStateChangeListener);
-
- mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
- @Override
- public void onDismiss(PopupMenu menu) {
- // FYU: thorough testing for closing menu either by the user or via
- // shutdownPopupMenu() called at various moments of the menu creation, revealed that
- // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu.
- mWindowManager.removeView(mPopupAnchor);
- anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
- mPopupMenu.setOnDismissListener(null);
- mPopupMenu.getMenu().clear();
- mIsPopupInUse = false;
- }
- });
-
- mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
- mIsPopupInUse = true;
- }
-
- private void activateTask(int taskPersistentId) {
- // Launch or bring the activity to front.
- final IActivityManager iAm = ActivityManagerNative.getDefault();
- try {
- iAm.startActivityFromRecents(taskPersistentId, null /* options */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception when activating a recent task", e);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Exception when activating a recent task", e);
- }
- }
-
- /**
- * Adds to the popup menu items for activating each of tasks in the specified list.
- */
- private void populateLaunchMenu(AppButtonData appButtonData) {
- Menu menu = mPopupMenu.getMenu();
- int taskCount = appButtonData.getTaskCount();
- for (int i = 0; i < taskCount; ++i) {
- final RecentTaskInfo taskInfo = appButtonData.tasks.get(i);
- MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString());
- item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- activateTask(taskInfo.persistentId);
- return true;
- }
- });
- }
- }
-
- /**
- * Shows a task selection menu for clicked or hovered-over apps that have more than 1 running
- * tasks.
- */
- void maybeShowLaunchMenu(ImageView appIcon) {
- if (mIsPopupInUse) return;
- AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
- if (appButtonData.getTaskCount() <= 1) return;
-
- populateLaunchMenu(appButtonData);
- showPopupMenu(appIcon);
- }
-
- /**
- * A listener for hovering over an app icon.
- */
- private class AppHoverListener implements View.OnHoverListener {
- private final long DELAY_MILLIS = 1000;
- private Runnable mShowMenuCallback;
-
- @Override
- public boolean onHover(final View v, MotionEvent event) {
- if (mShowMenuCallback == null) {
- mShowMenuCallback = new Runnable() {
- @Override
- public void run() {
- maybeShowLaunchMenu((ImageView) v);
- }
- };
- }
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- postDelayed(mShowMenuCallback, DELAY_MILLIS);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- removeCallbacks(mShowMenuCallback);
- break;
- }
- return false;
- }
- }
-
- /**
- * A click listener that launches an activity.
- */
- private class AppClickListener implements View.OnClickListener {
- private void launchApp(AppInfo appInfo, View anchor) {
- NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
- if (resolvedApp == null) {
- Toast.makeText(
- getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- return;
- }
-
- Intent launchIntent = resolvedApp.launchIntent;
-
- // Play a scale-up animation while launching the activity.
- // TODO: Consider playing a different animation, or no animation, if the activity is
- // already open in a visible window. In that case we should move the task to front
- // with minimal animation, perhaps using ActivityManager.moveTaskToFront().
- Rect sourceBounds = new Rect();
- anchor.getBoundsOnScreen(sourceBounds);
- ActivityOptions opts =
- ActivityOptions.makeScaleUpAnimation(
- anchor, 0, 0, anchor.getWidth(), anchor.getHeight());
- Bundle optsBundle = opts.toBundle();
- launchIntent.setSourceBounds(sourceBounds);
-
- mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
- }
-
- @Override
- public void onClick(View v) {
- AppButtonData appButtonData = (AppButtonData) v.getTag();
-
- if (appButtonData.getTaskCount() == 0) {
- launchApp(appButtonData.appInfo, v);
- } else {
- // Activate latest task.
- activateTask(appButtonData.tasks.get(0).persistentId);
-
- maybeShowLaunchMenu((ImageView) v);
- }
- }
- }
-
- /**
- * Context click listener that shows app's context menu.
- */
- private class AppContextClickListener implements View.OnContextClickListener {
- void updateState(ImageView appIcon) {
- savePinnedApps();
- if (DEBUG) {
- AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
- new GetActivityIconTask(mPackageManager, appIcon).execute(appButtonData);
- }
- }
-
- /**
- * Adds to the popup menu items for pinning and unpinning the app in the shelf.
- */
- void populateContextMenu(final ImageView appIcon) {
- final AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
- Menu menu = mPopupMenu.getMenu();
- if (appButtonData.pinned) {
- menu.add("Unpin").
- setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- appButtonData.pinned = false;
- removeView(appIcon);
- if (!appButtonData.isEmpty()) {
- // If the app has running tasks, re-add it to the end of shelf
- // after unpinning.
- addView(appIcon);
- }
- updateState(appIcon);
- return true;
- }
- });
- } else {
- menu.add("Pin").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- appButtonData.pinned = true;
- removeView(appIcon);
- // Re-add the pinned icon to the end of the pinned list.
- addView(appIcon, getNewAppIndex(getChildCount(), true));
- updateState(appIcon);
- return true;
- }
- });
- }
- }
-
- @Override
- public boolean onContextClick(View v) {
- if (mIsPopupInUse) return true;
- ImageView appIcon = (ImageView) v;
- populateContextMenu(appIcon);
- showPopupMenu(appIcon);
- return true;
- }
- }
-
- private void onUserSwitched(int currentUserId) {
- sAppsModel.setCurrentUser(currentUserId);
- recreatePinnedAppButtons();
- }
-
- private void onManagedProfileRemoved(UserHandle removedProfile) {
- // Unpin apps from the removed profile.
- boolean itemsWereUnpinned = false;
- for(int i = getChildCount() - 1; i >= 0; --i) {
- View view = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)view.getTag();
- if (appButtonData == null) return; // Skip the drag placeholder.
- if (!appButtonData.pinned) continue;
- if (!appButtonData.appInfo.getUser().equals(removedProfile)) continue;
-
- appButtonData.pinned = false;
- itemsWereUnpinned = true;
- if (appButtonData.isEmpty()) {
- removeViewAt(i);
- }
- }
- if (itemsWereUnpinned) {
- savePinnedApps();
- }
- }
-
- /**
- * Returns app data for a button that matches the provided app info, if it exists, or null
- * otherwise.
- */
- private AppButtonData findAppButtonData(AppInfo appInfo) {
- int size = getChildCount();
- for (int i = 0; i < size; ++i) {
- View view = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)view.getTag();
- if (appButtonData == null) continue; // Skip the drag placeholder.
- if (appButtonData.appInfo.equals(appInfo)) {
- return appButtonData;
- }
- }
- return null;
- }
-
- private void updateTasks(List<RecentTaskInfo> tasks) {
- // Remove tasks from all app buttons.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- View view = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)view.getTag();
- if (appButtonData == null) return; // Skip the drag placeholder.
- appButtonData.clearTasks();
- }
-
- // Re-add tasks to app buttons, adding new buttons if needed.
- int size = tasks.size();
- for (int i = 0; i != size; ++i) {
- RecentTaskInfo task = tasks.get(i);
- AppInfo taskAppInfo = taskToAppInfo(task);
- if (taskAppInfo == null) continue;
- AppButtonData appButtonData = findAppButtonData(taskAppInfo);
- if (appButtonData == null) {
- appButtonData = new AppButtonData(taskAppInfo, false);
- addAppButton(appButtonData);
- }
- appButtonData.addTask(task);
- }
-
- // Remove unpinned apps that now have no tasks.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- View view = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)view.getTag();
- if (appButtonData == null) return; // Skip the drag placeholder.
- if (appButtonData.isEmpty()) {
- removeViewAt(i);
- }
- }
-
- if (DEBUG) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- View view = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)view.getTag();
- if (appButtonData == null) return; // Skip the drag placeholder.
- new GetActivityIconTask(mPackageManager, (ImageView )view).execute(appButtonData);
-
- }
- }
- }
-
- private void updateRecentApps() {
- ActivityManager activityManager =
- (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- // TODO: Should this be getRunningTasks?
- List<RecentTaskInfo> recentTasks = activityManager.getRecentTasksForUser(
- ActivityManager.getMaxAppRecentsLimitStatic(),
- ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
- ActivityManager.RECENT_IGNORE_UNAVAILABLE |
- ActivityManager.RECENT_INCLUDE_PROFILES,
- UserHandle.USER_CURRENT);
- if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size());
- updateTasks(recentTasks);
- }
-
- private static ComponentName getActivityForTask(RecentTaskInfo task) {
- // If the task was started from an alias, return the actual activity component that was
- // initially started.
- if (task.origActivity != null) {
- return task.origActivity;
- }
- // Prefer the first activity of the task.
- if (task.baseActivity != null) {
- return task.baseActivity;
- }
- // Then goes the activity that started the task.
- if (task.realActivity != null) {
- return task.realActivity;
- }
- // This should not happen, but fall back to the base intent's activity component name.
- return task.baseIntent.getComponent();
- }
-
- private ComponentName getLaunchComponentForPackage(String packageName, int userId) {
- // This code is based on ApplicationPackageManager.getLaunchIntentForPackage.
- PackageManager packageManager = mContext.getPackageManager();
-
- // First see if the package has an INFO activity; the existence of
- // such an activity is implied to be the desired front-door for the
- // overall package (such as if it has multiple launcher entries).
- Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
- intentToResolve.addCategory(Intent.CATEGORY_INFO);
- intentToResolve.setPackage(packageName);
- List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser(
- intentToResolve, 0, userId);
-
- // Otherwise, try to find a main launcher activity.
- if (ris == null || ris.size() <= 0) {
- // reuse the intent instance
- intentToResolve.removeCategory(Intent.CATEGORY_INFO);
- intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
- intentToResolve.setPackage(packageName);
- ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
- }
- if (ris == null || ris.size() <= 0) {
- Slog.i(TAG, "Failed to build intent for " + packageName);
- return null;
- }
- return new ComponentName(ris.get(0).activityInfo.packageName,
- ris.get(0).activityInfo.name);
- }
-
- private AppInfo taskToAppInfo(RecentTaskInfo task) {
- ComponentName componentName = getActivityForTask(task);
- UserHandle taskUser = new UserHandle(task.userId);
- AppInfo appInfo = new AppInfo(componentName, taskUser);
-
- if (sAppsModel.resolveApp(appInfo) == null) {
- // If task's activity is not launcheable, fall back to a launch component of the
- // task's package.
- ComponentName component = getLaunchComponentForPackage(
- componentName.getPackageName(), task.userId);
-
- if (component == null) {
- return null;
- }
-
- appInfo = new AppInfo(component, taskUser);
- }
-
- return appInfo;
- }
-
- /**
- * A listener that updates the app buttons whenever the recents task stack changes.
- */
- private class TaskStackListener extends ITaskStackListener.Stub {
- @Override
- public void onTaskStackChanged() throws RemoteException {
- // Post the message back to the UI thread.
- post(new Runnable() {
- @Override
- public void run() {
- if (isAttachedToWindow()) {
- updateRecentApps();
- }
- }
- });
- }
-
- @Override
- public void onActivityPinned() {
- }
- }
-
- @Override
- public void onPinnedAppsChanged() {
- if (getPinnedApps().equals(sAppsModel.getApps())) return;
- recreatePinnedAppButtons();
- updateRecentApps();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
deleted file mode 100644
index 76a9798..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Data model and controller for app icons appearing in the navigation bar. The data is stored on
- * disk in SharedPreferences. Each icon has a separate pref entry consisting of a flattened
- * ComponentName.
- */
-class NavigationBarAppsModel {
- public interface OnAppsChangedListener {
- void onPinnedAppsChanged();
- }
-
- public class ResolvedApp {
- Intent launchIntent;
- ResolveInfo ri;
- }
-
- private final static String TAG = "NavigationBarAppsModel";
-
- // Default number of apps to load initially.
- private final static int NUM_INITIAL_APPS = 4;
-
- // Preferences file name.
- private final static String SHARED_PREFERENCES_NAME = "com.android.systemui.navbarapps";
-
- // Preference name for the version of the other preferences.
- private final static String VERSION_PREF = "version";
-
- // Current version number for preferences.
- private final static int CURRENT_VERSION = 3;
-
- // Preference name for the number of app icons.
- private final static String APP_COUNT_PREF = "app_count";
-
- // Preference name prefix for each app's info. The actual pref has an integer appended to it.
- private final static String APP_PREF_PREFIX = "app_";
-
- // User serial number prefix for each app's info. The actual pref has an integer appended to it.
- private final static String APP_USER_PREFIX = "app_user_";
-
- // Character separating current user serial number from the user-specific part of a pref.
- // Example "22|app_user_2" - when logged as user with serial 22, we'll use this pref for the
- // user serial of the third app of the logged-in user.
- private final static char USER_SEPARATOR = '|';
-
- private final Context mContext;
- private final UserManager mUserManager;
- private final SharedPreferences mPrefs;
-
- // Apps are represented as an ordered list of app infos.
- private List<AppInfo> mApps = new ArrayList<AppInfo>();
-
- private List<OnAppsChangedListener> mOnAppsChangedListeners =
- new ArrayList<OnAppsChangedListener>();
-
- // Id of the current user.
- private int mCurrentUserId = -1;
-
- // Serial number of the current user.
- private long mCurrentUserSerialNumber = -1;
-
- public NavigationBarAppsModel(Context context) {
- mContext = context;
- mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
- int version = mPrefs.getInt(VERSION_PREF, -1);
- if (version != CURRENT_VERSION) {
- // Since the data format changed, clean everything.
- SharedPreferences.Editor edit = mPrefs.edit();
- edit.clear();
- edit.putInt(VERSION_PREF, CURRENT_VERSION);
- edit.apply();
- }
- }
-
- @VisibleForTesting
- protected IPackageManager getPackageManager() {
- return AppGlobals.getPackageManager();
- }
-
- // Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
- public ResolvedApp resolveApp(AppInfo appInfo) {
- ComponentName component = appInfo.getComponentName();
- int appUserId = appInfo.getUser().getIdentifier();
-
- if (mCurrentUserId != appUserId) {
- // Check if app user is a profile of current user and the app user is enabled.
- UserInfo appUserInfo = mUserManager.getUserInfo(appUserId);
- UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId);
- if (appUserInfo == null || currentUserInfo == null
- || appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || appUserInfo.profileGroupId != currentUserInfo.profileGroupId
- || !appUserInfo.isEnabled()) {
- Slog.e(TAG, "User " + appUserId +
- " is is not a profile of the current user, or is disabled.");
- return null;
- }
- }
-
- // This code is based on LauncherAppsService.startActivityAsUser code.
- Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- launchIntent.setPackage(component.getPackageName());
-
- try {
- ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId);
- if (info == null) {
- Slog.e(TAG, "Activity " + component + " is not installed.");
- return null;
- }
-
- if (!info.exported) {
- Slog.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
- return null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get activity info for " + component, e);
- return null;
- }
-
- // Check that the component actually has Intent.CATEGORY_LAUNCHER
- // as calling startActivityAsUser ignores the category and just
- // resolves based on the component if present.
- List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent,
- 0 /* flags */, appUserId);
- final int size = apps.size();
- for (int i = 0; i < size; ++i) {
- ResolveInfo ri = apps.get(i);
- ActivityInfo activityInfo = ri.activityInfo;
- if (activityInfo.packageName.equals(component.getPackageName()) &&
- activityInfo.name.equals(component.getClassName())) {
- // Found an activity with category launcher that matches
- // this component so ok to launch.
- launchIntent.setComponent(component);
- ResolvedApp resolvedApp = new ResolvedApp();
- resolvedApp.launchIntent = launchIntent;
- resolvedApp.ri = ri;
- return resolvedApp;
- }
- }
-
- Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
- return null;
- }
-
- public void addOnAppsChangedListener(OnAppsChangedListener listener) {
- mOnAppsChangedListeners.add(listener);
- }
-
- public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
- mOnAppsChangedListeners.remove(listener);
- }
-
- /**
- * Reinitializes the model for a new user.
- */
- public void setCurrentUser(int userId) {
- mCurrentUserId = userId;
- mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(new UserHandle(userId));
-
- mApps.clear();
-
- int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
- if (appCount >= 0) {
- loadAppsFromPrefs(appCount);
- } else {
- // We switched to this user for the first time ever. This is a good opportunity to clean
- // prefs for users deleted in the past.
- removePrefsForDeletedUsers();
-
- addDefaultApps();
- }
- }
-
- /**
- * Removes prefs for users that don't exist on the device.
- */
- private void removePrefsForDeletedUsers() {
- // Build a set of string representations of serial numbers of the device users.
- final List<UserInfo> users = mUserManager.getUsers();
- final int userCount = users.size();
-
- final Set<String> userSerials = new HashSet<String> ();
-
- for (int i = 0; i < userCount; ++i) {
- userSerials.add(Long.toString(users.get(i).serialNumber));
- }
-
- // Walk though all prefs and delete ones which user is not in the string set.
- final Map<String, ?> allPrefs = mPrefs.getAll();
- final SharedPreferences.Editor edit = mPrefs.edit();
-
- for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
- final String key = pref.getKey();
- if (key.equals(VERSION_PREF)) continue;
-
- final int userSeparatorPos = key.indexOf(USER_SEPARATOR);
-
- if (userSeparatorPos < 0) {
- // Removing anomalous pref with no user.
- edit.remove(key);
- continue;
- }
-
- final String prefUserSerial = key.substring(0, userSeparatorPos);
-
- if (!userSerials.contains(prefUserSerial)) {
- // Removes pref for a not existing user.
- edit.remove(key);
- continue;
- }
- }
-
- edit.apply();
- }
-
- /** Returns the list of apps. */
- public List<AppInfo> getApps() {
- return mApps;
- }
-
- /** Sets the list of apps and saves it. */
- public void setApps(List<AppInfo> apps) {
- mApps = apps;
- savePrefs();
-
- int size = mOnAppsChangedListeners.size();
- for (int i = 0; i < size; ++i) {
- mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
- }
- }
-
- /** Saves the current model to disk. */
- private void savePrefs() {
- SharedPreferences.Editor edit = mPrefs.edit();
- int appCount = mApps.size();
- edit.putInt(userPrefixed(APP_COUNT_PREF), appCount);
- for (int i = 0; i < appCount; i++) {
- final AppInfo appInfo = mApps.get(i);
- String componentNameString = appInfo.getComponentName().flattenToString();
- edit.putString(prefNameForApp(i), componentNameString);
- long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser());
- edit.putLong(prefUserForApp(i), userSerialNumber);
- }
- // Start an asynchronous disk write.
- edit.apply();
- }
-
- /** Loads AppInfo from prefs. Returns null if something is wrong. */
- private AppInfo loadAppFromPrefs(int index) {
- String prefValue = mPrefs.getString(prefNameForApp(index), null);
- if (prefValue == null) {
- Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index));
- return null;
- }
- ComponentName componentName = ComponentName.unflattenFromString(prefValue);
- if (componentName == null) {
- Slog.w(TAG, "Invalid component name " + prefValue);
- return null;
- }
- long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1);
- if (userSerialNumber == -1) {
- Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index));
- return null;
- }
- UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
- if (appUser == null) {
- Slog.w(TAG, "No user for serial " + userSerialNumber);
- return null;
- }
- AppInfo appInfo = new AppInfo(componentName, appUser);
- if (resolveApp(appInfo) == null) {
- return null;
- }
- return appInfo;
- }
-
- /** Loads the list of apps from SharedPreferences. */
- private void loadAppsFromPrefs(int appCount) {
- for (int i = 0; i < appCount; i++) {
- AppInfo appInfo = loadAppFromPrefs(i);
- if (appInfo != null) {
- mApps.add(appInfo);
- }
- }
-
- if (appCount != mApps.size()) savePrefs();
- }
-
- /** Adds the first few apps from the owner profile. Used for demo purposes. */
- private void addDefaultApps() {
- // Get a list of all app activities.
- final Intent queryIntent = new Intent(Intent.ACTION_MAIN, null);
- queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- final List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(
- queryIntent, 0 /* flags */, mCurrentUserId);
- final int appCount = apps.size();
- for (int i = 0; i < NUM_INITIAL_APPS && i < appCount; i++) {
- ResolveInfo ri = apps.get(i);
- ComponentName componentName = new ComponentName(
- ri.activityInfo.packageName, ri.activityInfo.name);
- mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId)));
- }
-
- savePrefs();
- }
-
- /** Returns a pref prefixed with the serial number of the current user. */
- private String userPrefixed(String pref) {
- return Long.toString(mCurrentUserSerialNumber) + USER_SEPARATOR + pref;
- }
-
- /** Returns the pref name for the app at a given index. */
- private String prefNameForApp(int index) {
- return userPrefixed(APP_PREF_PREFIX + Integer.toString(index));
- }
-
- /** Returns the pref name for the app's user at a given index. */
- private String prefUserForApp(int index) {
- return userPrefixed(APP_USER_PREFIX + Integer.toString(index));
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index beeee0b..7395a33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -43,8 +43,6 @@
private static final String RECENT = "recent";
private static final String NAVSPACE = "space";
- private static final String APP_SHELF = "app_shelf";
-
public static final String GRAVITY_SEPARATOR = ";";
public static final String BUTTON_SEPARATOR = ",";
@@ -141,19 +139,13 @@
inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group),
(ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);
- if (center.length == 1 && APP_SHELF.equals(center[0])) {
- inflateShelf((LinearLayout) mRot0.findViewById(R.id.ends_group),
- (LinearLayout) mRot0.findViewById(R.id.ends_group_lightsout), false);
- inflateShelf((LinearLayout) mRot90.findViewById(R.id.ends_group),
- (LinearLayout) mRot90.findViewById(R.id.ends_group_lightsout), true);
- } else {
- inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group),
- (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false);
- inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group),
- (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true);
- addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));
- addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));
- }
+ inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group),
+ (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false);
+ inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group),
+ (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true);
+
+ addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));
+ addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));
inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group),
(ViewGroup) mRot0.findViewById(R.id.ends_group_lightsout), false);
@@ -161,14 +153,6 @@
(ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);
}
- private void inflateShelf(LinearLayout layout, LinearLayout lightsOut, boolean landscape) {
- View v = (landscape ? mLandscapeInflater : mLayoutInflater)
- .inflate(R.layout.apps_bar, layout, false);
- layout.addView(v);
- addToDispatchers(v);
- copyToLightsout(v, lightsOut);
- }
-
private void addGravitySpacer(LinearLayout layout) {
layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 839b579..d86629f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -263,11 +263,6 @@
return mButtonDisatchers.get(R.id.ime_switcher);
}
- @Nullable
- public View getAppShelf() {
- return getCurrentView().findViewById(R.id.app_shelf);
- }
-
private void getCarModeIcons(Context ctx) {
mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode);
mBackLandCarModeIcon = mBackCarModeIcon;
@@ -395,12 +390,6 @@
getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
-
- // The app shelf, if it exists, follows the visibility of the home button.
- View appShelf = getAppShelf();
- if (appShelf != null) {
- appShelf.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
- }
}
private boolean inLockTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index ba20679..8e89efd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,11 +39,11 @@
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
@@ -57,6 +57,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
@@ -104,7 +105,6 @@
private View mQsNavbarScrim;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationStackScrollLayout mNotificationStackScroller;
- private int mNotificationTopPadding;
private boolean mAnimateNextTopPaddingChange;
private int mTrackingPointer;
@@ -151,9 +151,6 @@
private int mUnlockMoveDistance;
private float mEmptyDragAmount;
- private Interpolator mFastOutSlowInInterpolator;
- private Interpolator mFastOutLinearInterpolator;
- private Interpolator mDozeAnimationInterpolator;
private ObjectAnimator mClockAnimator;
private int mClockAnimationTarget = -1;
private int mTopPaddingAdjustment;
@@ -253,12 +250,6 @@
mNotificationStackScroller.setOverscrollTopChangedListener(this);
mNotificationStackScroller.setOnEmptySpaceClickListener(this);
mNotificationStackScroller.setScrollView(mScrollView);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
- android.R.interpolator.fast_out_slow_in);
- mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
- android.R.interpolator.fast_out_linear_in);
- mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(getContext(),
- android.R.interpolator.linear_out_slow_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
@@ -281,8 +272,6 @@
@Override
protected void loadDimens() {
super.loadDimens();
- mNotificationTopPadding = getResources().getDimensionPixelSize(
- R.dimen.notifications_top_padding);
mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
mStatusBarMinHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
@@ -371,7 +360,7 @@
}
mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
mQsSizeChangeAnimator.setDuration(300);
- mQsSizeChangeAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -400,8 +389,8 @@
if (mStatusBarState != StatusBarState.KEYGUARD) {
int bottom = mHeader.getCollapsedHeight();
stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
- ? bottom + mQsPeekHeight + mNotificationTopPadding
- : mKeyguardStatusBar.getHeight() + mNotificationTopPadding;
+ ? bottom + mQsPeekHeight
+ : mKeyguardStatusBar.getHeight();
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
@@ -433,8 +422,8 @@
public int computeMaxKeyguardNotifications(int maximum) {
float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(),
mKeyguardStatusView.getHeight());
- int keyguardPadding = getResources().getDimensionPixelSize(
- R.dimen.notification_padding_dimmed);
+ int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
+ R.dimen.notification_divider_height));
final int overflowheight = getResources().getDimensionPixelSize(
R.dimen.notification_summary_height);
float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize();
@@ -446,7 +435,7 @@
if (!(child instanceof ExpandableNotificationRow)) {
continue;
}
- availableSpace -= child.getMinHeight() + keyguardPadding;
+ availableSpace -= child.getMinHeight() + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
} else {
@@ -471,7 +460,7 @@
}
mClockAnimator = ObjectAnimator
.ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget);
- mClockAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mClockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -1107,7 +1096,7 @@
.translationY(0f)
.setStartDelay(delay)
.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
mQsContainer.setY(-mQsContainer.getHeight());
mQsContainerAnimator = ObjectAnimator.ofFloat(mQsContainer, View.TRANSLATION_Y,
@@ -1116,7 +1105,7 @@
- mQsContainer.getTop());
mQsContainerAnimator.setStartDelay(delay);
mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
- mQsContainerAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mQsContainerAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
mQsContainerAnimator.start();
mQsContainer.addOnLayoutChangeListener(mQsContainerAnimatorUpdater);
@@ -1138,7 +1127,7 @@
mHeader.animate().y(-mHeader.getHeight())
.setStartDelay(0)
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -1152,7 +1141,7 @@
.y(-mQsContainer.getHeight())
.setStartDelay(0)
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
}
@@ -1174,7 +1163,7 @@
anim.setDuration(mStatusBar.isKeyguardFadingAway()
? mStatusBar.getKeyguardFadingAwayDuration() / 2
: StackStateAnimator.ANIMATION_DURATION_STANDARD);
- anim.setInterpolator(mDozeAnimationInterpolator);
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -1199,7 +1188,7 @@
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.addUpdateListener(mStatusBarAnimateAlphaListener);
anim.setDuration(duration);
- anim.setInterpolator(mDozeAnimationInterpolator);
+ anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
anim.start();
}
@@ -1218,7 +1207,7 @@
.alpha(0f)
.setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
.setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
.start();
} else if (statusBarState == StatusBarState.KEYGUARD
@@ -1245,7 +1234,7 @@
.alpha(0f)
.setStartDelay(0)
.setDuration(160)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
if (keyguardFadingAway) {
mKeyguardStatusView.animate()
@@ -1263,7 +1252,7 @@
.alpha(1f)
.setStartDelay(0)
.setDuration(320)
- .setInterpolator(PhoneStatusBar.ALPHA_IN)
+ .setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
} else if (statusBarState == StatusBarState.KEYGUARD) {
mKeyguardStatusView.animate().cancel();
@@ -1380,8 +1369,7 @@
// on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to
// take the maximum and linearly interpolate with the panel expansion for a nice motion.
int maxNotifications = mClockPositionResult.stackScrollerPadding
- - mClockPositionResult.stackScrollerPaddingAdjustment
- - mNotificationTopPadding;
+ - mClockPositionResult.stackScrollerPaddingAdjustment;
int maxQs = getTempQsMaxExpansion();
int max = mStatusBarState == StatusBarState.KEYGUARD
? Math.max(maxNotifications, maxQs)
@@ -1395,7 +1383,7 @@
// We can only do the smoother transition on Keyguard when we also are not collapsing
// from a scrolled quick settings.
return interpolate(getQsExpansionFraction(),
- mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding,
+ mNotificationStackScroller.getIntrinsicPadding(),
mQsMaxExpansionHeight);
} else {
return mQsExpansionHeight;
@@ -1625,15 +1613,13 @@
maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
}
float totalHeight = Math.max(
- maxQsHeight + mNotificationStackScroller.getNotificationTopPadding(),
- mStatusBarState == StatusBarState.KEYGUARD
+ maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD
? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
: 0)
+ notificationHeight;
if (totalHeight > mNotificationStackScroller.getHeight()) {
float fullyCollapsedHeight = maxQsHeight
+ mNotificationStackScroller.getMinStackHeight()
- + mNotificationStackScroller.getNotificationTopPadding()
- getScrollViewScrollY();
totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
}
@@ -1680,14 +1666,14 @@
boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
if (active && !mUnlockIconActive && mTracking) {
- lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null);
+ lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null);
lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
- mFastOutLinearInterpolator);
+ Interpolators.FAST_OUT_LINEAR_IN);
} else if (!active && mUnlockIconActive && mTracking) {
lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
- 150, mFastOutLinearInterpolator, null);
+ 150, Interpolators.FAST_OUT_LINEAR_IN, null);
lockIcon.setImageScale(1.0f, true, 150,
- mFastOutLinearInterpolator);
+ Interpolators.FAST_OUT_LINEAR_IN);
}
mUnlockIconActive = active;
}
@@ -1727,7 +1713,7 @@
float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) {
translation = mNotificationStackScroller.getTopPadding() + stackTranslation
- - mNotificationTopPadding - mQsMinExpansionHeight;
+ - mQsMinExpansionHeight;
}
return Math.min(0, translation);
}
@@ -1892,8 +1878,8 @@
if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
- lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null);
- lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator);
+ lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null);
+ lockIcon.setImageScale(2.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN);
}
}
@@ -2033,12 +2019,12 @@
*/
private void startHighlightIconAnimation(final KeyguardAffordanceView icon) {
icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
- mFastOutSlowInInterpolator, new Runnable() {
+ Interpolators.FAST_OUT_SLOW_IN, new Runnable() {
@Override
public void run() {
icon.setImageAlpha(icon.getRestingAlpha(),
true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
- mFastOutSlowInInterpolator, null);
+ Interpolators.FAST_OUT_SLOW_IN, null);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index f0b7894..f036d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -29,7 +29,6 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -39,6 +38,7 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -100,8 +100,6 @@
private float mInitialTouchX;
private boolean mTouchDisabled;
- private Interpolator mLinearOutSlowInInterpolator;
- private Interpolator mFastOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
protected KeyguardBottomAreaView mKeyguardBottomArea;
@@ -161,7 +159,7 @@
}
mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
.setDuration(250);
- mPeekAnimator.setInterpolator(mLinearOutSlowInInterpolator);
+ mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
mPeekAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -187,10 +185,6 @@
public PanelView(Context context, AttributeSet attrs) {
super(context, attrs);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
- mFastOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
- mLinearOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
mFalsingManager = FalsingManager.getInstance(context);
}
@@ -951,7 +945,7 @@
float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
ValueAnimator animator = createHeightAnimator(target);
animator.setDuration(250);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -975,7 +969,7 @@
mKeyguardBottomArea.getIndicationView().animate()
.translationY(-mHintDistance)
.setDuration(250)
- .setInterpolator(mFastOutSlowInInterpolator)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e2a6184..354d704 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -87,13 +87,11 @@
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
@@ -124,6 +122,7 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
@@ -131,7 +130,6 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
-import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -438,10 +436,8 @@
private boolean mDozingRequested;
protected boolean mScrimSrcModeEnabled;
- private Interpolator mLinearInterpolator = new LinearInterpolator();
- private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
- public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN;
+ public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT;
private BackDropView mBackdrop;
private ImageView mBackdropFront, mBackdropBack;
@@ -744,9 +740,7 @@
mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
- SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_speed_bump, mStackScroller, false);
- mStackScroller.setSpeedBumpView(speedBump);
+
mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, mStackScroller, false);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
@@ -1256,7 +1250,7 @@
}
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (mNotificationData.shouldSuppressScreenOn(notification.getKey())) {
+ if (shouldSupressFullScreenIntent(notification.getKey())) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
}
@@ -1283,6 +1277,14 @@
setAreThereNotifications();
}
+ private boolean shouldSupressFullScreenIntent(String key) {
+ if (mPowerManager.isInteractive()) {
+ return mNotificationData.shouldSuppressPeek(key);
+ } else {
+ return mNotificationData.shouldSuppressScreenOn(key);
+ }
+ }
+
@Override
protected void updateNotificationRanking(RankingMap ranking) {
mNotificationData.updateRanking(ranking);
@@ -1841,7 +1843,7 @@
// if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
// libhwui.
.alpha(0.002f)
- .setInterpolator(mBackdropInterpolator)
+ .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.setDuration(300)
.setStartDelay(0)
.withEndAction(new Runnable() {
@@ -1860,7 +1862,7 @@
// behind.
.setDuration(mKeyguardFadingAwayDuration / 2)
.setStartDelay(mKeyguardFadingAwayDelay)
- .setInterpolator(mLinearInterpolator)
+ .setInterpolator(Interpolators.LINEAR)
.start();
}
}
@@ -3740,7 +3742,7 @@
mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
updateDozingState();
updatePublicMode();
- updateStackScrollerState(goingToFullShade);
+ updateStackScrollerState(goingToFullShade, fromShadeLocked);
updateNotifications();
checkBarModes();
updateMediaMetaData(false);
@@ -3761,11 +3763,11 @@
!= FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
}
- public void updateStackScrollerState(boolean goingToFullShade) {
+ public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
if (mStackScroller == null) return;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
- mStackScroller.setDimmed(onKeyguard, false /* animate */);
+ mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
mStackScroller.setExpandingEnabled(!onKeyguard);
ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
mStackScroller.setActivatedChild(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7e27856..9c2159be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -38,6 +38,7 @@
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
@@ -346,6 +347,7 @@
else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
else if (tileSpec.equals("user")) return new UserTile(this);
else if (tileSpec.equals("battery")) return new BatteryTile(this);
+ else if (tileSpec.equals("saver")) return new DataSaverTile(this);
else if (tileSpec.equals(ColorMatrixTile.COLOR_MATRIX_SPEC))
return new ColorMatrixTile(this);
// Intent tiles.
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/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 403199a..f310c2c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -22,6 +22,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
@@ -497,4 +498,16 @@
public void dontAnimateBouncerChangesUntilNextFrame() {
mDontAnimateBouncerChanges = true;
}
+
+ public void setExcludedBackgroundArea(Rect area) {
+ mScrimBehind.setExcludedArea(area);
+ }
+
+ public int getScrimBehindColor() {
+ return mScrimBehind.getScrimColorWithAlpha();
+ }
+
+ public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
+ mScrimBehind.setChangeRunnable(changeRunnable);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 512af1b..7247b57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -26,7 +26,9 @@
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+
import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.statusbar.Interpolators;
public class SettingsButton extends AlphaOptimizedImageButton {
@@ -157,8 +159,7 @@
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mUpToSpeed = true;
mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360);
- mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear));
+ mAnimator.setInterpolator(Interpolators.LINEAR);
mAnimator.setDuration(FULL_SPEED_LENGTH);
mAnimator.setRepeatCount(Animation.INFINITE);
mAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 64fb066..c6537e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -32,15 +32,15 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -63,8 +63,6 @@
private Context mContext;
private PhoneStatusBar mPhoneStatusBar;
- private Interpolator mLinearOutSlowIn;
- private Interpolator mFastOutSlowIn;
private DemoStatusIcons mDemoStatusIcons;
private LinearLayout mSystemIconArea;
@@ -129,10 +127,6 @@
maybeScaleBatteryMeterView(context);
mClock = (TextView) statusBar.findViewById(R.id.clock);
- mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in);
mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
mHandler = new Handler();
@@ -348,7 +342,7 @@
.alpha(0f)
.setDuration(160)
.setStartDelay(0)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -370,7 +364,7 @@
v.animate()
.alpha(1f)
.setDuration(320)
- .setInterpolator(PhoneStatusBar.ALPHA_IN)
+ .setInterpolator(Interpolators.ALPHA_IN)
.setStartDelay(50)
// We need to clean up any pending end action from animateHide if we call
@@ -382,7 +376,7 @@
if (mPhoneStatusBar.isKeyguardFadingAway()) {
v.animate()
.setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration())
- .setInterpolator(mLinearOutSlowIn)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay())
.start();
}
@@ -419,7 +413,7 @@
});
mTintAnimator.setDuration(duration);
mTintAnimator.setStartDelay(delay);
- mTintAnimator.setInterpolator(mFastOutSlowIn);
+ mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mTintAnimator.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
index 56c1e10..1aae496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import com.android.systemui.R;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -31,10 +29,11 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+
public class TrustDrawable extends Drawable {
private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
@@ -69,10 +68,6 @@
private final Animator mVisibleAnimator;
- private final Interpolator mLinearOutSlowInInterpolator;
- private final Interpolator mFastOutSlowInInterpolator;
- private final Interpolator mAccelerateDecelerateInterpolator;
-
public TrustDrawable(Context context) {
Resources r = context.getResources();
mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
@@ -83,12 +78,6 @@
mCurInnerRadius = mInnerRadiusEnter;
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.linear_out_slow_in);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_slow_in);
- mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
-
mVisibleAnimator = makeVisibleAnimator();
mPaint = new Paint();
@@ -212,19 +201,19 @@
private Animator makeVisibleAnimator() {
return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
- mAccelerateDecelerateInterpolator,
+ Interpolators.ACCELERATE_DECELERATE,
true /* repeating */, false /* stateUpdateListener */);
}
private Animator makeEnterAnimator(float radius, int alpha) {
return makeAnimators(radius, mInnerRadiusVisibleMax,
- alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator,
+ alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, Interpolators.LINEAR_OUT_SLOW_IN,
false /* repeating */, true /* stateUpdateListener */);
}
private Animator makeExitAnimator(float radius, int alpha) {
return makeAnimators(radius, mInnerRadiusExit,
- alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator,
+ alpha, 0, EXIT_DURATION, Interpolators.FAST_OUT_SLOW_IN,
false /* repeating */, true /* stateUpdateListener */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 4ae0321..59d54ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -16,15 +16,15 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+
/**
* Controls showing and hiding of the brightness mirror.
*/
@@ -46,13 +46,13 @@
public void showMirror() {
mBrightnessMirror.setVisibility(View.VISIBLE);
- mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT);
+ mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT);
outAnimation(mNotificationPanel.animate())
.withLayer();
}
public void hideMirror() {
- mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN);
+ mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, Interpolators.ALPHA_IN);
inAnimation(mNotificationPanel.animate())
.withLayer()
.withEndAction(new Runnable() {
@@ -66,12 +66,12 @@
private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
return a.alpha(0.0f)
.setDuration(TRANSITION_DURATION_OUT)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT);
+ .setInterpolator(Interpolators.ALPHA_OUT);
}
private ViewPropertyAnimator inAnimation(ViewPropertyAnimator a) {
return a.alpha(1.0f)
.setDuration(TRANSITION_DURATION_IN)
- .setInterpolator(PhoneStatusBar.ALPHA_IN);
+ .setInterpolator(Interpolators.ALPHA_IN);
}
@@ -103,10 +103,5 @@
lp.gravity = mBrightnessMirror.getResources().getInteger(
R.integer.notification_panel_layout_gravity);
mBrightnessMirror.setLayoutParams(lp);
-
- int padding = mBrightnessMirror.getResources().getDimensionPixelSize(
- R.dimen.notification_side_padding);
- mBrightnessMirror.setPadding(padding, mBrightnessMirror.getPaddingTop(),
- padding, mBrightnessMirror.getPaddingBottom());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
new file mode 100644
index 0000000..186e8b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -0,0 +1,89 @@
+/*
+ * 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.statusbar.policy;
+
+import android.content.Context;
+import android.net.INetworkPolicyListener;
+import android.net.NetworkPolicyManager;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+
+public class DataSaverController {
+
+ private final Handler mHandler = new Handler();
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
+ private final NetworkPolicyManager mPolicyManager;
+
+ public DataSaverController(Context context) {
+ mPolicyManager = NetworkPolicyManager.from(context);
+ }
+
+ private void handleRestrictBackgroundChanged(boolean isDataSaving) {
+ final int N = mListeners.size();
+ for (int i = 0; i < N; i++) {
+ mListeners.get(i).onDataSaverChanged(isDataSaving);
+ }
+ }
+
+ public void addListener(Listener listener) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mPolicyManager.registerListener(mPolicyListener);
+ }
+ listener.onDataSaverChanged(isDataSaverEnabled());
+ }
+
+ public void remListener(Listener listener) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mPolicyManager.unregisterListener(mPolicyListener);
+ }
+ }
+
+ public boolean isDataSaverEnabled() {
+ return mPolicyManager.getRestrictBackground();
+ }
+
+ public void setDataSaverEnabled(boolean enabled) {
+ mPolicyManager.setRestrictBackground(enabled);
+ }
+
+ private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+ @Override
+ public void onUidRulesChanged(int i, int i1) throws RemoteException {
+ }
+
+ @Override
+ public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleRestrictBackgroundChanged(isDataSaving);
+ }
+ });
+ }
+ };
+
+ public interface Listener {
+ void onDataSaverChanged(boolean isDataSaving);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index dd7bd56..f065522 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -84,7 +84,6 @@
private final View mStatusBarWindowView;
private final int mStatusBarHeight;
- private final int mNotificationsTopPadding;
private final Context mContext;
private final NotificationGroupManager mGroupManager;
private PhoneStatusBar mBar;
@@ -138,8 +137,6 @@
mGroupManager = groupManager;
mStatusBarHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
- mNotificationsTopPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.notifications_top_padding);
}
private void updateTouchableRegionListener() {
@@ -399,7 +396,7 @@
}
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY + mNotificationsTopPadding);
+ info.touchableRegion.set(minX, 0, maxX, maxY);
} else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
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/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 3f63b5f..d739d6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -32,7 +32,7 @@
import android.view.animation.Interpolator;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.Interpolators;
import java.util.ArrayList;
import java.util.HashSet;
@@ -59,7 +59,6 @@
private int mMaxWidth;
private final Interpolator mInterpolator = new LogInterpolator();
- private final Interpolator mAlphaExitInterpolator = PhoneStatusBar.ALPHA_OUT;
private boolean mSupportHardware;
private final View mTargetView;
@@ -225,7 +224,7 @@
private void exitSoftware() {
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
- alphaAnimator.setInterpolator(mAlphaExitInterpolator);
+ alphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
alphaAnimator.addListener(mAnimatorListener);
alphaAnimator.start();
@@ -331,7 +330,7 @@
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
RenderNodeAnimator.PAINT_ALPHA, 0);
opacityAnim.setDuration(ANIMATION_DURATION_FADE);
- opacityAnim.setInterpolator(mAlphaExitInterpolator);
+ opacityAnim.setInterpolator(Interpolators.ALPHA_OUT);
opacityAnim.addListener(mAnimatorListener);
opacityAnim.setTarget(mTargetView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 5cf6156..0959b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.DataSetObserver;
+import android.graphics.Interpolator;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -33,9 +34,9 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.systemui.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
/**
* Manages the user switcher on the Keyguard.
@@ -73,8 +74,7 @@
mAdapter.registerDataSetObserver(mDataSetObserver);
mUserSwitcherController = userSwitcherController;
mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
- AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_slow_in));
+ Interpolators.FAST_OUT_SLOW_IN);
mUserSwitcherContainer.setKeyguardUserSwitcher(this);
} else {
mUserSwitcherContainer = null;
@@ -158,7 +158,7 @@
mAnimating = true;
mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
mBgAnimator.setDuration(400);
- mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+ mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
mBgAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -174,7 +174,7 @@
mUserSwitcher.animate()
.alpha(0f)
.setDuration(300)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+ .setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index b2bcde3..755a5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -33,6 +33,7 @@
void onUserSwitched(int newUserId);
AccessPointController getAccessPointController();
DataUsageController getMobileDataController();
+ DataSaverController getDataSaverController();
public interface SignalCallback {
void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 3385c82..107a904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -78,6 +78,7 @@
private final SubscriptionManager mSubscriptionManager;
private final boolean mHasMobileDataFeature;
private final SubscriptionDefaults mSubDefaults;
+ private final DataSaverController mDataSaverController;
private Config mConfig;
// Subcontrollers.
@@ -156,6 +157,7 @@
mConfig = config;
mReceiverHandler = new Handler(bgLooper);
mCallbackHandler = callbackHandler;
+ mDataSaverController = new DataSaverController(context);
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
@@ -189,6 +191,10 @@
updateAirplaneMode(true /* force callback */);
}
+ public DataSaverController getDataSaverController() {
+ return mDataSaverController;
+ }
+
private void registerListeners() {
for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
mobileSignalController.registerListener();
@@ -811,11 +817,11 @@
public static class SubscriptionDefaults {
public int getDefaultVoiceSubId() {
- return SubscriptionManager.getDefaultVoiceSubId();
+ return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
public int getDefaultDataSubId() {
- return SubscriptionManager.getDefaultDataSubId();
+ return SubscriptionManager.getDefaultDataSubscriptionId();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 56f6564..561b18a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -25,12 +25,12 @@
boolean animateAlpha;
boolean animateY;
boolean animateZ;
- boolean animateScale;
boolean animateHeight;
boolean animateTopInset;
boolean animateDimmed;
boolean animateDark;
boolean animateHideSensitive;
+ public boolean animateShadowAlpha;
boolean hasDelays;
boolean hasGoToFullShadeEvent;
boolean hasDarkEvent;
@@ -57,11 +57,6 @@
return this;
}
- public AnimationFilter animateScale() {
- animateScale = true;
- return this;
- }
-
public AnimationFilter animateHeight() {
animateHeight = true;
return this;
@@ -87,6 +82,11 @@
return this;
}
+ public AnimationFilter animateShadowAlpha() {
+ animateShadowAlpha = true;
+ return this;
+ }
+
/**
* Combines multiple filters into {@code this} filter, using or as the operand .
*
@@ -118,12 +118,12 @@
animateAlpha |= filter.animateAlpha;
animateY |= filter.animateY;
animateZ |= filter.animateZ;
- animateScale |= filter.animateScale;
animateHeight |= filter.animateHeight;
animateTopInset |= filter.animateTopInset;
animateDimmed |= filter.animateDimmed;
animateDark |= filter.animateDark;
animateHideSensitive |= filter.animateHideSensitive;
+ animateShadowAlpha |= filter.animateShadowAlpha;
hasDelays |= filter.hasDelays;
}
@@ -131,8 +131,8 @@
animateAlpha = false;
animateY = false;
animateZ = false;
- animateScale = false;
animateHeight = false;
+ animateShadowAlpha = false;
animateTopInset = false;
animateDimmed = false;
animateDark = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index dc40fb0..409dac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -76,8 +76,8 @@
super(context, attrs, defStyleAttr, defStyleRes);
mChildPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_padding);
- mDividerHeight = getResources().getDimensionPixelSize(
- R.dimen.notification_children_divider_height);
+ mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
+ R.dimen.notification_divider_height));
mMaxNotificationHeight = getResources().getDimensionPixelSize(
R.dimen.notification_max_height);
mNotificationAppearDistance = getResources().getDimensionPixelSize(
@@ -324,7 +324,6 @@
childState.dark = parentState.dark;
childState.hideSensitive = parentState.hideSensitive;
childState.belowSpeedBump = parentState.belowSpeedBump;
- childState.scale = 1.0f;
childState.clipTopAmount = 0;
childState.topOverLap = 0;
boolean visible = i <= lastVisibleIndex;
@@ -384,30 +383,8 @@
* @param state the new state we animate to
*/
public void prepareExpansionChanged(StackScrollState state) {
- if (true) {
- // TODO: do something that makes sense
- return;
- }
- int childCount = mChildren.size();
- StackViewState sourceState = new StackViewState();
- ViewState dividerState = new ViewState();
- for (int i = 0; i < childCount; i++) {
- ExpandableNotificationRow child = mChildren.get(i);
- StackViewState viewState = state.getViewStateForView(child);
- sourceState.copyFrom(viewState);
- sourceState.alpha = 0;
- sourceState.yTranslation += mNotificationAppearDistance;
- state.applyState(child, sourceState);
-
- // layout the divider
- View divider = mDividers.get(i);
- dividerState.initFrom(divider);
- dividerState.yTranslation = viewState.yTranslation - mDividerHeight
- + mNotificationAppearDistance;
- dividerState.alpha = 0;
- state.applyViewState(divider, dividerState);
-
- }
+ // TODO: do something that makes sense, like placing the invisible views correctly
+ return;
}
public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 36b2810..d5b57ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -16,12 +16,22 @@
package com.android.systemui.statusbar.stack;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeAnimator;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@@ -32,6 +42,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.OverScroller;
import com.android.systemui.ExpandHelper;
@@ -43,9 +54,8 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.NotificationOverflowContainer;
-import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -64,6 +74,7 @@
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
+ public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
@@ -79,6 +90,7 @@
private SwipeHelper mSwipeHelper;
private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
+ private final Paint mBackgroundPaint = new Paint();
/**
* mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set
@@ -103,15 +115,13 @@
private float mInitialTouchX;
private float mInitialTouchY;
- private int mSidePaddings;
private Paint mDebugPaint;
private int mContentHeight;
private int mCollapsedSize;
private int mBottomStackSlowDownHeight;
private int mBottomStackPeekSize;
private int mPaddingBetweenElements;
- private int mPaddingBetweenElementsDimmed;
- private int mPaddingBetweenElementsNormal;
+ private int mIncreasedPaddingBetweenElements;
private int mTopPadding;
private int mCollapseSecondCardPadding;
@@ -163,7 +173,6 @@
private boolean mGoToFullShadeNeedsAnimation;
private boolean mIsExpanded = true;
private boolean mChildrenUpdateRequested;
- private SpeedBumpView mSpeedBumpView;
private boolean mIsExpansionChanging;
private boolean mPanelTracking;
private boolean mExpandingNotification;
@@ -183,7 +192,6 @@
*/
private float mMinTopOverScrollToEscape;
private int mIntrinsicPadding;
- private int mNotificationTopPadding;
private float mStackTranslation;
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
@@ -234,6 +242,45 @@
private NotificationOverflowContainer mOverflowContainer;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
private FalsingManager mFalsingManager;
+ private boolean mAnimationRunning;
+ private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater
+ = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // if it needs animation
+ if (!mNeedsAnimation && !mChildrenUpdateRequested) {
+ updateBackground();
+ }
+ return true;
+ }
+ };
+ private Rect mBackgroundBounds = new Rect();
+ private Rect mStartAnimationRect = new Rect();
+ private Rect mEndAnimationRect = new Rect();
+ private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
+ private boolean mAnimateNextBackgroundBottom;
+ private boolean mAnimateNextBackgroundTop;
+ private ObjectAnimator mBottomAnimator = null;
+ private ObjectAnimator mTopAnimator = null;
+ private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
+ private ActivatableNotificationView mLastVisibleBackgroundChild = null;
+ private int mBgColor;
+ private float mDimAmount;
+ private ValueAnimator mDimAnimator;
+ private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDimAnimator = null;
+ }
+ };
+ private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
+ = new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setDimAmount((Float) animation.getAnimatedValue());
+ }
+ };
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -250,6 +297,7 @@
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mBgColor = context.getColor(R.color.notification_shade_background_color);
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this,
@@ -261,18 +309,20 @@
mSwipeHelper.setLongPressListener(mLongPressListener);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
initView(context);
+ setWillNotDraw(false);
if (DEBUG) {
- setWillNotDraw(false);
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
mDebugPaint.setStrokeWidth(2);
mDebugPaint.setStyle(Paint.Style.STROKE);
}
mFalsingManager = FalsingManager.getInstance(context);
+ mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
}
@Override
protected void onDraw(Canvas canvas) {
+ canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
if (DEBUG) {
int y = mTopPadding;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -288,6 +338,20 @@
}
}
+ private void updateBackgroundDimming() {
+ float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+ // We need to manually blend in the background color
+ int scrimColor = mScrimController.getScrimBehindColor();
+ // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc
+ float alphaInv = 1 - alpha;
+ int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)),
+ (int) (Color.red(mBgColor) + alphaInv * Color.red(scrimColor)),
+ (int) (Color.green(mBgColor) + alphaInv * Color.green(scrimColor)),
+ (int) (Color.blue(mBgColor) + alphaInv * Color.blue(scrimColor)));
+ mBackgroundPaint.setColor(color);
+ invalidate();
+ }
+
private void initView(Context context) {
mScroller = new OverScroller(getContext());
setFocusable(true);
@@ -298,36 +362,23 @@
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mOverflingDistance = configuration.getScaledOverflingDistance();
-
- mSidePaddings = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_side_padding);
mCollapsedSize = context.getResources()
.getDimensionPixelSize(R.dimen.notification_min_height);
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mStackScrollAlgorithm.initView(context);
- mPaddingBetweenElementsDimmed = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
- mPaddingBetweenElementsNormal = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding);
- updatePadding(mAmbientState.isDimmed());
+ mPaddingBetweenElements = Math.max(1, context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_divider_height));
+ mIncreasedPaddingBetweenElements = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+
+ mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
R.dimen.min_top_overscroll_to_qs);
- mNotificationTopPadding = getResources().getDimensionPixelSize(
- R.dimen.notifications_top_padding);
mCollapseSecondCardPadding = getResources().getDimensionPixelSize(
R.dimen.notification_collapse_second_card_padding);
}
- private void updatePadding(boolean dimmed) {
- mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed()
- ? mPaddingBetweenElementsDimmed
- : mPaddingBetweenElementsNormal;
- mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
- updateContentHeight();
- notifyHeightChangeListener(null);
- }
-
private void notifyHeightChangeListener(ExpandableView view) {
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
@@ -337,10 +388,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int mode = MeasureSpec.getMode(widthMeasureSpec);
- int size = MeasureSpec.getSize(widthMeasureSpec);
- int childMeasureSpec = MeasureSpec.makeMeasureSpec(size - 2 * mSidePaddings, mode);
- measureChildren(childMeasureSpec, heightMeasureSpec);
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
}
@Override
@@ -368,6 +416,7 @@
mRequestViewResizeAnimationOnLayout = false;
}
requestChildrenUpdate();
+ updateFirstAndLastBackgroundViews();
}
private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
@@ -378,18 +427,6 @@
}
public void updateSpeedBumpIndex(int newIndex) {
- int currentIndex = indexOfChild(mSpeedBumpView);
-
- // If we are currently layouted before the new speed bump index, we have to decrease it.
- boolean validIndex = newIndex > 0;
- if (newIndex > getChildCount() - 1) {
- validIndex = false;
- newIndex = -1;
- }
- if (validIndex && currentIndex != newIndex) {
- changeViewPosition(mSpeedBumpView, newIndex);
- }
- updateSpeedBump(validIndex);
mAmbientState.setSpeedBumpIndex(newIndex);
}
@@ -686,8 +723,7 @@
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() == GONE
- || slidingChild instanceof StackScrollerDecorView
- || slidingChild == mSpeedBumpView) {
+ || slidingChild instanceof StackScrollerDecorView) {
continue;
}
float childTop = slidingChild.getTranslationY();
@@ -714,8 +750,7 @@
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() == GONE
- || slidingChild instanceof StackScrollerDecorView
- || slidingChild == mSpeedBumpView) {
+ || slidingChild instanceof StackScrollerDecorView) {
continue;
}
float childTop = slidingChild.getTranslationY();
@@ -1412,22 +1447,251 @@
private void updateContentHeight() {
int height = 0;
+ boolean previousNeedsIncreasedPaddings = false;
for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != View.GONE) {
+ ExpandableView expandableView = (ExpandableView) getChildAt(i);
+ if (expandableView.getVisibility() != View.GONE) {
+ boolean needsIncreasedPaddings = expandableView.needsIncreasedPadding();
if (height != 0) {
- // add the padding before this element
- height += mPaddingBetweenElements;
+ int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
+ ? mIncreasedPaddingBetweenElements
+ : mPaddingBetweenElements;
+ height += padding;
}
- if (child instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) child;
- height += expandableView.getIntrinsicHeight();
- }
+ previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+ height += expandableView.getIntrinsicHeight();
}
}
mContentHeight = height + mTopPadding;
}
+ private void updateBackground() {
+ if (mAmbientState.isDark()) {
+ return;
+ }
+ updateBackgroundBounds();
+ if (!mCurrentBounds.equals(mBackgroundBounds)) {
+ if (mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom || areBoundsAnimating()) {
+ startBackgroundAnimation();
+ } else {
+ mCurrentBounds.set(mBackgroundBounds);
+ applyCurrentBackgroundBounds();
+ }
+ } else {
+ if (mBottomAnimator != null) {
+ mBottomAnimator.cancel();
+ }
+ if (mTopAnimator != null) {
+ mTopAnimator.cancel();
+ }
+ }
+ mAnimateNextBackgroundBottom = false;
+ mAnimateNextBackgroundTop = false;
+ }
+
+ private boolean areBoundsAnimating() {
+ return mBottomAnimator != null || mTopAnimator != null;
+ }
+
+ private void startBackgroundAnimation() {
+ startBottomAnimation();
+ startTopAnimation();
+ }
+
+ private void startTopAnimation() {
+ int previousEndValue = mEndAnimationRect.top;
+ int newEndValue = mBackgroundBounds.top;
+ ObjectAnimator previousAnimator = mTopAnimator;
+ if (previousAnimator != null && previousEndValue == newEndValue) {
+ return;
+ }
+ if (!mAnimateNextBackgroundTop) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ int previousStartValue = mStartAnimationRect.top;
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ values[0].setIntValues(previousStartValue, newEndValue);
+ mStartAnimationRect.top = previousStartValue;
+ mEndAnimationRect.top = newEndValue;
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ setBackgroundTop(newEndValue);
+ return;
+ }
+ }
+ if (previousAnimator != null) {
+ previousAnimator.cancel();
+ }
+ ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
+ mCurrentBounds.top, newEndValue);
+ Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ animator.setInterpolator(interpolator);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStartAnimationRect.top = -1;
+ mEndAnimationRect.top = -1;
+ mTopAnimator = null;
+ }
+ });
+ animator.start();
+ mStartAnimationRect.top = mCurrentBounds.top;
+ mEndAnimationRect.top = newEndValue;
+ mTopAnimator = animator;
+ }
+
+ private void startBottomAnimation() {
+ int previousStartValue = mStartAnimationRect.bottom;
+ int previousEndValue = mEndAnimationRect.bottom;
+ int newEndValue = mBackgroundBounds.bottom;
+ ObjectAnimator previousAnimator = mBottomAnimator;
+ if (previousAnimator != null && previousEndValue == newEndValue) {
+ return;
+ }
+ if (!mAnimateNextBackgroundBottom) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ values[0].setIntValues(previousStartValue, newEndValue);
+ mStartAnimationRect.bottom = previousStartValue;
+ mEndAnimationRect.bottom = newEndValue;
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ setBackgroundBottom(newEndValue);
+ return;
+ }
+ }
+ if (previousAnimator != null) {
+ previousAnimator.cancel();
+ }
+ ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
+ mCurrentBounds.bottom, newEndValue);
+ Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ animator.setInterpolator(interpolator);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStartAnimationRect.bottom = -1;
+ mEndAnimationRect.bottom = -1;
+ mBottomAnimator = null;
+ }
+ });
+ animator.start();
+ mStartAnimationRect.bottom = mCurrentBounds.bottom;
+ mEndAnimationRect.bottom = newEndValue;
+ mBottomAnimator = animator;
+ }
+
+ private void setBackgroundTop(int top) {
+ mCurrentBounds.top = top;
+ applyCurrentBackgroundBounds();
+ }
+
+ public void setBackgroundBottom(int bottom) {
+ mCurrentBounds.bottom = bottom;
+ applyCurrentBackgroundBounds();
+ }
+
+ private void applyCurrentBackgroundBounds() {
+ mScrimController.setExcludedBackgroundArea(mCurrentBounds);
+ invalidate();
+ }
+
+ /**
+ * Update the background bounds to the new desired bounds
+ */
+ private void updateBackgroundBounds() {
+ mBackgroundBounds.left = (int) getX();
+ mBackgroundBounds.right = (int) (getX() + getWidth());
+ if (!mIsExpanded) {
+ mBackgroundBounds.top = 0;
+ mBackgroundBounds.bottom = 0;
+ }
+ ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
+ int top = 0;
+ if (firstView != null) {
+ int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(firstView);
+ if (mAnimateNextBackgroundTop
+ || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
+ || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
+ // we're ending up at the same location as we are now, lets just skip the animation
+ top = finalTranslationY;
+ } else {
+ top = (int) firstView.getTranslationY();
+ }
+ }
+ ActivatableNotificationView lastView = mLastVisibleBackgroundChild;
+ int bottom = 0;
+ if (lastView != null) {
+ int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(lastView);
+ int finalHeight = StackStateAnimator.getFinalActualHeight(lastView);
+ int finalBottom = finalTranslationY + finalHeight;
+ finalBottom = Math.min(finalBottom, getHeight());
+ if (mAnimateNextBackgroundBottom
+ || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
+ || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
+ // we're ending up at the same location as we are now, lets just skip the animation
+ bottom = finalBottom;
+ } else {
+ bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
+ bottom = Math.min(bottom, getHeight());
+ }
+ }
+ mBackgroundBounds.top = top;
+ mBackgroundBounds.bottom = bottom;
+ }
+
+ private ActivatableNotificationView getFirstPinnedHeadsUp() {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (row.isPinned()) {
+ return row;
+ }
+ }
+ }
+ return null;
+ }
+
+ private ActivatableNotificationView getLastChildWithBackground() {
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ActivatableNotificationView) {
+ return (ActivatableNotificationView) child;
+ }
+ }
+ return null;
+ }
+
+ private ActivatableNotificationView getFirstChildWithBackground() {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ActivatableNotificationView) {
+ return (ActivatableNotificationView) child;
+ }
+ }
+ return null;
+ }
+
/**
* Fling the scroll view
*
@@ -1489,7 +1753,7 @@
*/
public void updateTopPadding(float qsHeight, int scrollY, boolean animate,
boolean ignoreIntrinsicPadding) {
- float start = qsHeight - scrollY + mNotificationTopPadding;
+ float start = qsHeight - scrollY;
float stackHeight = getHeight() - start;
int minStackHeight = getMinStackHeight();
if (stackHeight <= minStackHeight) {
@@ -1505,10 +1769,6 @@
setStackHeight(mLastSetStackHeight);
}
- public int getNotificationTopPadding() {
- return mNotificationTopPadding;
- }
-
public int getMinStackHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
final int firstChildMinHeight = firstChild != null ? firstChild.getMinHeight()
@@ -1655,7 +1915,7 @@
}
((ExpandableView) child).setOnHeightChangedListener(null);
mCurrentStackScrollState.removeViewStateForView(child);
- updateScrollStateForRemovedChild(child);
+ updateScrollStateForRemovedChild((ExpandableView) child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated && !mSwipedOutViews.contains(child)) {
// Add this view to an overlay in order to ensure that it will still be temporary
@@ -1754,9 +2014,12 @@
*
* @param removedChild the removed child
*/
- private void updateScrollStateForRemovedChild(View removedChild) {
+ private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
int startingPosition = getPositionInLinearLayout(removedChild);
- int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
+ int padding = removedChild.needsIncreasedPadding()
+ ? mIncreasedPaddingBetweenElements :
+ mPaddingBetweenElements;
+ int childHeight = getIntrinsicHeight(removedChild) + padding;
int endPosition = startingPosition + childHeight;
if (endPosition <= mOwnScrollY) {
// This child is fully scrolled of the top, so we have to deduct its height from the
@@ -1779,16 +2042,25 @@
private int getPositionInLinearLayout(View requestedChild) {
int position = 0;
+ boolean previousNeedsIncreasedPaddings = false;
for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ boolean notGone = child.getVisibility() != View.GONE;
+ if (notGone) {
+ boolean needsIncreasedPaddings = child.needsIncreasedPadding();
+ if (position != 0) {
+ int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
+ ? mIncreasedPaddingBetweenElements :
+ mPaddingBetweenElements;
+ position += padding;
+ }
+ previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+ }
if (child == requestedChild) {
return position;
}
- if (child.getVisibility() != View.GONE) {
+ if (notGone) {
position += getIntrinsicHeight(child);
- if (i < getChildCount()-1) {
- position += mPaddingBetweenElements;
- }
}
}
return 0;
@@ -1800,6 +2072,20 @@
onViewAddedInternal(child);
}
+ private void updateFirstAndLastBackgroundViews() {
+ ActivatableNotificationView firstChild = getFirstChildWithBackground();
+ ActivatableNotificationView lastChild = getLastChildWithBackground();
+ if (mAnimationsEnabled && mIsExpanded) {
+ mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+ mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
+ } else {
+ mAnimateNextBackgroundTop = false;
+ mAnimateNextBackgroundBottom = false;
+ }
+ mFirstVisibleBackgroundChild = firstChild;
+ mLastVisibleBackgroundChild = lastChild;
+ }
+
private void onViewAddedInternal(View child) {
updateHideSensitiveForChild(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
@@ -1910,7 +2196,9 @@
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
mGoToFullShadeDelay);
+ setAnimationRunning(true);
mAnimationEvents.clear();
+ updateBackground();
} else {
applyCurrentState();
}
@@ -2396,6 +2684,7 @@
}
public void onChildAnimationFinished() {
+ setAnimationRunning(false);
requestChildrenUpdate();
runAnimationFinishedRunnables();
clearViewOverlays();
@@ -2418,16 +2707,38 @@
* See {@link AmbientState#setDimmed}.
*/
public void setDimmed(boolean dimmed, boolean animate) {
- mStackScrollAlgorithm.setDimmed(dimmed);
mAmbientState.setDimmed(dimmed);
- updatePadding(dimmed);
if (animate && mAnimationsEnabled) {
mDimmedNeedsAnimation = true;
mNeedsAnimation = true;
+ animateDimmed(dimmed);
+ } else {
+ setDimAmount(dimmed ? 1.0f : 0.0f);
}
requestChildrenUpdate();
}
+ private void setDimAmount(float dimAmount) {
+ mDimAmount = dimAmount;
+ updateBackgroundDimming();
+ }
+
+ private void animateDimmed(boolean dimmed) {
+ if (mDimAnimator != null) {
+ mDimAnimator.cancel();
+ }
+ float target = dimmed ? 1.0f : 0.0f;
+ if (target == mDimAmount) {
+ return;
+ }
+ mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
+ mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
+ mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mDimAnimator.addListener(mDimEndListener);
+ mDimAnimator.addUpdateListener(mDimUpdateListener);
+ mDimAnimator.start();
+ }
+
public void setHideSensitive(boolean hideSensitive, boolean animate) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
@@ -2466,30 +2777,10 @@
mListener.onChildLocationsChanged(this);
}
runAnimationFinishedRunnables();
- }
-
- public void setSpeedBumpView(SpeedBumpView speedBumpView) {
- mSpeedBumpView = speedBumpView;
- addView(speedBumpView);
- }
-
- private void updateSpeedBump(boolean visible) {
- boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
- if (visible != notGoneBefore) {
- int newVisibility = visible ? VISIBLE : GONE;
- mSpeedBumpView.setVisibility(newVisibility);
- if (visible) {
- // Make invisible to ensure that the appear animation is played.
- mSpeedBumpView.setInvisible();
- } else {
- // TODO: This doesn't really work, because the view is already set to GONE above.
- generateRemoveAnimation(mSpeedBumpView);
- }
- }
+ updateBackground();
}
public void goToFullShade(long delay) {
- updateSpeedBump(true /* visibility */);
mDismissView.setInvisible();
mEmptyShadeView.setInvisible();
mGoToFullShadeNeedsAnimation = true;
@@ -2533,6 +2824,14 @@
mNeedsAnimation = true;
}
requestChildrenUpdate();
+ if (dark) {
+ setWillNotDraw(!DEBUG);
+ mScrimController.setExcludedBackgroundArea(null);
+ } else {
+ updateBackground();
+ setWillNotDraw(false);
+ // TODO: fade in background
+ }
}
private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
@@ -2731,7 +3030,7 @@
}
public int getDismissViewHeight() {
- int height = mDismissView.getHeight() + mPaddingBetweenElementsNormal;
+ int height = mDismissView.getHeight() + mPaddingBetweenElements;
// Hack: Accommodate for additional distance when we only have one notification and the
// dismiss all button.
@@ -2881,6 +3180,12 @@
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
+ mScrimController.setScrimBehindChangeRunnable(new Runnable() {
+ @Override
+ public void run() {
+ updateBackgroundDimming();
+ }
+ });
}
public void forceNoOverlappingRendering(boolean force) {
@@ -2892,6 +3197,17 @@
return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
}
+ public void setAnimationRunning(boolean animationRunning) {
+ if (animationRunning != mAnimationRunning) {
+ if (animationRunning) {
+ getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
+ } else {
+ getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
+ }
+ mAnimationRunning = animationRunning;
+ }
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
@@ -2937,7 +3253,7 @@
// ANIMATION_TYPE_ADD
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2946,7 +3262,7 @@
// ANIMATION_TYPE_REMOVE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2955,7 +3271,7 @@
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -2964,37 +3280,35 @@
// ANIMATION_TYPE_TOP_PADDING_CHANGED
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateDimmed()
- .animateScale()
.animateZ(),
// ANIMATION_TYPE_START_DRAG
new AnimationFilter()
- .animateAlpha(),
+ .animateShadowAlpha(),
// ANIMATION_TYPE_SNAP_BACK
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight(),
// ANIMATION_TYPE_ACTIVATED_CHILD
new AnimationFilter()
- .animateScale()
- .animateAlpha(),
+ .animateZ(),
// ANIMATION_TYPE_DIMMED
new AnimationFilter()
.animateY()
- .animateScale()
.animateDimmed(),
// ANIMATION_TYPE_CHANGE_POSITION
new AnimationFilter()
- .animateAlpha()
+ .animateAlpha() // maybe the children change positions
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3007,12 +3321,11 @@
// ANIMATION_TYPE_GO_TO_FULL_SHADE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
.animateDimmed()
- .animateScale()
.animateZ()
.hasDelays(),
@@ -3022,7 +3335,7 @@
// ANIMATION_TYPE_VIEW_RESIZE
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3031,6 +3344,7 @@
// ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
new AnimationFilter()
.animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3038,7 +3352,7 @@
// ANIMATION_TYPE_HEADS_UP_APPEAR
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3046,7 +3360,7 @@
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3054,7 +3368,7 @@
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3063,7 +3377,7 @@
// ANIMATION_TYPE_HEADS_UP_OTHER
new AnimationFilter()
- .animateAlpha()
+ .animateShadowAlpha()
.animateHeight()
.animateTopInset()
.animateY()
@@ -3072,8 +3386,8 @@
// ANIMATION_TYPE_EVERYTHING
new AnimationFilter()
.animateAlpha()
+ .animateShadowAlpha()
.animateDark()
- .animateScale()
.animateDimmed()
.animateHideSensitive()
.animateHeight()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 822012d..f6959f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.stack;
import android.content.Context;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -27,6 +26,7 @@
import com.android.systemui.statusbar.ExpandableView;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
/**
@@ -41,15 +41,13 @@
private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
private static final int MAX_ITEMS_IN_TOP_STACK = 3;
- public static final float DIMMED_SCALE = 0.98f;
-
private int mPaddingBetweenElements;
+ private int mIncreasedPaddingBetweenElements;
private int mCollapsedSize;
private int mTopStackPeekSize;
private int mBottomStackPeekSize;
private int mZDistanceBetweenElements;
private int mZBasicHeight;
- private int mRoundedRectCornerRadius;
private StackIndentationFunctor mTopStackIndentationFunctor;
private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -61,16 +59,11 @@
private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
private int mTopStackTotalSize;
- private int mPaddingBetweenElementsDimmed;
- private int mPaddingBetweenElementsNormal;
- private int mNotificationsTopPadding;
private int mBottomStackSlowDownLength;
private int mTopStackSlowDownLength;
private int mCollapseSecondCardPadding;
- private boolean mScaleDimmed;
private ExpandableView mFirstChild;
private int mFirstChildMinHeight;
- private boolean mDimmed;
public StackScrollAlgorithm(Context context) {
initView(context);
@@ -82,9 +75,6 @@
}
private void updatePadding() {
- mPaddingBetweenElements = mDimmed && mScaleDimmed
- ? mPaddingBetweenElementsDimmed
- : mPaddingBetweenElementsNormal;
mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
+ mTopStackPeekSize;
mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
@@ -104,35 +94,25 @@
}
private void initConstants(Context context) {
- mPaddingBetweenElementsDimmed = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
- mPaddingBetweenElementsNormal = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding);
- mNotificationsTopPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.notifications_top_padding);
+ mPaddingBetweenElements = Math.max(1, context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_divider_height));
+ mIncreasedPaddingBetweenElements = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
mCollapsedSize = context.getResources()
.getDimensionPixelSize(R.dimen.notification_min_height);
mTopStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.top_stack_peek_amount);
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
- mZDistanceBetweenElements = context.getResources()
- .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
+ mZDistanceBetweenElements = Math.max(1, context.getResources()
+ .getDimensionPixelSize(R.dimen.z_distance_between_notifications));
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
mBottomStackSlowDownLength = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
mTopStackSlowDownLength = context.getResources()
.getDimensionPixelSize(R.dimen.top_stack_slow_down_length);
- mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
- R.dimen.notification_material_rounded_rect_radius);
mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
R.dimen.notification_collapse_second_card_padding);
- mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
- >= DisplayMetrics.DENSITY_420;
- }
-
- public boolean shouldScaleDimmed() {
- return mScaleDimmed;
}
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -159,7 +139,7 @@
scrollY = Math.max(0, scrollY);
algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll);
- updateVisibleChildren(resultState, algorithmState);
+ initAlgorithmState(resultState, algorithmState);
// Phase 1:
findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
@@ -212,8 +192,8 @@
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackViewState state = resultState.getViewStateForView(child);
- float newYTranslation = state.yTranslation + state.height * (1f - state.scale) / 2f;
- float newHeight = state.height * state.scale;
+ float newYTranslation = state.yTranslation;
+ float newHeight = state.height;
// apply clipping and shadow
float newNotificationEnd = newYTranslation + newHeight;
@@ -225,16 +205,6 @@
} else {
clipHeight = newNotificationEnd - previousNotificationEnd;
clipHeight = Math.max(0.0f, clipHeight);
- if (clipHeight != 0.0f) {
-
- // In the unlocked shade we have to clip a little bit higher because of the rounded
- // corners of the notifications, but only if we are not fully overlapped by
- // the top card.
- float clippingCorrection = state.dimmed
- ? 0
- : mRoundedRectCornerRadius * state.scale;
- clipHeight += clippingCorrection;
- }
}
updateChildClippingAndBackground(state, newHeight, clipHeight,
@@ -252,7 +222,7 @@
} else {
previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
previousNotificationEnd = newNotificationEnd;
- previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale;
+ previousNotificationStart = newYTranslation + state.clipTopAmount;
}
}
}
@@ -276,13 +246,13 @@
float clipHeight, float backgroundHeight) {
if (realHeight > clipHeight) {
// Rather overlap than create a hole.
- state.topOverLap = (int) Math.floor((realHeight - clipHeight) / state.scale);
+ state.topOverLap = (int) Math.floor(realHeight - clipHeight);
} else {
state.topOverLap = 0;
}
if (realHeight > backgroundHeight) {
// Rather overlap than create a hole.
- state.clipTopAmount = (int) Math.floor((realHeight - backgroundHeight) / state.scale);
+ state.clipTopAmount = (int) Math.floor(realHeight - backgroundHeight);
} else {
state.clipTopAmount = 0;
}
@@ -305,9 +275,6 @@
childViewState.dark = dark;
childViewState.hideSensitive = hideSensitive;
boolean isActivatedChild = activatedChild == child;
- childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild
- ? 1.0f
- : DIMMED_SCALE;
if (dimmed && isActivatedChild) {
childViewState.zTranslation += 2.0f * mZDistanceBetweenElements;
}
@@ -331,7 +298,8 @@
nextChild);
// The child below the dragged one must be fully visible
if (ambientState.isShadeExpanded()) {
- viewState.alpha = 1;
+ viewState.shadowAlpha = 1;
+ viewState.hidden = false;
}
}
@@ -344,19 +312,28 @@
}
/**
- * Update the visible children on the state.
+ * Initialize the algorithm state like updating the visible children.
*/
- private void updateVisibleChildren(StackScrollState resultState,
+ private void initAlgorithmState(StackScrollState resultState,
StackScrollAlgorithmState state) {
ViewGroup hostView = resultState.getHostView();
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
+ state.increasedPaddingSet.clear();
int notGoneIndex = 0;
+ ExpandableView lastView = null;
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+ boolean needsIncreasedPadding = v.needsIncreasedPadding();
+ if (needsIncreasedPadding) {
+ state.increasedPaddingSet.add(v);
+ if (lastView != null) {
+ state.increasedPaddingSet.add(lastView);
+ }
+ }
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -374,6 +351,7 @@
}
}
}
+ lastView = v;
}
}
}
@@ -414,15 +392,17 @@
int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f
? algorithmState.lastTopStackIndex
: (int) algorithmState.itemsInTopStack;
+ int paddingAfterChild;
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = StackViewState.LOCATION_UNKNOWN;
+ paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
int minHeight = child.getMinHeight();
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
- + mPaddingBetweenElements;
+ + paddingAfterChild;
float scrollOffset = yPositionInScrollView - algorithmState.scrollY +
mFirstChildMinHeight;
@@ -437,20 +417,20 @@
// The y position after this element
float nextYPosition = currentYPosition + childHeight +
- mPaddingBetweenElements;
+ paddingAfterChild;
if (i <= algorithmState.lastTopStackIndex) {
// Case 1:
// We are in the top Stack
- updateStateForTopStackChild(algorithmState,
+ updateStateForTopStackChild(algorithmState, child,
numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
clampPositionToTopStackEnd(childViewState, childHeight);
// check if we are overlapping with the bottom stack
- if (childViewState.yTranslation + childHeight + mPaddingBetweenElements
+ if (childViewState.yTranslation + childHeight + paddingAfterChild
>= bottomStackStart && !mIsExpansionChanging && i != 0) {
// we just collapse this element slightly
- int newSize = (int) Math.max(bottomStackStart - mPaddingBetweenElements -
+ int newSize = (int) Math.max(bottomStackStart - paddingAfterChild -
childViewState.yTranslation, minHeight);
childViewState.height = newSize;
updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart,
@@ -466,7 +446,7 @@
// According to the regular scroll view we are fully translated out of the
// bottom of the screen so we are fully in the bottom stack
updateStateForChildFullyInBottomStack(algorithmState,
- bottomStackStart, childViewState, minHeight, ambientState);
+ bottomStackStart, childViewState, minHeight, ambientState, child);
} else {
// According to the regular scroll view we are currently translating out of /
// into the bottom of the screen
@@ -483,7 +463,8 @@
// The first card is always rendered.
if (i == 0) {
- childViewState.alpha = 1.0f;
+ childViewState.hidden = false;
+ childViewState.shadowAlpha = 1.0f;
childViewState.yTranslation = Math.max(
mFirstChildMinHeight - algorithmState.scrollY, 0);
if (childViewState.yTranslation + childViewState.height
@@ -497,7 +478,7 @@
if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
}
- currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+ currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
yPositionInScrollView = yPositionInScrollViewAfterElement;
childViewState.yTranslation += ambientState.getTopPadding()
@@ -506,6 +487,13 @@
updateHeadsUpStates(resultState, algorithmState, ambientState);
}
+ private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
+ ExpandableView child) {
+ return algorithmState.increasedPaddingSet.contains(child)
+ ? mIncreasedPaddingBetweenElements
+ : mPaddingBetweenElements;
+ }
+
private void updateHeadsUpStates(StackScrollState resultState,
StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
@@ -530,8 +518,7 @@
bottomPosition);
}
if (row.isPinned()) {
- childState.yTranslation = Math.max(childState.yTranslation,
- mNotificationsTopPadding);
+ childState.yTranslation = Math.max(childState.yTranslation, 0);
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
if (!isTopEntry) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -610,7 +597,7 @@
// This is the transitioning element on top of bottom stack, calculate how far we are in.
algorithmState.partialInBottom = 1.0f - (
(transitioningPositionStart - currentYPosition) / (childHeight +
- mPaddingBetweenElements));
+ getPaddingAfterChild(algorithmState, child)));
// the offset starting at the transitionPosition of the bottom stack
float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
@@ -618,12 +605,12 @@
int newHeight = childHeight;
if (childHeight > child.getMinHeight()) {
newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset -
- mPaddingBetweenElements - currentYPosition, childHeight),
+ getPaddingAfterChild(algorithmState, child) - currentYPosition, childHeight),
child.getMinHeight());
childViewState.height = newHeight;
}
childViewState.yTranslation = transitioningPositionStart + offset - newHeight
- - mPaddingBetweenElements;
+ - getPaddingAfterChild(algorithmState, child);
// We want at least to be at the end of the top stack when collapsing
clampPositionToTopStackEnd(childViewState, newHeight);
@@ -632,22 +619,23 @@
private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, StackViewState childViewState,
- int minHeight, AmbientState ambientState) {
+ int minHeight, AmbientState ambientState, ExpandableView child) {
float currentYPosition;
algorithmState.itemsInBottomStack += 1.0f;
if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
// We are visually entering the bottom stack
currentYPosition = transitioningPositionStart
+ mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
- - mPaddingBetweenElements;
+ - getPaddingAfterChild(algorithmState, child);
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
} else {
// we are fully inside the stack
if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
- childViewState.alpha = 0.0f;
+ childViewState.hidden = true;
+ childViewState.shadowAlpha = 0.0f;
} else if (algorithmState.itemsInBottomStack
> MAX_ITEMS_IN_BOTTOM_STACK + 1) {
- childViewState.alpha = 1.0f - algorithmState.partialInBottom;
+ childViewState.shadowAlpha = 1.0f - algorithmState.partialInBottom;
}
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
currentYPosition = ambientState.getInnerHeight();
@@ -658,7 +646,7 @@
}
private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
- int numberOfElementsCompletelyIn, int i, int childHeight,
+ ExpandableView child, int numberOfElementsCompletelyIn, int i, int childHeight,
StackViewState childViewState, float scrollOffset) {
@@ -669,10 +657,11 @@
if (paddedIndex >= 0) {
// We are currently visually entering the top stack
- float distanceToStack = (childHeight + mPaddingBetweenElements)
+ float distanceToStack = (childHeight + getPaddingAfterChild(algorithmState, child))
- algorithmState.scrolledPixelsTop;
if (i == algorithmState.lastTopStackIndex
- && distanceToStack > (mTopStackTotalSize + mPaddingBetweenElements)) {
+ && distanceToStack > (mTopStackTotalSize
+ + getPaddingAfterChild(algorithmState, child))) {
// Child is currently translating into stack but not yet inside slow down zone.
// Handle it like the regular scrollview.
@@ -682,7 +671,8 @@
float numItemsBefore;
if (i == algorithmState.lastTopStackIndex) {
numItemsBefore = 1.0f
- - (distanceToStack / (mTopStackTotalSize + mPaddingBetweenElements));
+ - (distanceToStack / (mTopStackTotalSize
+ + getPaddingAfterChild(algorithmState, child)));
} else {
numItemsBefore = algorithmState.itemsInTopStack - i;
}
@@ -694,10 +684,11 @@
childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING;
} else {
if (paddedIndex == -1) {
- childViewState.alpha = 1.0f - algorithmState.partialInTop;
+ childViewState.shadowAlpha = 1.0f - algorithmState.partialInTop;
} else {
// We are hidden behind the top card and faded out, so we can hide ourselves.
- childViewState.alpha = 0.0f;
+ childViewState.hidden = true;
+ childViewState.shadowAlpha = 0.0f;
}
childViewState.yTranslation = mFirstChildMinHeight - childHeight;
childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
@@ -718,15 +709,15 @@
// The y Position if the element would be in a regular scrollView
float yPositionInScrollView = 0.0f;
int childCount = algorithmState.visibleChildren.size();
-
// find the number of elements in the top stack.
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackViewState childViewState = resultState.getViewStateForView(child);
int childHeight = getMaxAllowedChildHeight(child);
+ int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
- + mPaddingBetweenElements;
+ + paddingAfterChild;
if (yPositionInScrollView < algorithmState.scrollY) {
if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) {
@@ -754,7 +745,7 @@
algorithmState.scrolledPixelsTop = algorithmState.scrollY
- yPositionInScrollView;
algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
- + mPaddingBetweenElements);
+ + paddingAfterChild);
// Our element can be expanded, so this can get negative
algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
@@ -763,7 +754,7 @@
if (i == 0) {
// If it is expanded we have to collapse it to a new size
float newSize = yPositionInScrollViewAfterElement
- - mPaddingBetweenElements
+ - paddingAfterChild
- algorithmState.scrollY + mFirstChildMinHeight;
newSize = Math.max(mFirstChildMinHeight, newSize);
algorithmState.itemsInTopStack = 1.0f;
@@ -917,11 +908,6 @@
}
}
- public void setDimmed(boolean dimmed) {
- mDimmed = dimmed;
- updatePadding();
- }
-
public void onReset(ExpandableView view) {
if (view.equals(mFirstChildWhileExpanding)) {
updateFirstChildMaxSizeToMaxHeight();
@@ -969,6 +955,11 @@
* The children from the host view which are not gone.
*/
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
+
+ /**
+ * The children from the host that need an increased padding after them.
+ */
+ public final HashSet<ExpandableView> increasedPaddingSet = new HashSet<>();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index e155d70..1fedc1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -25,7 +25,6 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.SpeedBumpView;
import java.util.HashMap;
import java.util.List;
@@ -83,8 +82,10 @@
// initialize with the default values of the view
viewState.height = view.getIntrinsicHeight();
viewState.gone = view.getVisibility() == View.GONE;
- viewState.alpha = 1;
+ viewState.alpha = 1f;
+ viewState.shadowAlpha = 1f;
viewState.notGoneIndex = -1;
+ viewState.hidden = false;
}
public StackViewState getViewStateForView(View requestedView) {
@@ -107,9 +108,7 @@
if (!applyState(child, state)) {
continue;
}
- if(child instanceof SpeedBumpView) {
- performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0);
- } else if (child instanceof DismissView) {
+ if (child instanceof DismissView) {
DismissView dismissView = (DismissView) child;
boolean visible = state.topOverLap < mClearAllTopPadding;
dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
@@ -146,6 +145,14 @@
view.setActualHeight(newHeight, false /* notifyListeners */);
}
+ float shadowAlpha = view.getShadowAlpha();
+ float newShadowAlpha = state.shadowAlpha;
+
+ // apply shadowAlpha
+ if (shadowAlpha != newShadowAlpha) {
+ view.setShadowAlpha(newShadowAlpha);
+ }
+
// apply dimming
view.setDimmed(state.dimmed, false /* animate */);
@@ -183,12 +190,10 @@
float yTranslation = view.getTranslationY();
float xTranslation = view.getTranslationX();
float zTranslation = view.getTranslationZ();
- float scale = view.getScaleX();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
- float newScale = state.scale;
- boolean becomesInvisible = newAlpha == 0.0f;
+ boolean becomesInvisible = newAlpha == 0.0f || state.hidden;
if (alpha != newAlpha && xTranslation == 0) {
// apply layer type
boolean becomesFullyVisible = newAlpha == 1.0f;
@@ -225,34 +230,5 @@
if (zTranslation != newZTranslation) {
view.setTranslationZ(newZTranslation);
}
-
- // apply scale
- if (scale != newScale) {
- view.setScaleX(newScale);
- view.setScaleY(newScale);
- }
}
-
- public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, StackViewState state,
- long delay) {
- View nextChild = getNextChildNotGone(i);
- if (nextChild != null) {
- float lineEnd = state.yTranslation + state.height / 2;
- StackViewState nextState = getViewStateForView(nextChild);
- boolean startIsAboveNext = nextState.yTranslation > lineEnd;
- speedBump.animateDivider(startIsAboveNext, delay, null /* onFinishedRunnable */);
- }
- }
-
- private View getNextChildNotGone(int childIndex) {
- int childCount = mHostView.getChildCount();
- for (int i = childIndex + 1; i < childCount; i++) {
- View child = mHostView.getChildAt(i);
- if (child.getVisibility() != View.GONE) {
- return child;
- }
- }
- return null;
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 9d5e072..e75e8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -22,13 +22,12 @@
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.view.View;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.SpeedBumpView;
+import com.android.systemui.statusbar.Interpolators;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
@@ -55,24 +54,23 @@
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
- private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
+ private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
- private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
+ private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
- private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag;
private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
+ private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
- private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mHeadsUpAppearInterpolator;
private final int mGoToFullShadeAppearingTranslation;
private final StackViewState mTmpState = new StackViewState();
@@ -99,8 +97,6 @@
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
- android.R.interpolator.fast_out_slow_in);
mGoToFullShadeAppearingTranslation =
hostLayout.getContext().getResources().getDimensionPixelSize(
R.dimen.go_to_full_shade_appearing_translation);
@@ -200,7 +196,6 @@
*/
public void startStackAnimations(final ExpandableView child, StackViewState viewState,
StackScrollState finalState, int i, long fixedDelay) {
- final float alpha = viewState.alpha;
boolean wasAdded = mNewAddChildren.contains(child);
long duration = mCurrentLength;
if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
@@ -212,14 +207,14 @@
}
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
- boolean scaleChanging = child.getScaleX() != viewState.scale;
- boolean alphaChanging = alpha != child.getAlpha();
+ boolean alphaChanging = viewState.alpha != child.getAlpha();
boolean heightChanging = viewState.height != child.getActualHeight();
+ boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha();
boolean darkChanging = viewState.dark != child.isDark();
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
boolean hasDelays = mAnimationFilter.hasDelays;
- boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
- alphaChanging || heightChanging || topInsetChanging || darkChanging;
+ boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging
+ || heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging;
long delay = 0;
if (fixedDelay != -1) {
delay = fixedDelay;
@@ -234,6 +229,11 @@
startHeightAnimation(child, viewState, duration, delay);
}
+ // start shadow alpha animation
+ if (shadowAlphaChanging) {
+ startShadowAlphaAnimation(child, viewState, duration, delay);
+ }
+
// start top inset animation
if (topInsetChanging) {
startInsetAnimation(child, viewState, duration, delay);
@@ -255,10 +255,7 @@
if (wasAdded) {
child.performAddAnimation(delay, mCurrentLength);
}
- if (child instanceof SpeedBumpView) {
- finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState,
- delay + duration);
- } else if (child instanceof ExpandableNotificationRow) {
+ if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
row.startChildAnimation(finalState, this, delay, duration);
}
@@ -275,13 +272,12 @@
public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
boolean wasVisible = child.getVisibility() == View.VISIBLE;
final float alpha = viewState.alpha;
- if (!wasVisible && alpha != 0 && !viewState.gone) {
+ if (!wasVisible && alpha != 0 && !viewState.gone && !viewState.hidden) {
child.setVisibility(View.VISIBLE);
}
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
- boolean scaleChanging = child.getScaleX() != viewState.scale;
- float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha();
+ float childAlpha = child.getAlpha();
boolean alphaChanging = viewState.alpha != childAlpha;
if (child instanceof ExpandableView) {
// We don't want views to change visibility when they are animating to GONE
@@ -298,11 +294,6 @@
startZTranslationAnimation(child, viewState, duration, delay);
}
- // start scale animation
- if (scaleChanging) {
- startScaleAnimation(child, viewState, duration);
- }
-
// start alpha animation
if (alphaChanging && child.getTranslationX() == 0) {
startAlphaAnimation(child, viewState, duration, delay);
@@ -384,6 +375,64 @@
return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
}
+ private void startShadowAlphaAnimation(final ExpandableView child,
+ StackViewState viewState, long duration, long delay) {
+ Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
+ Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
+ float newEndValue = viewState.shadowAlpha;
+ if (previousEndValue != null && previousEndValue == newEndValue) {
+ return;
+ }
+ ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
+ if (!mAnimationFilter.animateShadowAlpha) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ float relativeDiff = newEndValue - previousEndValue;
+ float newStartValue = previousStartValue + relativeDiff;
+ values[0].setFloatValues(newStartValue, newEndValue);
+ child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
+ child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ child.setShadowAlpha(newEndValue);
+ return;
+ }
+ }
+
+ ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ child.setShadowAlpha((float) animation.getAnimatedValue());
+ }
+ });
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
+ animator.setDuration(newDuration);
+ if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+ animator.setStartDelay(delay);
+ }
+ animator.addListener(getGlobalAnimationFinishedListener());
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
+ child.setTag(TAG_START_SHADOW_ALPHA, null);
+ child.setTag(TAG_END_SHADOW_ALPHA, null);
+ }
+ });
+ startAnimator(animator);
+ child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
+ child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
+ child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+ }
+
private void startHeightAnimation(final ExpandableView child,
StackViewState viewState, long duration, long delay) {
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
@@ -421,7 +470,7 @@
false /* notifyListeners */);
}
});
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
animator.setDuration(newDuration);
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -479,7 +528,7 @@
child.setClipTopAmount((int) animation.getAnimatedValue());
}
});
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
animator.setDuration(newDuration);
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -534,7 +583,7 @@
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
child.getAlpha(), newEndValue);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
// Handle layer type
child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
animator.addListener(new AnimatorListenerAdapter() {
@@ -605,7 +654,7 @@
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
child.getTranslationZ(), newEndValue);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
animator.setDuration(newDuration);
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -659,7 +708,7 @@
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
child.getTranslationY(), newEndValue);
Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
- mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator;
+ mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN;
animator.setInterpolator(interpolator);
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
animator.setDuration(newDuration);
@@ -683,60 +732,6 @@
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
}
- private void startScaleAnimation(final View child,
- ViewState viewState, long duration) {
- Float previousStartValue = getChildTag(child, TAG_START_SCALE);
- Float previousEndValue = getChildTag(child, TAG_END_SCALE);
- float newEndValue = viewState.scale;
- if (previousEndValue != null && previousEndValue == newEndValue) {
- return;
- }
- ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
- if (!mAnimationFilter.animateScale) {
- // just a local update was performed
- if (previousAnimator != null) {
- // we need to increase all animation keyframes of the previous animator by the
- // relative change to the end value
- PropertyValuesHolder[] values = previousAnimator.getValues();
- float relativeDiff = newEndValue - previousEndValue;
- float newStartValue = previousStartValue + relativeDiff;
- values[0].setFloatValues(newStartValue, newEndValue);
- values[1].setFloatValues(newStartValue, newEndValue);
- child.setTag(TAG_START_SCALE, newStartValue);
- child.setTag(TAG_END_SCALE, newEndValue);
- previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
- return;
- } else {
- // no new animation needed, let's just apply the value
- child.setScaleX(newEndValue);
- child.setScaleY(newEndValue);
- }
- }
-
- PropertyValuesHolder holderX =
- PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue);
- PropertyValuesHolder holderY =
- PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
- ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
- animator.setInterpolator(mFastOutSlowInInterpolator);
- long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
- animator.setDuration(newDuration);
- animator.addListener(getGlobalAnimationFinishedListener());
- // remove the tag when the animation is finished
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- child.setTag(TAG_ANIMATOR_SCALE, null);
- child.setTag(TAG_START_SCALE, null);
- child.setTag(TAG_END_SCALE, null);
- }
- });
- startAnimator(animator);
- child.setTag(TAG_ANIMATOR_SCALE, animator);
- child.setTag(TAG_START_SCALE, child.getScaleX());
- child.setTag(TAG_END_SCALE, newEndValue);
- }
-
private void startAnimator(ValueAnimator animator) {
mAnimatorSet.add(animator);
animator.start();
@@ -934,7 +929,7 @@
isRubberbanded);
}
});
- overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
overScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -976,6 +971,22 @@
}
}
+ /**
+ * Get the end value of the height animation running on a view or the actualHeight
+ * if no animation is running.
+ */
+ public static float getFinalTranslationY(ExpandableView view) {
+ if (view == null) {
+ return 0;
+ }
+ ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
+ if (yAnimator == null) {
+ return view.getTranslationY();
+ } else {
+ return getChildTag(view, TAG_END_TRANSLATION_Y);
+ }
+ }
+
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 55ef440..41824ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -42,6 +42,7 @@
public boolean dark;
public boolean hideSensitive;
public boolean belowSpeedBump;
+ public float shadowAlpha;
/**
* The amount which the view should be clipped from the top. This is calculated to
@@ -74,6 +75,7 @@
StackViewState svs = (StackViewState) viewState;
height = svs.height;
dimmed = svs.dimmed;
+ shadowAlpha = svs.shadowAlpha;
dark = svs.dark;
hideSensitive = svs.hideSensitive;
belowSpeedBump = svs.belowSpeedBump;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 3e538df..5beaac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -29,21 +29,21 @@
public float yTranslation;
public float zTranslation;
public boolean gone;
- public float scale;
+ public boolean hidden;
public void copyFrom(ViewState viewState) {
alpha = viewState.alpha;
yTranslation = viewState.yTranslation;
zTranslation = viewState.zTranslation;
gone = viewState.gone;
- scale = viewState.scale;
+ hidden = viewState.hidden;
}
public void initFrom(View view) {
- alpha = view.getVisibility() == View.INVISIBLE ? 0.0f : view.getAlpha();
+ alpha = view.getAlpha();
yTranslation = view.getTranslationY();
zTranslation = view.getTranslationZ();
gone = view.getVisibility() == View.GONE;
- scale = view.getScaleX();
+ hidden = false;
}
}
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..e947ed5 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;
/**
@@ -96,6 +94,12 @@
}
}
};
+ private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
+ @Override
+ public void run() {
+ movePipToFullscreen();
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -314,6 +318,12 @@
// Post the message back to the UI thread.
mHandler.post(mOnActivityPinnedRunnable);
}
+
+ @Override
+ public void onPinnedActivityRestartAttempt() {
+ // Post the message back to the UI thread.
+ mHandler.post(mOnPinnedActivityRestartAttempt);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java b/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java
deleted file mode 100644
index 9438af1..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-public class IconPulser {
- private static final float PULSE_SCALE = 1.1f;
-
- private final Interpolator mFastOutSlowInInterpolator;
-
- public IconPulser(Context context) {
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.fast_out_slow_in);
- }
-
- public void start(final View target) {
- if (target == null || target.getScaleX() != 1) return; // n/a, or already running
- target.animate().cancel();
- target.animate().scaleX(PULSE_SCALE).scaleY(PULSE_SCALE)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- target.animate().scaleX(1).scaleY(1).setListener(null);
- }
- });
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index ed6fc9e..b005a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -258,7 +258,6 @@
if (w > max) {
w = max;
}
- w -= mContext.getResources().getDimensionPixelSize(R.dimen.notification_side_padding) * 2;
lp.width = w;
mDialogView.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
deleted file mode 100644
index f51e8ff..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import org.mockito.InOrder;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.test.AndroidTestCase;
-
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Tests for the data model for the navigation bar app icons. */
-public class NavigationBarAppsModelTest extends AndroidTestCase {
- private PackageManager mMockPackageManager;
- private IPackageManager mMockIPackageManager;
- private SharedPreferences mMockPrefs;
- private SharedPreferences.Editor mMockEdit;
- private UserManager mMockUserManager;
-
- private NavigationBarAppsModel mModel;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- // Mockito setup boilerplate.
- System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
- Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-
- final Context context = mock(Context.class);
- mMockPackageManager = mock(PackageManager.class);
- mMockIPackageManager = mock(IPackageManager.class);
- mMockPrefs = mock(SharedPreferences.class);
- mMockEdit = mock(SharedPreferences.Editor.class);
- mMockUserManager = mock(UserManager.class);
-
- when(context.getSharedPreferences(
- "com.android.systemui.navbarapps", Context.MODE_PRIVATE)).thenReturn(mMockPrefs);
- when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
- when(context.getPackageManager()).thenReturn(mMockPackageManager);
-
- setContext(context);
-
- when(mMockUserManager.getUsers()).thenReturn(new ArrayList<UserInfo>());
- // Assume the version pref is present and equal to the current version.
- when(mMockPrefs.getInt("version", -1)).thenReturn(3);
- when(mMockPrefs.edit()).thenReturn(mMockEdit);
-
- when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(222L);
- when(mMockUserManager.getSerialNumberForUser(new UserHandle(4))).thenReturn(444L);
- when(mMockUserManager.getSerialNumberForUser(new UserHandle(5))).thenReturn(555L);
- when(mMockUserManager.getUserForSerialNumber(222L)).thenReturn(new UserHandle(2));
- when(mMockUserManager.getUserForSerialNumber(444L)).thenReturn(new UserHandle(4));
- when(mMockUserManager.getUserForSerialNumber(555L)).thenReturn(new UserHandle(5));
-
- UserInfo ui2 = new UserInfo();
- ui2.profileGroupId = 999;
- UserInfo ui4 = new UserInfo();
- ui4.profileGroupId = 999;
- UserInfo ui5 = new UserInfo();
- ui5.profileGroupId = 999;
- when(mMockUserManager.getUserInfo(2)).thenReturn(ui2);
- when(mMockUserManager.getUserInfo(4)).thenReturn(ui4);
- when(mMockUserManager.getUserInfo(5)).thenReturn(ui5);
-
- mModel = new NavigationBarAppsModel(context) {
- @Override
- protected IPackageManager getPackageManager() {
- return mMockIPackageManager;
- }
- };
- }
-
- /** Tests resolveApp(). */
- public void testResolveApp() {
- ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
- mockNonExportedActivityInfo.exported = false;
- ActivityInfo mockExportedActivityInfo = new ActivityInfo();
- mockExportedActivityInfo.exported = true;
- try {
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package1", "class1"), 0, 4)).
- thenReturn(mockNonExportedActivityInfo);
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package2", "class2"), 0, 5)).
- thenThrow(new RemoteException());
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package3", "class3"), 0, 6)).
- thenReturn(mockExportedActivityInfo);
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package4", "class4"), 0, 7)).
- thenReturn(mockExportedActivityInfo);
- } catch (RemoteException e) {
- fail("RemoteException can't happen in the test, but it happened.");
- }
-
- // Assume some installed activities.
- ActivityInfo ai0 = new ActivityInfo();
- ai0.packageName = "package0";
- ai0.name = "class0";
- ActivityInfo ai1 = new ActivityInfo();
- ai1.packageName = "package4";
- ai1.name = "class4";
- ResolveInfo ri0 = new ResolveInfo();
- ri0.activityInfo = ai0;
- ResolveInfo ri1 = new ResolveInfo();
- ri1.activityInfo = ai1;
- when(mMockPackageManager
- .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
- .thenReturn(Arrays.asList(ri0, ri1));
-
- mModel.setCurrentUser(3);
- // Unlauncheable (for various reasons) apps.
- assertEquals(null, mModel.resolveApp(
- new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
- mModel.setCurrentUser(4);
- assertEquals(null, mModel.resolveApp(
- new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
- mModel.setCurrentUser(5);
- assertEquals(null, mModel.resolveApp(
- new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
- mModel.setCurrentUser(6);
- assertEquals(null, mModel.resolveApp(
- new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
-
- // A launcheable app.
- mModel.setCurrentUser(7);
- NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp(
- new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
- assertNotNull(resolvedApp);
- Intent intent = resolvedApp.launchIntent;
- assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
- assertEquals("package4", intent.getPackage());
- assertEquals(ri1, resolvedApp.ri);
- }
-
- /** Initializes the model from SharedPreferences for a few app activites. */
- private void initializeModelFromPrefs() {
- // Assume several apps are stored.
- when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
- when(mMockPrefs.getString("222|app_0", null)).thenReturn("package1/class1");
- when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(444L);
- when(mMockPrefs.getString("222|app_1", null)).thenReturn("package2/class2");
- when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(555L);
-
- ActivityInfo mockActivityInfo = new ActivityInfo();
- mockActivityInfo.exported = true;
- try {
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package1", "class1"), 0, 4)).thenReturn(mockActivityInfo);
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package2", "class2"), 0, 5)).thenReturn(mockActivityInfo);
- } catch (RemoteException e) {
- fail("RemoteException can't happen in the test, but it happened.");
- }
-
- // Assume some installed activities.
- ActivityInfo ai0 = new ActivityInfo();
- ai0.packageName = "package0";
- ai0.name = "class0";
- ActivityInfo ai1 = new ActivityInfo();
- ai1.packageName = "package1";
- ai1.name = "class1";
- ActivityInfo ai2 = new ActivityInfo();
- ai2.packageName = "package2";
- ai2.name = "class2";
- ResolveInfo ri0 = new ResolveInfo();
- ri0.activityInfo = ai0;
- ResolveInfo ri1 = new ResolveInfo();
- ri1.activityInfo = ai1;
- ResolveInfo ri2 = new ResolveInfo();
- ri2.activityInfo = ai2;
- when(mMockPackageManager
- .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
- .thenReturn(Arrays.asList(ri0, ri1, ri2));
-
- mModel.setCurrentUser(2);
- }
-
- /** Tests initializing the model from SharedPreferences. */
- public void testInitializeFromPrefs() {
- initializeModelFromPrefs();
- List<AppInfo> apps = mModel.getApps();
- assertEquals(2, apps.size());
- assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
- assertEquals(new UserHandle(4), apps.get(0).getUser());
- assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
- assertEquals(new UserHandle(5), apps.get(1).getUser());
- }
-
- /** Tests initializing the model when the SharedPreferences aren't available. */
- public void testInitializeDefaultApps() {
- // Assume the user's app count pref isn't available.
- when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
-
- // Assume some installed activities.
- ActivityInfo ai1 = new ActivityInfo();
- ai1.packageName = "package1";
- ai1.name = "class1";
- ActivityInfo ai2 = new ActivityInfo();
- ai2.packageName = "package2";
- ai2.name = "class2";
- ResolveInfo ri1 = new ResolveInfo();
- ri1.activityInfo = ai1;
- ResolveInfo ri2 = new ResolveInfo();
- ri2.activityInfo = ai2;
- when(mMockPackageManager
- .queryIntentActivitiesAsUser(any(Intent.class), eq(0), eq(2)))
- .thenReturn(Arrays.asList(ri1, ri2));
-
- // Setting the user should load the installed activities.
- mModel.setCurrentUser(2);
- List<AppInfo> apps = mModel.getApps();
- assertEquals(2, apps.size());
- assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
- assertEquals(new UserHandle(2), apps.get(0).getUser());
- assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
- assertEquals(new UserHandle(2), apps.get(1).getUser());
- InOrder order = inOrder(mMockEdit);
- order.verify(mMockEdit).apply();
- order.verify(mMockEdit).putInt("222|app_count", 2);
- order.verify(mMockEdit).putString("222|app_0", "package1/class1");
- order.verify(mMockEdit).putLong("222|app_user_0", 222L);
- order.verify(mMockEdit).putString("222|app_1", "package2/class2");
- order.verify(mMockEdit).putLong("222|app_user_1", 222L);
- order.verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests initializing the model if one of the prefs is missing. */
- public void testInitializeWithMissingPref() {
- // Assume two apps are nominally stored.
- when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
- when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
- when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
-
- // But assume one pref is missing.
- when(mMockPrefs.getString("222|app_1", null)).thenReturn(null);
-
- ActivityInfo mockActivityInfo = new ActivityInfo();
- mockActivityInfo.exported = true;
- try {
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
- } catch (RemoteException e) {
- fail("RemoteException can't happen in the test, but it happened.");
- }
-
- ActivityInfo ai0 = new ActivityInfo();
- ai0.packageName = "package0";
- ai0.name = "class0";
- ResolveInfo ri0 = new ResolveInfo();
- ri0.activityInfo = ai0;
- when(mMockPackageManager
- .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
- .thenReturn(Arrays.asList(ri0));
-
- // Initializing the model should load from prefs and skip the missing one.
- mModel.setCurrentUser(2);
- List<AppInfo> apps = mModel.getApps();
- assertEquals(1, apps.size());
- assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
- assertEquals(new UserHandle(5), apps.get(0).getUser());
- InOrder order = inOrder(mMockEdit);
- order.verify(mMockEdit).putInt("222|app_count", 1);
- order.verify(mMockEdit).putString("222|app_0", "package0/class0");
- order.verify(mMockEdit).putLong("222|app_user_0", 555L);
- order.verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests initializing the model if one of the apps is unlauncheable. */
- public void testInitializeWithUnlauncheableApp() {
- // Assume two apps are nominally stored.
- when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
- when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
- when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
- when(mMockPrefs.getString("222|app_1", null)).thenReturn("package1/class1");
- when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(444L);
-
- ActivityInfo mockActivityInfo = new ActivityInfo();
- mockActivityInfo.exported = true;
- try {
- when(mMockIPackageManager.getActivityInfo(
- new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
- } catch (RemoteException e) {
- fail("RemoteException can't happen in the test, but it happened.");
- }
-
- ActivityInfo ai0 = new ActivityInfo();
- ai0.packageName = "package0";
- ai0.name = "class0";
- ResolveInfo ri0 = new ResolveInfo();
- ri0.activityInfo = ai0;
- when(mMockPackageManager
- .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
- .thenReturn(Arrays.asList(ri0));
-
- // Initializing the model should load from prefs and skip the unlauncheable one.
- mModel.setCurrentUser(2);
- List<AppInfo> apps = mModel.getApps();
- assertEquals(1, apps.size());
- assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
- assertEquals(new UserHandle(5), apps.get(0).getUser());
-
- // Once an unlauncheable app is detected, the model should save all apps excluding the
- // unlauncheable one.
- verify(mMockEdit).putInt("222|app_count", 1);
- verify(mMockEdit).putString("222|app_0", "package0/class0");
- verify(mMockEdit).putLong("222|app_user_0", 555L);
- verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests saving the model to SharedPreferences. */
- public void testSavePrefs() {
- initializeModelFromPrefs();
-
- mModel.setApps(mModel.getApps());
- verify(mMockEdit).putInt("222|app_count", 2);
- verify(mMockEdit).putString("222|app_0", "package1/class1");
- verify(mMockEdit).putLong("222|app_user_0", 444L);
- verify(mMockEdit).putString("222|app_1", "package2/class2");
- verify(mMockEdit).putLong("222|app_user_1", 555L);
- verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests cleaning all prefs on a version change. */
- public void testVersionChange() {
- // Assume the version pref changed.
- when(mMockPrefs.getInt("version", -1)).thenReturn(1);
-
- new NavigationBarAppsModel(getContext());
- verify(mMockEdit).clear();
- verify(mMockEdit).putInt("version", 3);
- verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests cleaning prefs for deleted users. */
- public void testCleaningDeletedUsers() {
- // Users on the device.
- final UserInfo user1 = new UserInfo(11, "", 0);
- user1.serialNumber = 1111;
- final UserInfo user2 = new UserInfo(13, "", 0);
- user2.serialNumber = 1313;
-
- when(mMockUserManager.getUsers()).thenReturn(Arrays.asList(user1, user2));
-
- when(mMockPrefs.edit()).
- thenReturn(mMockEdit).
- thenReturn(mock(SharedPreferences.Editor.class));
-
- // Assume the user's app count pref isn't available. This will trigger clearing deleted
- // users' prefs.
- when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
-
- final Map allPrefs = new HashMap<String, Object>();
- allPrefs.put("version", null);
- allPrefs.put("some_strange_pref", null);
- allPrefs.put("", null);
- allPrefs.put("|", null);
- allPrefs.put("1313|app_count", null);
- allPrefs.put("1212|app_count", null);
- when(mMockPrefs.getAll()).thenReturn(allPrefs);
-
- // Setting the user should remove prefs for deleted users.
- mModel.setCurrentUser(2);
- verify(mMockEdit).remove("some_strange_pref");
- verify(mMockEdit).remove("");
- verify(mMockEdit).remove("|");
- verify(mMockEdit).remove("1212|app_count");
- verify(mMockEdit).apply();
- verifyNoMoreInteractions(mMockEdit);
- }
-
- /** Tests the apps-changed listener. */
- public void testAppsChangedListeners() {
- NavigationBarAppsModel.OnAppsChangedListener listener =
- mock(NavigationBarAppsModel.OnAppsChangedListener.class);
-
- mModel.addOnAppsChangedListener(listener);
- mModel.setApps(new ArrayList<AppInfo>());
- verify(listener).onPinnedAppsChanged();
- verifyNoMoreInteractions(listener);
-
- mModel.removeOnAppsChangedListener(listener);
- mModel.setApps(new ArrayList<AppInfo>());
- verifyNoMoreInteractions(listener);
- }
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 88e55dd..697777c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -339,5 +339,8 @@
// Logged when the user calls an emergency contact.
ACTION_CALL_EMERGENCY_CONTACT = 283;
+
+ // QS Tile for Data Saver.
+ QS_DATA_SAVER = 284;
}
}
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/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d63dd0c..46198b0 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1920,7 +1920,9 @@
synchronized (mBackupParticipants) {
if (replacing) {
// This is the package-replaced case; we just remove the entry
- // under the old uid and fall through to re-add.
+ // under the old uid and fall through to re-add. If an app
+ // just added key/value backup participation, this picks it up
+ // as a known participant.
removePackageParticipantsLocked(pkgList, uid);
}
addPackageParticipantsLocked(pkgList);
@@ -1933,6 +1935,14 @@
if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
+ } else {
+ // The app might have just transitioned out of full-data into
+ // doing key/value backups, or might have just disabled backups
+ // entirely. Make sure it is no longer in the full-data queue.
+ synchronized (mQueueLock) {
+ dequeueFullBackupLocked(packageName);
+ }
+ writeFullBackupScheduleAsync();
}
// Transport maintenance: rebind to known existing transports that have
@@ -1964,6 +1974,9 @@
if (replacing) {
// The package is being updated. We'll receive a PACKAGE_ADDED shortly.
} else {
+ // Outright removal. In the full-data case, the app will be dropped
+ // from the queue when its (now obsolete) name comes up again for
+ // backup.
synchronized (mBackupParticipants) {
removePackageParticipantsLocked(pkgList, uid);
}
@@ -4190,11 +4203,21 @@
// as well as any explicit mention of the 'special' shared-storage agent
// package (we handle that one at the end).
if (MORE_DEBUG) {
- Slog.d(TAG, "Ignoring not eligible package " + pkg);
+ Slog.d(TAG, "Ignoring ineligible package " + pkg);
}
sendBackupOnResult(mBackupObserver, pkg,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
+ } else if (!appGetsFullBackup(info)) {
+ // Cull any packages that are found in the queue but now aren't supposed
+ // to get full-data backup operations.
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Ignoring full-data backup of key/value participant "
+ + pkg);
+ }
+ sendBackupOnResult(mBackupObserver, pkg,
+ BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ continue;
} else if (appIsStopped(info.applicationInfo)) {
// Cull any packages in the 'stopped' state: they've either just been
// installed or have explicitly been force-stopped by the user. In both
@@ -4592,21 +4615,28 @@
}
/**
+ * Remove a package from the full-data queue.
+ */
+ void dequeueFullBackupLocked(String packageName) {
+ final int N = mFullBackupQueue.size();
+ for (int i = N-1; i >= 0; i--) {
+ final FullBackupEntry e = mFullBackupQueue.get(i);
+ if (packageName.equals(e.packageName)) {
+ mFullBackupQueue.remove(i);
+ }
+ }
+ }
+
+ /**
* Enqueue full backup for the given app, with a note about when it last ran.
*/
void enqueueFullBackup(String packageName, long lastBackedUp) {
FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
synchronized (mQueueLock) {
- int N = mFullBackupQueue.size();
// First, sanity check that we aren't adding a duplicate. Slow but
// straightforward; we'll have at most on the order of a few hundred
// items in this list.
- for (int i = N-1; i >= 0; i--) {
- final FullBackupEntry e = mFullBackupQueue.get(i);
- if (packageName.equals(e.packageName)) {
- mFullBackupQueue.remove(i);
- }
- }
+ dequeueFullBackupLocked(packageName);
// This is also slow but easy for modest numbers of apps: work backwards
// from the end of the queue until we find an item whose last backup
@@ -4700,21 +4730,24 @@
return false;
}
- if (mFullBackupQueue.size() == 0) {
- // no work to do so just bow out
- if (DEBUG) {
- Slog.i(TAG, "Backup queue empty; doing nothing");
- }
- return false;
- }
-
- // At this point we know that we have work to do, but possibly not right now.
+ // At this point we think that we have work to do, but possibly not right now.
// Any exit without actually running backups will also require that we
// reschedule the job.
boolean runBackup = true;
boolean headBusy;
do {
+ // Recheck each time, because culling due to ineligibility may
+ // have emptied the queue.
+ if (mFullBackupQueue.size() == 0) {
+ // no work to do so just bow out
+ if (DEBUG) {
+ Slog.i(TAG, "Backup queue empty; doing nothing");
+ }
+ runBackup = false;
+ break;
+ }
+
headBusy = false;
if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
@@ -4744,6 +4777,19 @@
try {
PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ if (!appGetsFullBackup(appInfo)) {
+ // The head app isn't supposed to get full-data backups [any more];
+ // so we cull it and force a loop around to consider the new head
+ // app.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Culling package " + entry.packageName
+ + " in full-backup queue but not eligible");
+ }
+ mFullBackupQueue.remove(0);
+ headBusy = true; // force the while() condition
+ continue;
+ }
+
headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
if (headBusy) {
@@ -4762,7 +4808,6 @@
enqueueFullBackup(entry.packageName,
nextEligible - MIN_FULL_BACKUP_INTERVAL);
}
-
} catch (NameNotFoundException nnf) {
// So, we think we want to back this up, but it turns out the package
// in question is no longer installed. We want to drop it from the
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index c1a082b..499b706 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -114,13 +114,6 @@
private static final int SERVICE_IBLUETOOTH = 1;
private static final int SERVICE_IBLUETOOTHGATT = 2;
- private static final String[] DEVICE_TYPE_NAMES = new String[] {
- "???",
- "BR/EDR",
- "LE",
- "DUAL"
- };
-
private final Context mContext;
private static int mBleAppCount = 0;
@@ -131,6 +124,7 @@
private final ContentResolver mContentResolver;
private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
+ private IBinder mBluetoothBinder;
private IBluetooth mBluetooth;
private IBluetoothGatt mBluetoothGatt;
private boolean mBinding;
@@ -242,6 +236,7 @@
mContext = context;
mBluetooth = null;
+ mBluetoothBinder = null;
mBluetoothGatt = null;
mBinding = false;
mUnbinding = false;
@@ -678,6 +673,7 @@
}
if (DBG) Log.d(TAG, "Sending unbind request.");
+ mBluetoothBinder = null;
mBluetooth = null;
//Unbind
mContext.unbindService(mConnection);
@@ -1166,6 +1162,7 @@
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
mBinding = false;
+ mBluetoothBinder = service;
mBluetooth = IBluetooth.Stub.asInterface(service);
try {
@@ -1674,44 +1671,15 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-
- writer.println("Bluetooth Status");
- writer.println(" enabled: " + mEnable);
- writer.println(" state: " + mState);
- writer.println(" address: " + mAddress);
- writer.println(" name: " + mName + "\n");
- writer.flush();
-
- if (mBluetooth == null) {
- writer.println("Bluetooth Service not connected");
- } else {
- ParcelFileDescriptor pfd = null;
- try {
- writer.println("Bonded devices:");
- for (BluetoothDevice device : mBluetooth.getBondedDevices()) {
- writer.println(" " + device.getAddress() +
- " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " +
- device.getName());
- }
- writer.flush();
-
- pfd = ParcelFileDescriptor.dup(fd);
- mBluetooth.dump(pfd);
- } catch (RemoteException re) {
- writer.println("RemoteException while calling Bluetooth Service");
- } catch (IOException ioe) {
- writer.println("IOException attempting to dup() fd");
- } finally {
- if (pfd != null) {
- try {
- pfd.close();
- } catch (IOException ioe) {
- writer.println("IOException attempting to close() fd");
- }
- }
- }
+ public void dump(FileDescriptor fd, PrintWriter writer, String args[]) {
+ if (mBluetoothBinder == null) {
+ writer.println("Bluetooth Service not connected");
+ } else {
+ try {
+ mBluetoothBinder.dump(fd, args);
+ } catch (RemoteException re) {
+ writer.println("RemoteException while calling Bluetooth Service");
}
+ }
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9927fd6..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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index dd19c6a..95f5734 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -186,7 +186,6 @@
private final Handler mFgHandler;
private final Handler mDaemonHandler;
- private final PhoneStateListener mPhoneStateListener;
private IBatteryStats mBatteryStats;
@@ -283,22 +282,6 @@
mDaemonHandler = new Handler(FgThread.get().getLooper());
- mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- mDaemonHandler.getLooper()) {
- @Override
- public void onDataConnectionRealTimeInfoChanged(
- DataConnectionRealTimeInfo dcRtInfo) {
- if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo);
- notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
- dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
- }
- };
- TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- if (tm != null) {
- tm.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO);
- }
-
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 19a4851..820551d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -34,7 +34,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.CellLocation;
-import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.telephony.SubscriptionManager;
@@ -179,8 +178,6 @@
private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
- private DataConnectionRealTimeInfo mDcRtInfo = new DataConnectionRealTimeInfo();
-
private int mRingingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
private int mForegroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -262,7 +259,8 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
Integer newDefaultSubIdObj = new Integer(intent.getIntExtra(
- PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubId()));
+ PhoneConstants.SUBSCRIPTION_KEY,
+ SubscriptionManager.getDefaultSubscriptionId()));
int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY,
SubscriptionManager.getPhoneId(mDefaultSubId));
if (DBG) {
@@ -624,13 +622,6 @@
remove(r.binder);
}
}
- if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
- try {
- r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState);
@@ -921,31 +912,6 @@
}
}
- public void notifyDataConnectionRealTimeInfo(DataConnectionRealTimeInfo dcRtInfo) {
- if (!checkNotifyPermission("notifyDataConnectionRealTimeInfo()")) {
- return;
- }
-
- synchronized (mRecords) {
- mDcRtInfo = dcRtInfo;
- for (Record r : mRecords) {
- if (validateEventsAndUserLocked(r,
- PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO)) {
- try {
- if (DBG_LOC) {
- log("notifyDataConnectionRealTimeInfo: mDcRtInfo="
- + mDcRtInfo + " r=" + r);
- }
- r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
@@ -1370,7 +1336,6 @@
pw.println(" mCellLocation=" + mCellLocation[i]);
pw.println(" mCellInfo=" + mCellInfo.get(i));
}
- pw.println(" mDcRtInfo=" + mDcRtInfo);
pw.println("registrations: count=" + recordCount);
for (Record r : mRecords) {
pw.println(" " + r);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2683be6..d0006aa 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4266,21 +4266,6 @@
}
}
- private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
- for (String perm : permissions) {
- if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, " caller uid " + callingUid + " has " + perm);
- }
- final int opCode = AppOpsManager.permissionToOpCode(perm);
- if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
- opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
- }
- }
- return false;
- }
private int handleIncomingUser(int userId) {
try {
@@ -4355,12 +4340,50 @@
private List<String> getTypesVisibleToCaller(int callingUid, int userId,
String opPackageName) {
- boolean isPermitted =
- isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
- Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+ List<String> permissionsToCheck = new ArrayList<String>(2);
+ permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+ try {
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+ opPackageName, 0 /* flags */);
+ /*
+ * At or before SDK 23, clients discover all the accounts in their
+ * user profile (via AccountManager.getAccounts(...)) by declaring
+ * the GET_ACCOUNTS permission.
+ *
+ * After SDK 23 the GET_ACCOUNTS permission is deprecated. Instead
+ * apps will be able to retrieve those accounts managed by
+ * authenticators sharing a package signature without any special
+ * permissions. The only clients able to discover all the accounts
+ * on the device will be those with the GET_ACCOUNTS_PRVILEGED
+ * system permission.
+ */
+ if (23 >= appInfo.targetSdkVersion) {
+ permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS);
+ }
+ } catch (NameNotFoundException e) {
+ // No application associated with the specified package.
+ Log.w(TAG, "No application associated with package: " + opPackageName);
+ }
+ boolean isPermitted = isPermitted(opPackageName, callingUid, permissionsToCheck);
return getTypesForCaller(callingUid, userId, isPermitted);
}
+ private boolean isPermitted(String opPackageName, int callingUid, List<String> permissions) {
+ for (String perm : permissions) {
+ if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, " caller uid " + callingUid + " has " + perm);
+ }
+ final int opCode = AppOpsManager.permissionToOpCode(perm);
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private List<String> getTypesManagedByCaller(int callingUid, int userId) {
return getTypesForCaller(callingUid, userId, false);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7ba6338..8c0ec78 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2775,7 +2775,7 @@
}
if (anrMessage != null) {
- mAm.appNotResponding(proc, null, null, false, anrMessage);
+ mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1434e5e..2c55ee2 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;
@@ -278,7 +277,6 @@
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
@@ -393,7 +391,7 @@
// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
- private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
@@ -599,6 +597,12 @@
final UserController mUserController;
+ final AppErrors mAppErrors;
+
+ public boolean canShowErrorDialogs() {
+ return mShowDialogs && !mSleeping && !mShuttingDown;
+ }
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -669,38 +673,6 @@
ProcessRecord mHeavyWeightProcess = null;
/**
- * The last time that various processes have crashed.
- */
- final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
-
- /**
- * Information about a process that is currently marked as bad.
- */
- static final class BadProcessInfo {
- BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
- this.time = time;
- this.shortMsg = shortMsg;
- this.longMsg = longMsg;
- this.stack = stack;
- }
-
- final long time;
- final String shortMsg;
- final String longMsg;
- final String stack;
- }
-
- /**
- * Set of applications that we consider to be bad, and will reject
- * incoming broadcasts from (which the user has no control over).
- * Processes are added to this set when they have crashed twice within
- * a minimum amount of time; they are removed from it when they are
- * later restarted (hopefully due to some user action). The value is the
- * time it was added to the list.
- */
- final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>();
-
- /**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
@@ -1352,8 +1324,6 @@
final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
- ArraySet<String> mAppsNotReportingCrashes;
-
/**
* Runtime CPU use collection thread. This object's lock is used to
* perform synchronization with the thread (notifying it to run).
@@ -1473,6 +1443,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;
@@ -1511,80 +1482,11 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_UI_MSG: {
- HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
- boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
- synchronized (ActivityManagerService.this) {
- ProcessRecord proc = (ProcessRecord)data.get("app");
- AppErrorResult res = (AppErrorResult) data.get("result");
- if (proc != null && proc.crashDialog != null) {
- Slog.e(TAG, "App already has crash dialog: " + proc);
- if (res != null) {
- res.set(0);
- }
- return;
- }
- boolean isBackground = (UserHandle.getAppId(proc.uid)
- >= Process.FIRST_APPLICATION_UID
- && proc.pid != MY_PID);
- for (int userId : mUserController.getCurrentProfileIdsLocked()) {
- isBackground &= (proc.userId != userId);
- }
- if (isBackground && !showBackground) {
- Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
- if (res != null) {
- res.set(0);
- }
- return;
- }
- final boolean crashSilenced = mAppsNotReportingCrashes != null &&
- mAppsNotReportingCrashes.contains(proc.info.packageName);
- if (mShowDialogs && !mSleeping && !mShuttingDown && !crashSilenced) {
- Dialog d = new AppErrorDialog(mContext,
- ActivityManagerService.this, res, proc);
- d.show();
- proc.crashDialog = d;
- } else {
- // The device is asleep, so just pretend that the user
- // saw a crash dialog and hit "force quit".
- if (res != null) {
- res.set(0);
- }
- }
- }
-
+ mAppErrors.handleShowAppErrorUi(msg);
ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_UI_MSG: {
- synchronized (ActivityManagerService.this) {
- HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
- ProcessRecord proc = (ProcessRecord)data.get("app");
- if (proc != null && proc.anrDialog != null) {
- Slog.e(TAG, "App already has anr dialog: " + proc);
- return;
- }
-
- Intent intent = new Intent("android.intent.action.ANR");
- if (!mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- }
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
-
- if (mShowDialogs) {
- Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (ActivityRecord)data.get("activity"),
- msg.arg1 != 0);
- d.show();
- proc.anrDialog = d;
- } else {
- // Just kill the app if there is no dialog to be shown.
- killAppAtUsersRequest(proc, null);
- }
- }
-
+ mAppErrors.handleShowAnrUi(msg);
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
@@ -2056,6 +1958,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;
@@ -2495,6 +2411,7 @@
mServices = new ActiveServices(this);
mProviderMap = new ProviderMap(this);
+ mAppErrors = new AppErrors(mContext, this);
// TODO: Move creation of battery stats service outside of activity manager service.
File dataDir = Environment.getDataDirectory();
@@ -3013,7 +2930,7 @@
return index;
}
- private static void killProcessGroup(int uid, int pid) {
+ static void killProcessGroup(int uid, int pid) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
Process.killProcessGroup(uid, pid);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -3335,7 +3252,7 @@
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
- if (mBadProcesses.get(info.processName, info.uid) != null) {
+ if (mAppErrors.isBadProcessLocked(info)) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ "/" + info.processName);
return null;
@@ -3347,12 +3264,12 @@
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ "/" + info.processName);
- mProcessCrashTimes.remove(info.processName, info.uid);
- if (mBadProcesses.get(info.processName, info.uid) != null) {
+ mAppErrors.resetProcessCrashTimeLocked(info);
+ if (mAppErrors.isBadProcessLocked(info)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
info.processName);
- mBadProcesses.remove(info.processName, info.uid);
+ mAppErrors.clearBadProcessLocked(info);
if (app != null) {
app.bad = false;
}
@@ -4760,46 +4677,7 @@
}
synchronized(this) {
- ProcessRecord proc = null;
-
- // Figure out which process to kill. We don't trust that initialPid
- // still has any relation to current pids, so must scan through the
- // list.
- synchronized (mPidsSelfLocked) {
- for (int i=0; i<mPidsSelfLocked.size(); i++) {
- ProcessRecord p = mPidsSelfLocked.valueAt(i);
- if (p.uid != uid) {
- continue;
- }
- if (p.pid == initialPid) {
- proc = p;
- break;
- }
- if (p.pkgList.containsKey(packageName)) {
- proc = p;
- }
- }
- }
-
- if (proc == null) {
- Slog.w(TAG, "crashApplication: nothing for uid=" + uid
- + " initialPid=" + initialPid
- + " packageName=" + packageName);
- return;
- }
-
- if (proc.thread != null) {
- if (proc.pid == Process.myPid()) {
- Log.w(TAG, "crashApplication: trying to crash self!");
- return;
- }
- long ident = Binder.clearCallingIdentity();
- try {
- proc.thread.scheduleCrash(message);
- } catch (RemoteException e) {
- }
- Binder.restoreCallingIdentity(ident);
- }
+ mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, message);
}
}
@@ -5280,169 +5158,6 @@
}
}
- final void appNotResponding(ProcessRecord app, ActivityRecord activity,
- ActivityRecord parent, boolean aboveSystem, final String annotation) {
- ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
- SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
-
- if (mController != null) {
- try {
- // 0 == continue, -1 = kill process immediately
- int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
- if (res < 0 && app.pid != MY_PID) {
- app.kill("anr", true);
- }
- } catch (RemoteException e) {
- mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- }
-
- long anrTime = SystemClock.uptimeMillis();
- if (MONITOR_CPU_USAGE) {
- updateCpuStatsNow();
- }
-
- synchronized (this) {
- // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
- if (mShuttingDown) {
- Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
- return;
- } else if (app.notResponding) {
- Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
- return;
- } else if (app.crashing) {
- Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
- return;
- }
-
- // In case we come through here for the same app before completing
- // this one, mark as anring now so we will bail out.
- app.notResponding = true;
-
- // Log the ANR to the event log.
- EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
- app.processName, app.info.flags, annotation);
-
- // Dump thread traces as quickly as we can, starting with "interesting" processes.
- firstPids.add(app.pid);
-
- int parentPid = app.pid;
- if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
- if (parentPid != app.pid) firstPids.add(parentPid);
-
- if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r != null && r.thread != null) {
- int pid = r.pid;
- if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
- if (r.persistent) {
- firstPids.add(pid);
- } else {
- lastPids.put(pid, Boolean.TRUE);
- }
- }
- }
- }
- }
-
- // Log the ANR to the main log.
- StringBuilder info = new StringBuilder();
- info.setLength(0);
- info.append("ANR in ").append(app.processName);
- if (activity != null && activity.shortComponentName != null) {
- info.append(" (").append(activity.shortComponentName).append(")");
- }
- info.append("\n");
- info.append("PID: ").append(app.pid).append("\n");
- if (annotation != null) {
- info.append("Reason: ").append(annotation).append("\n");
- }
- if (parent != null && parent != activity) {
- info.append("Parent: ").append(parent.shortComponentName).append("\n");
- }
-
- final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
- File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
- NATIVE_STACKS_OF_INTEREST);
-
- String cpuInfo = null;
- if (MONITOR_CPU_USAGE) {
- updateCpuStatsNow();
- synchronized (mProcessCpuTracker) {
- cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
- }
- info.append(processCpuTracker.printCurrentLoad());
- info.append(cpuInfo);
- }
-
- info.append(processCpuTracker.printCurrentState(anrTime));
-
- Slog.e(TAG, info.toString());
- if (tracesFile == null) {
- // There is no trace file, so dump (only) the alleged culprit's threads to the log
- Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
- }
-
- addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
- cpuInfo, tracesFile, null);
-
- if (mController != null) {
- try {
- // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
- int res = mController.appNotResponding(app.processName, app.pid, info.toString());
- if (res != 0) {
- if (res < 0 && app.pid != MY_PID) {
- app.kill("anr", true);
- } else {
- synchronized (this) {
- mServices.scheduleServiceTimeoutLocked(app);
- }
- }
- return;
- }
- } catch (RemoteException e) {
- mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- }
-
- // Unless configured otherwise, swallow ANRs in background processes & kill the process.
- boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
- synchronized (this) {
- mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
-
- if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
- app.kill("bg anr", true);
- return;
- }
-
- // Set the app's notResponding state, and look up the errorReportReceiver
- makeAppNotRespondingLocked(app,
- activity != null ? activity.shortComponentName : null,
- annotation != null ? "ANR " + annotation : "ANR",
- info.toString());
-
- // Bring up the infamous App Not Responding dialog
- Message msg = Message.obtain();
- HashMap<String, Object> map = new HashMap<String, Object>();
- msg.what = SHOW_NOT_RESPONDING_UI_MSG;
- msg.obj = map;
- msg.arg1 = aboveSystem ? 1 : 0;
- map.put("app", app);
- if (activity != null) {
- map.put("activity", activity);
- }
-
- mUiHandler.sendMessage(msg);
- }
- }
-
final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
if (!mLaunchWarningShown) {
mLaunchWarningShown = true;
@@ -6065,33 +5780,7 @@
Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
}
- final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
- for (int ip = pmap.size() - 1; ip >= 0; ip--) {
- SparseArray<Long> ba = pmap.valueAt(ip);
- for (i = ba.size() - 1; i >= 0; i--) {
- boolean remove = false;
- final int entUid = ba.keyAt(i);
- if (packageName != null) {
- if (userId == UserHandle.USER_ALL) {
- if (UserHandle.getAppId(entUid) == appId) {
- remove = true;
- }
- } else {
- if (entUid == UserHandle.getUid(userId, appId)) {
- remove = true;
- }
- }
- } else if (UserHandle.getUserId(entUid) == userId) {
- remove = true;
- }
- if (remove) {
- ba.removeAt(i);
- }
- }
- if (ba.size() == 0) {
- pmap.removeAt(ip);
- }
- }
+ mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
@@ -6256,7 +5945,7 @@
}
}
- private final boolean removeProcessLocked(ProcessRecord app,
+ boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
final int uid = app.uid;
@@ -8989,6 +8678,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
@@ -10919,7 +10613,7 @@
final long token = Binder.clearCallingIdentity();
try {
- appNotResponding(host, null, null, false, "ContentProvider not responding");
+ mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding");
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -11284,6 +10978,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();
@@ -11673,7 +11377,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- appNotResponding(proc, activity, parent, aboveSystem, annotation);
+ mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
}
@@ -12467,17 +12171,8 @@
com.android.internal.R.dimen.thumbnail_height);
mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
- final String appsNotReportingCrashes = res.getString(
- com.android.internal.R.string.config_appsNotReportingCrashes);
- if (appsNotReportingCrashes != null) {
- final String[] split = appsNotReportingCrashes.split(",");
- if (split.length > 0) {
- mAppsNotReportingCrashes = new ArraySet<>();
- for (int i = 0; i < split.length; i++) {
- mAppsNotReportingCrashes.add(split[i]);
- }
- }
- }
+ mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
+ com.android.internal.R.string.config_appsNotReportingCrashes));
}
}
@@ -12882,174 +12577,12 @@
}
}
- private boolean makeAppCrashingLocked(ProcessRecord app,
- String shortMsg, String longMsg, String stackTrace) {
- app.crashing = true;
- app.crashingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
- startAppProblemLocked(app);
- app.stopFreezingAllLocked();
- return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace);
- }
-
- private void makeAppNotRespondingLocked(ProcessRecord app,
- String activity, String shortMsg, String longMsg) {
- app.notResponding = true;
- app.notRespondingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
- activity, shortMsg, longMsg, null);
- startAppProblemLocked(app);
- app.stopFreezingAllLocked();
- }
-
- /**
- * Generate a process error record, suitable for attachment to a ProcessRecord.
- *
- * @param app The ProcessRecord in which the error occurred.
- * @param condition Crashing, Application Not Responding, etc. Values are defined in
- * ActivityManager.AppErrorStateInfo
- * @param activity The activity associated with the crash, if known.
- * @param shortMsg Short message describing the crash.
- * @param longMsg Long message describing the crash.
- * @param stackTrace Full crash stack trace, may be null.
- *
- * @return Returns a fully-formed AppErrorStateInfo record.
- */
- private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
- int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
- ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
-
- report.condition = condition;
- report.processName = app.processName;
- report.pid = app.pid;
- report.uid = app.info.uid;
- report.tag = activity;
- report.shortMsg = shortMsg;
- report.longMsg = longMsg;
- report.stackTrace = stackTrace;
-
- return report;
- }
-
void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
synchronized (this) {
- app.crashing = false;
- app.crashingReport = null;
- app.notResponding = false;
- app.notRespondingReport = null;
- if (app.anrDialog == fromDialog) {
- app.anrDialog = null;
- }
- if (app.waitDialog == fromDialog) {
- app.waitDialog = null;
- }
- if (app.pid > 0 && app.pid != MY_PID) {
- handleAppCrashLocked(app, "user-terminated" /*reason*/,
- null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/);
- app.kill("user request after error", true);
- }
+ mAppErrors.killAppAtUserRequestLocked(app, fromDialog);
}
}
- private boolean handleAppCrashLocked(ProcessRecord app, String reason,
- String shortMsg, String longMsg, String stackTrace) {
- long now = SystemClock.uptimeMillis();
-
- Long crashTime;
- if (!app.isolated) {
- crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
- } else {
- crashTime = null;
- }
- if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
- // This process loses!
- Slog.w(TAG, "Process " + app.info.processName
- + " has crashed too many times: killing!");
- EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
- app.userId, app.info.processName, app.uid);
- mStackSupervisor.handleAppCrashLocked(app);
- if (!app.persistent) {
- // We don't want to start this process again until the user
- // explicitly does so... but for persistent process, we really
- // need to keep it running. If a persistent process is actually
- // repeatedly crashing, then badness for everyone.
- EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
- app.info.processName);
- if (!app.isolated) {
- // XXX We don't have a way to mark isolated processes
- // as bad, since they don't have a peristent identity.
- mBadProcesses.put(app.info.processName, app.uid,
- new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
- mProcessCrashTimes.remove(app.info.processName, app.uid);
- }
- app.bad = true;
- app.removed = true;
- // Don't let services in this process be restarted and potentially
- // annoy the user repeatedly. Unless it is persistent, since those
- // processes run critical code.
- removeProcessLocked(app, false, false, "crash");
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- return false;
- }
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- } else {
- mStackSupervisor.finishTopRunningActivityLocked(app, reason);
- }
-
- // Bump up the crash count of any services currently running in the proc.
- for (int i=app.services.size()-1; i>=0; i--) {
- // Any services running in the application need to be placed
- // back in the pending list.
- ServiceRecord sr = app.services.valueAt(i);
- sr.crashCount++;
- }
-
- // If the crashing process is what we consider to be the "home process" and it has been
- // replaced by a third-party app, clear the package preferred activities from packages
- // with a home activity running in the process to prevent a repeatedly crashing app
- // from blocking the user to manually clear the list.
- final ArrayList<ActivityRecord> activities = app.activities;
- if (app == mHomeProcess && activities.size() > 0
- && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()) {
- Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
- try {
- ActivityThread.getPackageManager()
- .clearPackagePreferredActivities(r.packageName);
- } catch (RemoteException c) {
- // pm is in same process, this will never happen.
- }
- }
- }
- }
-
- if (!app.isolated) {
- // XXX Can't keep track of crash times for isolated processes,
- // because they don't have a perisistent identity.
- mProcessCrashTimes.put(app.info.processName, app.uid, now);
- }
-
- if (app.crashHandler != null) mHandler.post(app.crashHandler);
- return true;
- }
-
- void startAppProblemLocked(ProcessRecord app) {
- // If this app is not running under the current user, then we
- // can't give it a report button because that would require
- // launching the report UI under a different user.
- app.errorReportReceiver = null;
-
- for (int userId : mUserController.getCurrentProfileIdsLocked()) {
- if (app.userId == userId) {
- app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
- mContext, app.info.packageName, app.info.flags);
- }
- }
- skipCurrentReceiverLocked(app);
- }
-
void skipCurrentReceiverLocked(ProcessRecord app) {
for (BroadcastQueue queue : mBroadcastQueues) {
queue.skipCurrentReceiverLocked(app);
@@ -13085,7 +12618,7 @@
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
- crashApplication(r, crashInfo);
+ mAppErrors.crashApplication(r, crashInfo);
}
public void handleApplicationStrictModeViolation(
@@ -13296,7 +12829,7 @@
if (r != null && r.pid != Process.myPid() &&
Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WTF_IS_FATAL, 0) != 0) {
- crashApplication(r, crashInfo);
+ mAppErrors.crashApplication(r, crashInfo);
return true;
} else {
return false;
@@ -13505,164 +13038,6 @@
}
}
- /**
- * Bring up the "unexpected error" dialog box for a crashing app.
- * Deal with edge cases (intercepts from instrumented applications,
- * ActivityController, error intent receivers, that sort of thing).
- * @param r the application crashing
- * @param crashInfo describing the failure
- */
- private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
- long timeMillis = System.currentTimeMillis();
- String shortMsg = crashInfo.exceptionClassName;
- String longMsg = crashInfo.exceptionMessage;
- String stackTrace = crashInfo.stackTrace;
- if (shortMsg != null && longMsg != null) {
- longMsg = shortMsg + ": " + longMsg;
- } else if (shortMsg != null) {
- longMsg = shortMsg;
- }
-
- AppErrorResult result = new AppErrorResult();
- synchronized (this) {
- if (mController != null) {
- try {
- String name = r != null ? r.processName : null;
- int pid = r != null ? r.pid : Binder.getCallingPid();
- int uid = r != null ? r.info.uid : Binder.getCallingUid();
- if (!mController.appCrashed(name, pid,
- shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
- if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
- && "Native crash".equals(crashInfo.exceptionClassName)) {
- Slog.w(TAG, "Skip killing native crashed app " + name
- + "(" + pid + ") during testing");
- } else {
- Slog.w(TAG, "Force-killing crashed app " + name
- + " at watcher's request");
- if (r != null) {
- r.kill("crash", true);
- } else {
- // Huh.
- Process.killProcess(pid);
- killProcessGroup(uid, pid);
- }
- }
- return;
- }
- } catch (RemoteException e) {
- mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- // If this process is running instrumentation, finish it.
- if (r != null && r.instrumentationClass != null) {
- Slog.w(TAG, "Error in app " + r.processName
- + " running instrumentation " + r.instrumentationClass + ":");
- if (shortMsg != null) Slog.w(TAG, " " + shortMsg);
- if (longMsg != null) Slog.w(TAG, " " + longMsg);
- Bundle info = new Bundle();
- info.putString("shortMsg", shortMsg);
- info.putString("longMsg", longMsg);
- finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
- Binder.restoreCallingIdentity(origId);
- return;
- }
-
- // Log crash in battery stats.
- if (r != null) {
- mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
- }
-
- // If we can't identify the process or it's already exceeded its crash quota,
- // quit right away without showing a crash dialog.
- if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
- Binder.restoreCallingIdentity(origId);
- return;
- }
-
- Message msg = Message.obtain();
- msg.what = SHOW_ERROR_UI_MSG;
- HashMap data = new HashMap();
- data.put("result", result);
- data.put("app", r);
- msg.obj = data;
- mUiHandler.sendMessage(msg);
-
- Binder.restoreCallingIdentity(origId);
- }
-
- int res = result.get();
-
- Intent appErrorIntent = null;
- synchronized (this) {
- if (r != null && !r.isolated) {
- // XXX Can't keep track of crash time for isolated processes,
- // since they don't have a persistent identity.
- mProcessCrashTimes.put(r.info.processName, r.uid,
- SystemClock.uptimeMillis());
- }
- if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
- appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
- }
- }
-
- if (appErrorIntent != null) {
- try {
- mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
- } catch (ActivityNotFoundException e) {
- Slog.w(TAG, "bug report receiver dissappeared", e);
- }
- }
- }
-
- Intent createAppErrorIntentLocked(ProcessRecord r,
- long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
- ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
- if (report == null) {
- return null;
- }
- Intent result = new Intent(Intent.ACTION_APP_ERROR);
- result.setComponent(r.errorReportReceiver);
- result.putExtra(Intent.EXTRA_BUG_REPORT, report);
- result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return result;
- }
-
- private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
- long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
- if (r.errorReportReceiver == null) {
- return null;
- }
-
- if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
- return null;
- }
-
- ApplicationErrorReport report = new ApplicationErrorReport();
- report.packageName = r.info.packageName;
- report.installerPackageName = r.errorReportReceiver.getPackageName();
- report.processName = r.processName;
- report.time = timeMillis;
- report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-
- if (r.crashing || r.forceCrashReport) {
- report.type = ApplicationErrorReport.TYPE_CRASH;
- report.crashInfo = crashInfo;
- } else if (r.notResponding) {
- report.type = ApplicationErrorReport.TYPE_ANR;
- report.anrInfo = new ApplicationErrorReport.AnrInfo();
-
- report.anrInfo.activity = r.notRespondingReport.tag;
- report.anrInfo.cause = r.notRespondingReport.shortMsg;
- report.anrInfo.info = r.notRespondingReport.longMsg;
- }
-
- return report;
- }
-
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
enforceNotIsolatedCaller("getProcessesInErrorState");
// assume our apps are happy - lazy create the list
@@ -14400,88 +13775,9 @@
needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage);
- if (mProcessCrashTimes.getMap().size() > 0) {
- boolean printed = false;
- long now = SystemClock.uptimeMillis();
- final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
- final int NP = pmap.size();
- for (int ip=0; ip<NP; ip++) {
- String pname = pmap.keyAt(ip);
- SparseArray<Long> uids = pmap.valueAt(ip);
- final int N = uids.size();
- for (int i=0; i<N; i++) {
- int puid = uids.keyAt(i);
- ProcessRecord r = mProcessNames.get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" Time since processes crashed:");
- printed = true;
- printedAnything = true;
- }
- pw.print(" Process "); pw.print(pname);
- pw.print(" uid "); pw.print(puid);
- pw.print(": last crashed ");
- TimeUtils.formatDuration(now-uids.valueAt(i), pw);
- pw.println(" ago");
- }
- }
- }
-
- if (mBadProcesses.getMap().size() > 0) {
- boolean printed = false;
- final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
- final int NP = pmap.size();
- for (int ip=0; ip<NP; ip++) {
- String pname = pmap.keyAt(ip);
- SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
- final int N = uids.size();
- for (int i=0; i<N; i++) {
- int puid = uids.keyAt(i);
- ProcessRecord r = mProcessNames.get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.pkgList.containsKey(dumpPackage))) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" Bad processes:");
- printedAnything = true;
- }
- BadProcessInfo info = uids.valueAt(i);
- pw.print(" Bad process "); pw.print(pname);
- pw.print(" uid "); pw.print(puid);
- pw.print(": crashed at time "); pw.println(info.time);
- if (info.shortMsg != null) {
- pw.print(" Short msg: "); pw.println(info.shortMsg);
- }
- if (info.longMsg != null) {
- pw.print(" Long msg: "); pw.println(info.longMsg);
- }
- if (info.stack != null) {
- pw.println(" Stack:");
- int lastPos = 0;
- for (int pos=0; pos<info.stack.length(); pos++) {
- if (info.stack.charAt(pos) == '\n') {
- pw.print(" ");
- pw.write(info.stack, lastPos, pos-lastPos);
- pw.println();
- lastPos = pos+1;
- }
- }
- if (lastPos < info.stack.length()) {
- pw.print(" ");
- pw.write(info.stack, lastPos, info.stack.length()-lastPos);
- pw.println();
- }
- }
- }
- }
+ needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
+ if (needSep) {
+ printedAnything = true;
}
if (dumpPackage == null) {
@@ -17563,6 +16859,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 +16941,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:
@@ -21128,13 +20440,6 @@
}
}
- void stopReportingCrashesLocked(ProcessRecord proc) {
- if (mAppsNotReportingCrashes == null) {
- mAppsNotReportingCrashes = new ArraySet<>();
- }
- mAppsNotReportingCrashes.add(proc.info.packageName);
- }
-
private final class LocalService extends ActivityManagerInternal {
@Override
public void onWakefulnessChanged(int wakefulness) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4bac2d6..ef8d230 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3041,42 +3041,44 @@
mService.updateOomAdjLocked();
}
- final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+ final TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
ActivityRecord r = topRunningActivityLocked();
- if (r != null && r.app == app) {
- // If the top running activity is from this crashing
- // process, then terminate it to avoid getting in a loop.
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- int taskNdx = mTaskHistory.indexOf(r.task);
- int activityNdx = r.task.mActivities.indexOf(r);
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
- // Also terminate any activities below it that aren't yet
- // stopped, to avoid a situation where one will get
- // re-start our crashing activity once it gets resumed again.
- --activityNdx;
- if (activityNdx < 0) {
- do {
- --taskNdx;
- if (taskNdx < 0) {
- break;
- }
- activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
- } while (activityNdx < 0);
- }
- if (activityNdx >= 0) {
- r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
- if (r.state == ActivityState.RESUMED
- || r.state == ActivityState.PAUSING
- || r.state == ActivityState.PAUSED) {
- if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
- }
+ TaskRecord finishedTask = null;
+ if (r == null || r.app != app) {
+ return null;
+ }
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ int taskNdx = mTaskHistory.indexOf(r.task);
+ int activityNdx = r.task.mActivities.indexOf(r);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+ finishedTask = r.task;
+ // Also terminate any activities below it that aren't yet
+ // stopped, to avoid a situation where one will get
+ // re-start our crashing activity once it gets resumed again.
+ --activityNdx;
+ if (activityNdx < 0) {
+ do {
+ --taskNdx;
+ if (taskNdx < 0) {
+ break;
+ }
+ activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+ } while (activityNdx < 0);
+ }
+ if (activityNdx >= 0) {
+ r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+ if (r.state == ActivityState.RESUMED
+ || r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED) {
+ if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
}
}
}
+ return finishedTask;
}
final void finishVoiceTask(IVoiceInteractionSession session) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1fc674b..8db2f8f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1668,15 +1668,21 @@
return false;
}
- void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+ TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+ TaskRecord finishedTask = null;
+ ActivityStack focusedStack = getFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- stack.finishTopRunningActivityLocked(app, reason);
+ TaskRecord t = stack.finishTopRunningActivityLocked(app, reason);
+ if (stack == focusedStack || finishedTask == null) {
+ finishedTask = t;
+ }
}
}
+ return finishedTask;
}
void finishVoiceTask(IVoiceInteractionSession session) {
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/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index c87eae0..b746a4b 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -16,73 +16,74 @@
package com.android.server.am;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.content.Context;
-import android.content.DialogInterface;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.TextView;
-final class AppErrorDialog extends BaseErrorDialog {
+import java.util.List;
+
+import static com.android.server.am.ActivityManagerService.IS_USER_BUILD;
+
+final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
private final ActivityManagerService mService;
private final AppErrorResult mResult;
private final ProcessRecord mProc;
+ private final boolean mRepeating;
+
private CharSequence mName;
// Event 'what' codes
- static final int FORCE_QUIT = 0;
- static final int FORCE_QUIT_AND_REPORT = 1;
+ static final int FORCE_QUIT = 1;
+ static final int FORCE_QUIT_AND_REPORT = 2;
+ static final int RESTART = 3;
+ static final int RESET = 4;
+ static final int MUTE = 5;
// 5-minute timeout, then we automatically dismiss the crash dialog
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
-
- public AppErrorDialog(Context context, ActivityManagerService service,
- AppErrorResult result, ProcessRecord app) {
- super(context);
+ public AppErrorDialog(Context context, ActivityManagerService service, Data data) {
+ super(context);
Resources res = context.getResources();
mService = service;
- mProc = app;
- mResult = result;
- if ((app.pkgList.size() == 1) &&
- (mName = context.getPackageManager().getApplicationLabel(app.info)) != null) {
- setMessage(res.getString(
- com.android.internal.R.string.aerr_application,
- mName.toString(), app.info.processName));
+ mProc = data.proc;
+ mResult = data.result;
+ mRepeating = data.repeating;
+ if ((mProc.pkgList.size() == 1) &&
+ (mName = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
+ setTitle(res.getString(
+ mRepeating ? com.android.internal.R.string.aerr_application_repeated
+ : com.android.internal.R.string.aerr_application,
+ mName.toString(), mProc.info.processName));
} else {
- mName = app.processName;
- setMessage(res.getString(
- com.android.internal.R.string.aerr_process,
+ mName = mProc.processName;
+ setTitle(res.getString(
+ mRepeating ? com.android.internal.R.string.aerr_process_repeated
+ : com.android.internal.R.string.aerr_process,
mName.toString()));
}
setCancelable(false);
- setButton(DialogInterface.BUTTON_POSITIVE,
- res.getText(com.android.internal.R.string.force_close),
- mHandler.obtainMessage(FORCE_QUIT));
-
- if (app.errorReportReceiver != null) {
- setButton(DialogInterface.BUTTON_NEGATIVE,
- res.getText(com.android.internal.R.string.report),
- mHandler.obtainMessage(FORCE_QUIT_AND_REPORT));
- }
-
- setTitle(res.getText(com.android.internal.R.string.aerr_title));
WindowManager.LayoutParams attrs = getWindow().getAttributes();
- attrs.setTitle("Application Error: " + app.info.processName);
+ attrs.setTitle("Application Error: " + mProc.info.processName);
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
| WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
- if (app.persistent) {
+ if (mProc.persistent) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
}
@@ -95,38 +96,44 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (!ActivityManagerService.IS_USER_BUILD) {
- FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom);
- Context context = getContext();
- LayoutInflater.from(context).inflate(
- com.android.internal.R.layout.app_error_dialog_dont_show_again, frame, true);
- ((TextView) frame.findViewById(com.android.internal.R.id.text)).setText(
- context.getResources().getString(
- com.android.internal.R.string.aerr_process_silence,
- mName.toString()));
- findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
- }
+ final FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom);
+ final Context context = getContext();
+ LayoutInflater.from(context).inflate(
+ com.android.internal.R.layout.app_error_dialog, frame, true);
+
+ final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart);
+ restart.setOnClickListener(this);
+ restart.setVisibility(!mRepeating ? View.VISIBLE : View.GONE);
+ final TextView reset = (TextView) findViewById(com.android.internal.R.id.aerr_reset);
+ reset.setOnClickListener(this);
+ reset.setVisibility(mRepeating ? View.VISIBLE : View.GONE);
+ final TextView report = (TextView) findViewById(com.android.internal.R.id.aerr_report);
+ report.setOnClickListener(this);
+ final boolean hasReceiver = mProc.errorReportReceiver != null;
+ report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
+ final TextView close = (TextView) findViewById(com.android.internal.R.id.aerr_close);
+ close.setOnClickListener(this);
+ final TextView mute = (TextView) findViewById(com.android.internal.R.id.aerr_mute);
+ mute.setOnClickListener(this);
+ mute.setVisibility(!IS_USER_BUILD ? View.VISIBLE : View.GONE);
+
+ findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
}
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
- View view = findViewById(com.android.internal.R.id.checkbox);
- final boolean stopReporting = view != null && ((CheckBox) view).isChecked();
+ final int result = msg.what;
+
synchronized (mService) {
if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
mProc.crashDialog = null;
}
- if (stopReporting) {
- mService.stopReportingCrashesLocked(mProc);
- }
}
- mResult.set(msg.what);
+ mResult.set(result);
// Make sure we don't have time timeout still hanging around.
removeMessages(FORCE_QUIT);
- // If this is a timeout we won't be automatically closed, so go
- // ahead and explicitly dismiss ourselves just in case.
dismiss();
}
};
@@ -139,4 +146,34 @@
}
super.dismiss();
}
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case com.android.internal.R.id.aerr_restart:
+ mHandler.obtainMessage(RESTART).sendToTarget();
+ break;
+ case com.android.internal.R.id.aerr_reset:
+ mHandler.obtainMessage(RESET).sendToTarget();
+ break;
+ case com.android.internal.R.id.aerr_report:
+ mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
+ break;
+ case com.android.internal.R.id.aerr_close:
+ mHandler.obtainMessage(FORCE_QUIT).sendToTarget();
+ break;
+ case com.android.internal.R.id.aerr_mute:
+ mHandler.obtainMessage(MUTE).sendToTarget();
+ break;
+ default:
+ break;
+ }
+ }
+
+ static class Data {
+ AppErrorResult result;
+ TaskRecord task;
+ boolean repeating;
+ ProcessRecord proc;
+ }
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
new file mode 100644
index 0000000..58d9f45
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -0,0 +1,964 @@
+/*
+ * 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.server.am;
+
+import com.android.internal.app.ProcessMap;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.server.Watchdog;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.app.ApplicationErrorReport;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.concurrent.Semaphore;
+
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
+
+/**
+ * Controls error conditions in applications.
+ */
+class AppErrors {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
+
+ private final ActivityManagerService mService;
+ private final Context mContext;
+
+ private ArraySet<String> mAppsNotReportingCrashes;
+
+ /**
+ * The last time that various processes have crashed since they were last explicitly started.
+ */
+ private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>();
+
+ /**
+ * The last time that various processes have crashed (not reset even when explicitly started).
+ */
+ private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
+
+ /**
+ * Set of applications that we consider to be bad, and will reject
+ * incoming broadcasts from (which the user has no control over).
+ * Processes are added to this set when they have crashed twice within
+ * a minimum amount of time; they are removed from it when they are
+ * later restarted (hopefully due to some user action). The value is the
+ * time it was added to the list.
+ */
+ private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
+
+
+ AppErrors(Context context, ActivityManagerService service) {
+ mService = service;
+ mContext = context;
+ }
+
+ boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep,
+ String dumpPackage) {
+ if (!mProcessCrashTimes.getMap().isEmpty()) {
+ boolean printed = false;
+ final long now = SystemClock.uptimeMillis();
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<Long> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.pkgList.containsKey(dumpPackage))) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Time since processes crashed:");
+ printed = true;
+ }
+ pw.print(" Process "); pw.print(pname);
+ pw.print(" uid "); pw.print(puid);
+ pw.print(": last crashed ");
+ TimeUtils.formatDuration(now-uids.valueAt(i), pw);
+ pw.println(" ago");
+ }
+ }
+ }
+
+ if (!mBadProcesses.getMap().isEmpty()) {
+ boolean printed = false;
+ final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.pkgList.containsKey(dumpPackage))) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Bad processes:");
+ printed = true;
+ }
+ final BadProcessInfo info = uids.valueAt(i);
+ pw.print(" Bad process "); pw.print(pname);
+ pw.print(" uid "); pw.print(puid);
+ pw.print(": crashed at time "); pw.println(info.time);
+ if (info.shortMsg != null) {
+ pw.print(" Short msg: "); pw.println(info.shortMsg);
+ }
+ if (info.longMsg != null) {
+ pw.print(" Long msg: "); pw.println(info.longMsg);
+ }
+ if (info.stack != null) {
+ pw.println(" Stack:");
+ int lastPos = 0;
+ for (int pos = 0; pos < info.stack.length(); pos++) {
+ if (info.stack.charAt(pos) == '\n') {
+ pw.print(" ");
+ pw.write(info.stack, lastPos, pos-lastPos);
+ pw.println();
+ lastPos = pos+1;
+ }
+ }
+ if (lastPos < info.stack.length()) {
+ pw.print(" ");
+ pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+ pw.println();
+ }
+ }
+ }
+ }
+ }
+ return needSep;
+ }
+
+ boolean isBadProcessLocked(ApplicationInfo info) {
+ return mBadProcesses.get(info.processName, info.uid) != null;
+ }
+
+ void clearBadProcessLocked(ApplicationInfo info) {
+ mBadProcesses.remove(info.processName, info.uid);
+ }
+
+ void resetProcessCrashTimeLocked(ApplicationInfo info) {
+ mProcessCrashTimes.remove(info.processName, info.uid);
+ }
+
+ void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) {
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ for (int ip = pmap.size() - 1; ip >= 0; ip--) {
+ SparseArray<Long> ba = pmap.valueAt(ip);
+ for (int i = ba.size() - 1; i >= 0; i--) {
+ boolean remove = false;
+ final int entUid = ba.keyAt(i);
+ if (!resetEntireUser) {
+ if (userId == UserHandle.USER_ALL) {
+ if (UserHandle.getAppId(entUid) == appId) {
+ remove = true;
+ }
+ } else {
+ if (entUid == UserHandle.getUid(userId, appId)) {
+ remove = true;
+ }
+ }
+ } else if (UserHandle.getUserId(entUid) == userId) {
+ remove = true;
+ }
+ if (remove) {
+ ba.removeAt(i);
+ }
+ }
+ if (ba.size() == 0) {
+ pmap.removeAt(ip);
+ }
+ }
+ }
+
+ void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) {
+ if (appsNotReportingCrashesConfig != null) {
+ final String[] split = appsNotReportingCrashesConfig.split(",");
+ if (split.length > 0) {
+ mAppsNotReportingCrashes = new ArraySet<>();
+ Collections.addAll(mAppsNotReportingCrashes, split);
+ }
+ }
+ }
+
+ void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
+ app.crashing = false;
+ app.crashingReport = null;
+ app.notResponding = false;
+ app.notRespondingReport = null;
+ if (app.anrDialog == fromDialog) {
+ app.anrDialog = null;
+ }
+ if (app.waitDialog == fromDialog) {
+ app.waitDialog = null;
+ }
+ if (app.pid > 0 && app.pid != MY_PID) {
+ handleAppCrashLocked(app, "user-terminated" /*reason*/,
+ null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
+ app.kill("user request after error", true);
+ }
+ }
+
+ void scheduleAppCrashLocked(int uid, int initialPid, String packageName,
+ String message) {
+ ProcessRecord proc = null;
+
+ // Figure out which process to kill. We don't trust that initialPid
+ // still has any relation to current pids, so must scan through the
+ // list.
+
+ synchronized (mService.mPidsSelfLocked) {
+ for (int i=0; i<mService.mPidsSelfLocked.size(); i++) {
+ ProcessRecord p = mService.mPidsSelfLocked.valueAt(i);
+ if (p.uid != uid) {
+ continue;
+ }
+ if (p.pid == initialPid) {
+ proc = p;
+ break;
+ }
+ if (p.pkgList.containsKey(packageName)) {
+ proc = p;
+ }
+ }
+ }
+
+ if (proc == null) {
+ Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+ + " initialPid=" + initialPid
+ + " packageName=" + packageName);
+ return;
+ }
+
+ if (proc.thread != null) {
+ if (proc.pid == Process.myPid()) {
+ Log.w(TAG, "crashApplication: trying to crash self!");
+ return;
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ proc.thread.scheduleCrash(message);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ /**
+ * Bring up the "unexpected error" dialog box for a crashing app.
+ * Deal with edge cases (intercepts from instrumented applications,
+ * ActivityController, error intent receivers, that sort of thing).
+ * @param r the application crashing
+ * @param crashInfo describing the failure
+ */
+ void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+ long timeMillis = System.currentTimeMillis();
+ String shortMsg = crashInfo.exceptionClassName;
+ String longMsg = crashInfo.exceptionMessage;
+ String stackTrace = crashInfo.stackTrace;
+ if (shortMsg != null && longMsg != null) {
+ longMsg = shortMsg + ": " + longMsg;
+ } else if (shortMsg != null) {
+ longMsg = shortMsg;
+ }
+
+ AppErrorResult result = new AppErrorResult();
+ TaskRecord task;
+ synchronized (mService) {
+ if (mService.mController != null) {
+ try {
+ String name = r != null ? r.processName : null;
+ int pid = r != null ? r.pid : Binder.getCallingPid();
+ int uid = r != null ? r.info.uid : Binder.getCallingUid();
+ if (!mService.mController.appCrashed(name, pid,
+ shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
+ if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
+ && "Native crash".equals(crashInfo.exceptionClassName)) {
+ Slog.w(TAG, "Skip killing native crashed app " + name
+ + "(" + pid + ") during testing");
+ } else {
+ Slog.w(TAG, "Force-killing crashed app " + name
+ + " at watcher's request");
+ if (r != null) {
+ r.kill("crash", true);
+ } else {
+ // Huh.
+ Process.killProcess(pid);
+ ActivityManagerService.killProcessGroup(uid, pid);
+ }
+ }
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+
+ // If this process is running instrumentation, finish it.
+ if (r != null && r.instrumentationClass != null) {
+ Slog.w(TAG, "Error in app " + r.processName
+ + " running instrumentation " + r.instrumentationClass + ":");
+ if (shortMsg != null) Slog.w(TAG, " " + shortMsg);
+ if (longMsg != null) Slog.w(TAG, " " + longMsg);
+ Bundle info = new Bundle();
+ info.putString("shortMsg", shortMsg);
+ info.putString("longMsg", longMsg);
+ mService.finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+
+ // Log crash in battery stats.
+ if (r != null) {
+ mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
+ }
+
+ AppErrorDialog.Data data = new AppErrorDialog.Data();
+ data.result = result;
+ data.proc = r;
+
+ // If we can't identify the process or it's already exceeded its crash quota,
+ // quit right away without showing a crash dialog.
+ if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+
+ Message msg = Message.obtain();
+ msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
+
+ task = data.task;
+ msg.obj = data;
+ mService.mUiHandler.sendMessage(msg);
+
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ int res = result.get();
+
+ Intent appErrorIntent = null;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (res == AppErrorDialog.RESET) {
+ String[] packageList = r.getPackageList();
+ if (packageList != null) {
+ PackageManager pm = mContext.getPackageManager();
+ final Semaphore s = new Semaphore(0);
+ for (int i = 0; i < packageList.length; i++) {
+ if (i < packageList.length - 1) {
+ pm.deleteApplicationCacheFiles(packageList[i], null);
+ } else {
+ pm.deleteApplicationCacheFiles(packageList[i],
+ new IPackageDataObserver.Stub() {
+ @Override
+ public void onRemoveCompleted(String packageName,
+ boolean succeeded) {
+ s.release();
+ }
+ });
+
+ // Wait until cache has been cleared before we restart.
+ try {
+ s.acquire();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ // If there was nothing to reset, just restart;
+ res = AppErrorDialog.RESTART;
+ }
+ synchronized (mService) {
+ if (res == AppErrorDialog.MUTE) {
+ stopReportingCrashesLocked(r);
+ }
+ if (res == AppErrorDialog.RESTART) {
+ mService.removeProcessLocked(r, false, true, "crash");
+ if (task != null) {
+ try {
+ mService.startActivityFromRecents(task.taskId,
+ ActivityOptions.makeBasic().toBundle());
+ } catch (IllegalArgumentException e) {
+ // Hmm, that didn't work, app might have crashed before creating a
+ // recents entry. Let's see if we have a safe-to-restart intent.
+ if (task.intent.getCategories().contains(
+ Intent.CATEGORY_LAUNCHER)) {
+ mService.startActivityInPackage(task.mCallingUid,
+ task.mCallingPackage, task.intent,
+ null, null, null, 0, 0,
+ ActivityOptions.makeBasic().toBundle(),
+ task.userId, null, null);
+ }
+ }
+ }
+ }
+ if (res == AppErrorDialog.FORCE_QUIT) {
+ long orig = Binder.clearCallingIdentity();
+ try {
+ // Kill it with fire!
+ mService.mStackSupervisor.handleAppCrashLocked(r);
+ if (!r.persistent) {
+ mService.removeProcessLocked(r, false, false, "crash");
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(orig);
+ }
+ }
+ if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+ appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
+ }
+ if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
+ // XXX Can't keep track of crash time for isolated processes,
+ // since they don't have a persistent identity.
+ mProcessCrashTimes.put(r.info.processName, r.uid,
+ SystemClock.uptimeMillis());
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (appErrorIntent != null) {
+ try {
+ mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
+ } catch (ActivityNotFoundException e) {
+ Slog.w(TAG, "bug report receiver dissappeared", e);
+ }
+ }
+ }
+
+ private boolean makeAppCrashingLocked(ProcessRecord app,
+ String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
+ app.crashing = true;
+ app.crashingReport = generateProcessError(app,
+ ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
+ startAppProblemLocked(app);
+ app.stopFreezingAllLocked();
+ return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
+ data);
+ }
+
+ void startAppProblemLocked(ProcessRecord app) {
+ // If this app is not running under the current user, then we
+ // can't give it a report button because that would require
+ // launching the report UI under a different user.
+ app.errorReportReceiver = null;
+
+ for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ if (app.userId == userId) {
+ app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+ mContext, app.info.packageName, app.info.flags);
+ }
+ }
+ mService.skipCurrentReceiverLocked(app);
+ }
+
+ /**
+ * Generate a process error record, suitable for attachment to a ProcessRecord.
+ *
+ * @param app The ProcessRecord in which the error occurred.
+ * @param condition Crashing, Application Not Responding, etc. Values are defined in
+ * ActivityManager.AppErrorStateInfo
+ * @param activity The activity associated with the crash, if known.
+ * @param shortMsg Short message describing the crash.
+ * @param longMsg Long message describing the crash.
+ * @param stackTrace Full crash stack trace, may be null.
+ *
+ * @return Returns a fully-formed AppErrorStateInfo record.
+ */
+ private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
+ int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
+ ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
+
+ report.condition = condition;
+ report.processName = app.processName;
+ report.pid = app.pid;
+ report.uid = app.info.uid;
+ report.tag = activity;
+ report.shortMsg = shortMsg;
+ report.longMsg = longMsg;
+ report.stackTrace = stackTrace;
+
+ return report;
+ }
+
+ Intent createAppErrorIntentLocked(ProcessRecord r,
+ long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
+ ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
+ if (report == null) {
+ return null;
+ }
+ Intent result = new Intent(Intent.ACTION_APP_ERROR);
+ result.setComponent(r.errorReportReceiver);
+ result.putExtra(Intent.EXTRA_BUG_REPORT, report);
+ result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return result;
+ }
+
+ private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
+ long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
+ if (r.errorReportReceiver == null) {
+ return null;
+ }
+
+ if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
+ return null;
+ }
+
+ ApplicationErrorReport report = new ApplicationErrorReport();
+ report.packageName = r.info.packageName;
+ report.installerPackageName = r.errorReportReceiver.getPackageName();
+ report.processName = r.processName;
+ report.time = timeMillis;
+ report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+
+ if (r.crashing || r.forceCrashReport) {
+ report.type = ApplicationErrorReport.TYPE_CRASH;
+ report.crashInfo = crashInfo;
+ } else if (r.notResponding) {
+ report.type = ApplicationErrorReport.TYPE_ANR;
+ report.anrInfo = new ApplicationErrorReport.AnrInfo();
+
+ report.anrInfo.activity = r.notRespondingReport.tag;
+ report.anrInfo.cause = r.notRespondingReport.shortMsg;
+ report.anrInfo.info = r.notRespondingReport.longMsg;
+ }
+
+ return report;
+ }
+
+ boolean handleAppCrashLocked(ProcessRecord app, String reason,
+ String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
+ long now = SystemClock.uptimeMillis();
+
+ Long crashTime;
+ Long crashTimePersistent;
+ if (!app.isolated) {
+ crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
+ crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
+ } else {
+ crashTime = crashTimePersistent = null;
+ }
+ if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
+ // This process loses!
+ Slog.w(TAG, "Process " + app.info.processName
+ + " has crashed too many times: killing!");
+ EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
+ app.userId, app.info.processName, app.uid);
+ mService.mStackSupervisor.handleAppCrashLocked(app);
+ if (!app.persistent) {
+ // We don't want to start this process again until the user
+ // explicitly does so... but for persistent process, we really
+ // need to keep it running. If a persistent process is actually
+ // repeatedly crashing, then badness for everyone.
+ EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
+ app.info.processName);
+ if (!app.isolated) {
+ // XXX We don't have a way to mark isolated processes
+ // as bad, since they don't have a peristent identity.
+ mBadProcesses.put(app.info.processName, app.uid,
+ new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+ mProcessCrashTimes.remove(app.info.processName, app.uid);
+ }
+ app.bad = true;
+ app.removed = true;
+ // Don't let services in this process be restarted and potentially
+ // annoy the user repeatedly. Unless it is persistent, since those
+ // processes run critical code.
+ mService.removeProcessLocked(app, false, false, "crash");
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ return false;
+ }
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ } else {
+ TaskRecord affectedTask =
+ mService.mStackSupervisor.finishTopRunningActivityLocked(app, reason);
+ if (data != null) {
+ data.task = affectedTask;
+ }
+ if (data != null && crashTimePersistent != null
+ && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
+ data.repeating = true;
+ }
+ }
+
+ // Bump up the crash count of any services currently running in the proc.
+ for (int i=app.services.size()-1; i>=0; i--) {
+ // Any services running in the application need to be placed
+ // back in the pending list.
+ ServiceRecord sr = app.services.valueAt(i);
+ sr.crashCount++;
+ }
+
+ // If the crashing process is what we consider to be the "home process" and it has been
+ // replaced by a third-party app, clear the package preferred activities from packages
+ // with a home activity running in the process to prevent a repeatedly crashing app
+ // from blocking the user to manually clear the list.
+ final ArrayList<ActivityRecord> activities = app.activities;
+ if (app == mService.mHomeProcess && activities.size() > 0
+ && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.isHomeActivity()) {
+ Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+ try {
+ ActivityThread.getPackageManager()
+ .clearPackagePreferredActivities(r.packageName);
+ } catch (RemoteException c) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ }
+ }
+
+ if (!app.isolated) {
+ // XXX Can't keep track of crash times for isolated processes,
+ // because they don't have a perisistent identity.
+ mProcessCrashTimes.put(app.info.processName, app.uid, now);
+ mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
+ }
+
+ if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
+ return true;
+ }
+
+ void handleShowAppErrorUi(Message msg) {
+ AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
+ boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+ synchronized (mService) {
+ ProcessRecord proc = data.proc;
+ AppErrorResult res = data.result;
+ if (proc != null && proc.crashDialog != null) {
+ Slog.e(TAG, "App already has crash dialog: " + proc);
+ if (res != null) {
+ res.set(0);
+ }
+ return;
+ }
+ boolean isBackground = (UserHandle.getAppId(proc.uid)
+ >= Process.FIRST_APPLICATION_UID
+ && proc.pid != MY_PID);
+ for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ isBackground &= (proc.userId != userId);
+ }
+ if (isBackground && !showBackground) {
+ Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
+ if (res != null) {
+ res.set(0);
+ }
+ return;
+ }
+ final boolean crashSilenced = mAppsNotReportingCrashes != null &&
+ mAppsNotReportingCrashes.contains(proc.info.packageName);
+ if (mService.canShowErrorDialogs() && !crashSilenced) {
+ Dialog d = new AppErrorDialog(mContext, mService, data);
+ d.show();
+ proc.crashDialog = d;
+ } else {
+ // The device is asleep, so just pretend that the user
+ // saw a crash dialog and hit "force quit".
+ if (res != null) {
+ res.set(0);
+ }
+ }
+ }
+ }
+
+ void stopReportingCrashesLocked(ProcessRecord proc) {
+ if (mAppsNotReportingCrashes == null) {
+ mAppsNotReportingCrashes = new ArraySet<>();
+ }
+ mAppsNotReportingCrashes.add(proc.info.packageName);
+ }
+
+ final void appNotResponding(ProcessRecord app, ActivityRecord activity,
+ ActivityRecord parent, boolean aboveSystem, final String annotation) {
+ ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
+ SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
+
+ if (mService.mController != null) {
+ try {
+ // 0 == continue, -1 = kill process immediately
+ int res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation);
+ if (res < 0 && app.pid != MY_PID) {
+ app.kill("anr", true);
+ }
+ } catch (RemoteException e) {
+ mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ }
+
+ long anrTime = SystemClock.uptimeMillis();
+ if (ActivityManagerService.MONITOR_CPU_USAGE) {
+ mService.updateCpuStatsNow();
+ }
+
+ synchronized (mService) {
+ // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+ if (mService.mShuttingDown) {
+ Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
+ return;
+ } else if (app.notResponding) {
+ Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
+ return;
+ } else if (app.crashing) {
+ Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
+ return;
+ }
+
+ // In case we come through here for the same app before completing
+ // this one, mark as anring now so we will bail out.
+ app.notResponding = true;
+
+ // Log the ANR to the event log.
+ EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
+ app.processName, app.info.flags, annotation);
+
+ // Dump thread traces as quickly as we can, starting with "interesting" processes.
+ firstPids.add(app.pid);
+
+ int parentPid = app.pid;
+ if (parent != null && parent.app != null && parent.app.pid > 0) {
+ parentPid = parent.app.pid;
+ }
+ if (parentPid != app.pid) firstPids.add(parentPid);
+
+ if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+ for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mService.mLruProcesses.get(i);
+ if (r != null && r.thread != null) {
+ int pid = r.pid;
+ if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
+ if (r.persistent) {
+ firstPids.add(pid);
+ } else {
+ lastPids.put(pid, Boolean.TRUE);
+ }
+ }
+ }
+ }
+ }
+
+ // Log the ANR to the main log.
+ StringBuilder info = new StringBuilder();
+ info.setLength(0);
+ info.append("ANR in ").append(app.processName);
+ if (activity != null && activity.shortComponentName != null) {
+ info.append(" (").append(activity.shortComponentName).append(")");
+ }
+ info.append("\n");
+ info.append("PID: ").append(app.pid).append("\n");
+ if (annotation != null) {
+ info.append("Reason: ").append(annotation).append("\n");
+ }
+ if (parent != null && parent != activity) {
+ info.append("Parent: ").append(parent.shortComponentName).append("\n");
+ }
+
+ final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+ File tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
+ NATIVE_STACKS_OF_INTEREST);
+
+ String cpuInfo = null;
+ if (ActivityManagerService.MONITOR_CPU_USAGE) {
+ mService.updateCpuStatsNow();
+ synchronized (mService.mProcessCpuTracker) {
+ cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
+ }
+ info.append(processCpuTracker.printCurrentLoad());
+ info.append(cpuInfo);
+ }
+
+ info.append(processCpuTracker.printCurrentState(anrTime));
+
+ Slog.e(TAG, info.toString());
+ if (tracesFile == null) {
+ // There is no trace file, so dump (only) the alleged culprit's threads to the log
+ Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
+ }
+
+ mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
+ cpuInfo, tracesFile, null);
+
+ if (mService.mController != null) {
+ try {
+ // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
+ int res = mService.mController.appNotResponding(
+ app.processName, app.pid, info.toString());
+ if (res != 0) {
+ if (res < 0 && app.pid != MY_PID) {
+ app.kill("anr", true);
+ } else {
+ synchronized (mService) {
+ mService.mServices.scheduleServiceTimeoutLocked(app);
+ }
+ }
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ }
+
+ // Unless configured otherwise, swallow ANRs in background processes & kill the process.
+ boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+
+ synchronized (mService) {
+ mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
+
+ if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
+ app.kill("bg anr", true);
+ return;
+ }
+
+ // Set the app's notResponding state, and look up the errorReportReceiver
+ makeAppNotRespondingLocked(app,
+ activity != null ? activity.shortComponentName : null,
+ annotation != null ? "ANR " + annotation : "ANR",
+ info.toString());
+
+ // Bring up the infamous App Not Responding dialog
+ Message msg = Message.obtain();
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+ msg.obj = map;
+ msg.arg1 = aboveSystem ? 1 : 0;
+ map.put("app", app);
+ if (activity != null) {
+ map.put("activity", activity);
+ }
+
+ mService.mUiHandler.sendMessage(msg);
+ }
+ }
+
+ private void makeAppNotRespondingLocked(ProcessRecord app,
+ String activity, String shortMsg, String longMsg) {
+ app.notResponding = true;
+ app.notRespondingReport = generateProcessError(app,
+ ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+ activity, shortMsg, longMsg, null);
+ startAppProblemLocked(app);
+ app.stopFreezingAllLocked();
+ }
+
+ void handleShowAnrUi(Message msg) {
+ synchronized (mService) {
+ HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
+ ProcessRecord proc = (ProcessRecord)data.get("app");
+ if (proc != null && proc.anrDialog != null) {
+ Slog.e(TAG, "App already has anr dialog: " + proc);
+ return;
+ }
+
+ Intent intent = new Intent("android.intent.action.ANR");
+ if (!mService.mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+
+ if (mService.canShowErrorDialogs()) {
+ Dialog d = new AppNotRespondingDialog(mService,
+ mContext, proc, (ActivityRecord)data.get("activity"),
+ msg.arg1 != 0);
+ d.show();
+ proc.anrDialog = d;
+ } else {
+ // Just kill the app if there is no dialog to be shown.
+ mService.killAppAtUsersRequest(proc, null);
+ }
+ }
+ }
+
+ /**
+ * Information about a process that is currently marked as bad.
+ */
+ static final class BadProcessInfo {
+ BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
+ this.time = time;
+ this.shortMsg = shortMsg;
+ this.longMsg = longMsg;
+ this.stack = stack;
+ }
+
+ final long time;
+ final String shortMsg;
+ final String longMsg;
+ final String stack;
+ }
+
+}
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index f4c1664..4587b72 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -117,7 +117,7 @@
ProcessRecord app = mProc;
if (msg.what == WAIT_AND_REPORT) {
- appErrorIntent = mService.createAppErrorIntentLocked(app,
+ appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app,
System.currentTimeMillis(), null);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 622aa16..37b0af1 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -199,7 +199,7 @@
@Override
public void run() {
- mService.appNotResponding(mApp, null, null, false, mAnnotation);
+ mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
}
}
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/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index fda1ec1..6da84bd 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -73,7 +73,6 @@
mHandler.obtainMessage(ACTION_OK_AND_REPORT));
}
- setTitle(res.getText(com.android.internal.R.string.aerr_title));
getWindow().addPrivateFlags(PRIVATE_FLAG_SYSTEM_ERROR);
getWindow().setTitle("Strict Mode Violation: " + app.info.processName);
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2ee74db..7f783ec 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1242,58 +1242,55 @@
}
@Override
- public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
+ public void setPriority(String pkg, int uid, Notification.Topic topic, int priority) {
checkCallerIsSystem();
- mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
+ mRankingHelper.setPriority(pkg, uid, topic, priority);
savePolicyFile();
}
@Override
- public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
+ public int getPriority(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getTopicPriority(pkg, uid, topic);
+ return mRankingHelper.getPriority(pkg, uid, topic);
}
@Override
- public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
+ public void setVisibilityOverride(String pkg, int uid, Notification.Topic topic,
int visibility) {
checkCallerIsSystem();
- mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
+ mRankingHelper.setVisibilityOverride(pkg, uid, topic, visibility);
savePolicyFile();
}
@Override
- public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
+ public int getVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
+ return mRankingHelper.getVisibilityOverride(pkg, uid, topic);
}
@Override
- public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
+ public void setImportance(String pkg, int uid, Notification.Topic topic,
int importance) {
enforceSystemOrSystemUI("Caller not system or systemui");
- if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
- UserHandle.getUserId(uid),
- REASON_TOPIC_BANNED, topic, null);
+ if (topic == null) {
+ // App wide, potentially store block in app ops.
+ setNotificationsEnabledForPackageImpl(pkg, uid,
+ importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
+ } else {
+ if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
+ UserHandle.getUserId(uid),
+ REASON_TOPIC_BANNED, topic, null);
+ }
}
- mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
+ mRankingHelper.setImportance(pkg, uid, topic, importance);
savePolicyFile();
}
@Override
- public int getTopicImportance(String pkg, int uid, Notification.Topic topic) {
+ public int getImportance(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getTopicImportance(pkg, uid, topic);
- }
-
- @Override
- public void setAppImportance(String pkg, int uid, int importance) {
- enforceSystemOrSystemUI("Caller not system or systemui");
- setNotificationsEnabledForPackageImpl(pkg, uid,
- importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
- mRankingHelper.setAppImportance(pkg, uid, importance);
- savePolicyFile();
+ return mRankingHelper.getImportance(pkg, uid, topic);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 490e890..484b0e9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -33,6 +33,7 @@
import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -106,6 +107,7 @@
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
+ stats = new NotificationUsageStats.SingleNotificationStats();
mImportance = defaultImportance();
}
@@ -133,27 +135,22 @@
importance = IMPORTANCE_MAX;
break;
}
+ stats.requestedImportance = importance;
boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
|| (n.defaults & Notification.DEFAULT_VIBRATE) != 0
|| n.sound != null
|| n.vibrate != null;
+ stats.isNoisy = isNoisy;
if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
importance = IMPORTANCE_DEFAULT;
}
- // maybe only do this for target API < N?
- if (isNoisy) {
- if (importance >= IMPORTANCE_HIGH) {
- importance = IMPORTANCE_MAX;
- } else {
- importance = IMPORTANCE_HIGH;
- }
- }
if (n.fullScreenIntent != null) {
importance = IMPORTANCE_MAX;
}
+ stats.naturalImportance = importance;
return importance;
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 1cdc6db..e75324f 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+
import android.app.Notification;
import android.content.ContentValues;
import android.content.Context;
@@ -101,7 +103,6 @@
* Called when a notification has been posted.
*/
public synchronized void registerPostedByApp(NotificationRecord notification) {
- notification.stats = new SingleNotificationStats();
notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
@@ -119,13 +120,16 @@
* Called when a notification has been updated.
*/
public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) {
- notification.stats = old.stats;
+ notification.stats.updateFrom(old.stats);
AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
for (AggregatedStats stats : aggregatedStatsArray) {
stats.numUpdatedByApp++;
stats.countApiUse(notification);
}
releaseAggregatedStatsLocked(aggregatedStatsArray);
+ if (ENABLE_SQLITE_LOG) {
+ mSQLiteLog.logPosted(notification);
+ }
}
/**
@@ -297,10 +301,6 @@
public int numWithActions;
public int numPrivate;
public int numSecret;
- public int numPriorityMax;
- public int numPriorityHigh;
- public int numPriorityLow;
- public int numPriorityMin;
public int numWithBigText;
public int numWithBigPicture;
public int numForegroundService;
@@ -314,11 +314,17 @@
public int numWithSubText;
public int numWithInfoText;
public int numInterrupt;
+ public ImportanceHistogram noisyImportance;
+ public ImportanceHistogram quietImportance;
+ public ImportanceHistogram finalImportance;
public AggregatedStats(Context context, String key) {
this.key = key;
mContext = context;
mCreated = SystemClock.elapsedRealtime();
+ noisyImportance = new ImportanceHistogram(context, "note_imp_noisy_");
+ quietImportance = new ImportanceHistogram(context, "note_imp_quiet_");
+ finalImportance = new ImportanceHistogram(context, "note_importance_");
}
public void countApiUse(NotificationRecord record) {
@@ -354,20 +360,12 @@
break;
}
- switch (n.priority) {
- case Notification.PRIORITY_MAX:
- numPriorityMax++;
- break;
- case Notification.PRIORITY_HIGH:
- numPriorityHigh++;
- break;
- case Notification.PRIORITY_LOW:
- numPriorityLow++;
- break;
- case Notification.PRIORITY_MIN:
- numPriorityMin++;
- break;
+ if (record.stats.isNoisy) {
+ noisyImportance.increment(record.stats.requestedImportance);
+ } else {
+ quietImportance.increment(record.stats.requestedImportance);
}
+ finalImportance.increment(record.getImportance());
final Set<String> names = n.extras.keySet();
if (names.contains(Notification.EXTRA_BIG_TEXT)) {
@@ -419,10 +417,6 @@
maybeCount("note_with_actions", (numWithActions - mPrevious.numWithActions));
maybeCount("note_private", (numPrivate - mPrevious.numPrivate));
maybeCount("note_secret", (numSecret - mPrevious.numSecret));
- maybeCount("note_prio_max", (numPriorityMax - mPrevious.numPriorityMax));
- maybeCount("note_prio_high", (numPriorityHigh - mPrevious.numPriorityHigh));
- maybeCount("note_prio_low", (numPriorityLow - mPrevious.numPriorityLow));
- maybeCount("note_prio_min", (numPriorityMin - mPrevious.numPriorityMin));
maybeCount("note_interupt", (numInterrupt - mPrevious.numInterrupt));
maybeCount("note_big_text", (numWithBigText - mPrevious.numWithBigText));
maybeCount("note_big_pic", (numWithBigPicture - mPrevious.numWithBigPicture));
@@ -436,6 +430,9 @@
maybeCount("note_text", (numWithText - mPrevious.numWithText));
maybeCount("note_sub_text", (numWithSubText - mPrevious.numWithSubText));
maybeCount("note_info_text", (numWithInfoText - mPrevious.numWithInfoText));
+ noisyImportance.maybeCount(mPrevious.noisyImportance);
+ quietImportance.maybeCount(mPrevious.quietImportance);
+ finalImportance.maybeCount(mPrevious.finalImportance);
mPrevious.numPostedByApp = numPostedByApp;
mPrevious.numUpdatedByApp = numUpdatedByApp;
@@ -448,10 +445,6 @@
mPrevious.numWithActions = numWithActions;
mPrevious.numPrivate = numPrivate;
mPrevious.numSecret = numSecret;
- mPrevious.numPriorityMax = numPriorityMax;
- mPrevious.numPriorityHigh = numPriorityHigh;
- mPrevious.numPriorityLow = numPriorityLow;
- mPrevious.numPriorityMin = numPriorityMin;
mPrevious.numInterrupt = numInterrupt;
mPrevious.numWithBigText = numWithBigText;
mPrevious.numWithBigPicture = numWithBigPicture;
@@ -465,6 +458,9 @@
mPrevious.numWithText = numWithText;
mPrevious.numWithSubText = numWithSubText;
mPrevious.numWithInfoText = numWithInfoText;
+ noisyImportance.update(mPrevious.noisyImportance);
+ quietImportance.update(mPrevious.quietImportance);
+ finalImportance.update(mPrevious.finalImportance);
}
void maybeCount(String name, int value) {
@@ -483,17 +479,64 @@
}
private String toStringWithIndent(String indent) {
- return indent + "AggregatedStats{\n" +
- indent + " key='" + key + "',\n" +
- indent + " numPostedByApp=" + numPostedByApp + ",\n" +
- indent + " numUpdatedByApp=" + numUpdatedByApp + ",\n" +
- indent + " numRemovedByApp=" + numRemovedByApp + ",\n" +
- indent + " numPeopleCacheHit=" + numPeopleCacheHit + ",\n" +
- indent + " numWithStaredPeople=" + numWithStaredPeople + ",\n" +
- indent + " numWithValidPeople=" + numWithValidPeople + ",\n" +
- indent + " numPeopleCacheMiss=" + numPeopleCacheMiss + ",\n" +
- indent + " numBlocked=" + numBlocked + ",\n" +
- indent + "}";
+ StringBuilder output = new StringBuilder();
+ output.append(indent).append("AggregatedStats{\n");
+ String indentPlusTwo = indent + " ";
+ output.append(indentPlusTwo);
+ output.append("key='").append(key).append("',\n");
+ output.append(indentPlusTwo);
+ output.append("numPostedByApp=").append(numPostedByApp).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numUpdatedByApp=").append(numUpdatedByApp).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numRemovedByApp=").append(numRemovedByApp).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numPeopleCacheHit=").append(numPeopleCacheHit).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numWithStaredPeople=").append(numWithStaredPeople).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numWithValidPeople=").append(numWithValidPeople).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numPeopleCacheMiss=").append(numPeopleCacheMiss).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numBlocked=").append(numBlocked).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numWithActions=").append(numWithActions).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numPrivate=").append(numPrivate).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numSecret=").append(numSecret).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numInterrupt=").append(numInterrupt).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numWithBigText=").append(numWithBigText).append(",\n");
+ output.append(indentPlusTwo);
+ output.append("numWithBigPicture=").append(numWithBigPicture).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numForegroundService=").append(numForegroundService).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numOngoing=").append(numOngoing).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numAutoCancel=").append(numAutoCancel).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithLargeIcon=").append(numWithLargeIcon).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithInbox=").append(numWithInbox).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithMediaSession=").append(numWithMediaSession).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithTitle=").append(numWithTitle).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithText=").append(numWithText).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithSubText=").append(numWithSubText).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numWithInfoText=").append(numWithInfoText).append("\n");
+ output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
+ output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
+ output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
+ output.append(indent).append("}");
+ return output.toString();
}
public JSONObject dumpJson() throws JSONException {
@@ -511,10 +554,6 @@
maybePut(dump, "numWithActions", numWithActions);
maybePut(dump, "numPrivate", numPrivate);
maybePut(dump, "numSecret", numSecret);
- maybePut(dump, "numPriorityMax", numPriorityMax);
- maybePut(dump, "numPriorityHigh", numPriorityHigh);
- maybePut(dump, "numPriorityLow", numPriorityLow);
- maybePut(dump, "numPriorityMin", numPriorityMin);
maybePut(dump, "numInterrupt", numInterrupt);
maybePut(dump, "numWithBigText", numWithBigText);
maybePut(dump, "numWithBigPicture", numWithBigPicture);
@@ -528,6 +567,10 @@
maybePut(dump, "numWithText", numWithText);
maybePut(dump, "numWithSubText", numWithSubText);
maybePut(dump, "numWithInfoText", numWithInfoText);
+ noisyImportance.maybePut(dump, mPrevious.noisyImportance);
+ quietImportance.maybePut(dump, mPrevious.quietImportance);
+ finalImportance.maybePut(dump, mPrevious.finalImportance);
+
return dump;
}
@@ -538,6 +581,65 @@
}
}
+ private static class ImportanceHistogram {
+ // TODO define these somewhere else
+ private static final int NUM_IMPORTANCES = 5;
+ private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"};
+ private final Context mContext;
+ private final String[] mCounterNames;
+ private final String mPrefix;
+ private int[] mCount;
+
+ ImportanceHistogram(Context context, String prefix) {
+ mContext = context;
+ mCount = new int[NUM_IMPORTANCES];
+ mCounterNames = new String[NUM_IMPORTANCES];
+ mPrefix = prefix;
+ for (int i = 0; i < NUM_IMPORTANCES; i++) {
+ mCounterNames[i] = mPrefix + IMPORTANCE_NAMES[i];
+ }
+ }
+
+ void increment(int imp) {
+ imp = imp < 0 ? 0 : imp > NUM_IMPORTANCES ? NUM_IMPORTANCES : imp;
+ mCount[imp] ++;
+ }
+
+ void maybeCount(ImportanceHistogram prev) {
+ for (int i = 0; i < NUM_IMPORTANCES; i++) {
+ final int value = mCount[i] - prev.mCount[i];
+ if (value > 0) {
+ MetricsLogger.count(mContext, mCounterNames[i], value);
+ }
+ }
+ }
+
+ void update(ImportanceHistogram that) {
+ for (int i = 0; i < NUM_IMPORTANCES; i++) {
+ mCount[i] = that.mCount[i];
+ }
+ }
+
+ public void maybePut(JSONObject dump, ImportanceHistogram prev)
+ throws JSONException {
+ dump.put(mPrefix, new JSONArray(mCount));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder output = new StringBuilder();
+ output.append(mPrefix).append(": [");
+ for (int i = 0; i < NUM_IMPORTANCES; i++) {
+ output.append(mCount[i]);
+ if (i < (NUM_IMPORTANCES-1)) {
+ output.append(", ");
+ }
+ }
+ output.append("]");
+ return output.toString();
+ }
+ }
+
/**
* Tracks usage of an individual notification that is currently active.
*/
@@ -575,6 +677,12 @@
public long airtimeExpandedMs = 0;
/** Number of times the notification has been expanded by the user. */
public long userExpansionCount = 0;
+ /** Importance directly requested by the app. */
+ public int requestedImportance;
+ /** Did the app include sound or vibration on the notificaiton. */
+ public boolean isNoisy;
+ /** Importance after initial filtering for noise and other features */
+ public int naturalImportance;
public long getCurrentPosttimeMs() {
if (posttimeElapsedMs < 0) {
@@ -686,17 +794,40 @@
@Override
public String toString() {
- return "SingleNotificationStats{" +
- "posttimeElapsedMs=" + posttimeElapsedMs +
- ", posttimeToFirstClickMs=" + posttimeToFirstClickMs +
- ", posttimeToDismissMs=" + posttimeToDismissMs +
- ", airtimeCount=" + airtimeCount +
- ", airtimeMs=" + airtimeMs +
- ", currentAirtimeStartElapsedMs=" + currentAirtimeStartElapsedMs +
- ", airtimeExpandedMs=" + airtimeExpandedMs +
- ", posttimeToFirstVisibleExpansionMs=" + posttimeToFirstVisibleExpansionMs +
- ", currentAirtimeExpandedSEMs=" + currentAirtimeExpandedStartElapsedMs +
- '}';
+ StringBuilder output = new StringBuilder();
+ output.append("SingleNotificationStats{");
+
+ output.append("posttimeElapsedMs=").append(posttimeElapsedMs).append(", ");
+ output.append("posttimeToFirstClickMs=").append(posttimeToFirstClickMs).append(", ");
+ output.append("posttimeToDismissMs=").append(posttimeToDismissMs).append(", ");
+ output.append("airtimeCount=").append(airtimeCount).append(", ");
+ output.append("airtimeMs=").append(airtimeMs).append(", ");
+ output.append("currentAirtimeStartElapsedMs=").append(currentAirtimeStartElapsedMs)
+ .append(", ");
+ output.append("airtimeExpandedMs=").append(airtimeExpandedMs).append(", ");
+ output.append("posttimeToFirstVisibleExpansionMs=")
+ .append(posttimeToFirstVisibleExpansionMs).append(", ");
+ output.append("currentAirtimeExpandedStartElapsedMs=")
+ .append(currentAirtimeExpandedStartElapsedMs).append(", ");
+ output.append("requestedImportance=").append(requestedImportance).append(", ");
+ output.append("naturalImportance=").append(naturalImportance).append(", ");
+ output.append("isNoisy=").append(isNoisy);
+ output.append('}');
+ return output.toString();
+ }
+
+ /** Copy useful information out of the stats from the pre-update notifications. */
+ public void updateFrom(SingleNotificationStats old) {
+ posttimeElapsedMs = old.posttimeElapsedMs;
+ posttimeToFirstClickMs = old.posttimeToFirstClickMs;
+ airtimeCount = old.airtimeCount;
+ posttimeToFirstAirtimeMs = old.posttimeToFirstAirtimeMs;
+ currentAirtimeStartElapsedMs = old.currentAirtimeStartElapsedMs;
+ airtimeMs = old.airtimeMs;
+ posttimeToFirstVisibleExpansionMs = old.posttimeToFirstVisibleExpansionMs;
+ currentAirtimeExpandedStartElapsedMs = old.currentAirtimeExpandedStartElapsedMs;
+ airtimeExpandedMs = old.airtimeExpandedMs;
+ userExpansionCount = old.userExpansionCount;
}
}
@@ -741,7 +872,7 @@
private static final int MSG_DISMISS = 4;
private static final String DB_NAME = "notification_log.db";
- private static final int DB_VERSION = 4;
+ private static final int DB_VERSION = 5;
/** Age in ms after which events are pruned from the DB. */
private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week
@@ -762,7 +893,11 @@
private static final String COL_WHEN_MS = "when_ms";
private static final String COL_DEFAULTS = "defaults";
private static final String COL_FLAGS = "flags";
- private static final String COL_PRIORITY = "priority";
+ private static final String COL_IMPORTANCE_REQ = "importance_request";
+ private static final String COL_IMPORTANCE_FINAL = "importance_final";
+ private static final String COL_NOISY = "noisy";
+ private static final String COL_MUTED = "muted";
+ private static final String COL_DEMOTED = "demoted";
private static final String COL_CATEGORY = "category";
private static final String COL_ACTION_COUNT = "action_count";
private static final String COL_POSTTIME_MS = "posttime_ms";
@@ -776,14 +911,28 @@
private static final int EVENT_TYPE_CLICK = 2;
private static final int EVENT_TYPE_REMOVE = 3;
private static final int EVENT_TYPE_DISMISS = 4;
-
private static long sLastPruneMs;
+
private static long sNumWrites;
-
private final SQLiteOpenHelper mHelper;
- private final Handler mWriteHandler;
+ private final Handler mWriteHandler;
private static final long DAY_MS = 24 * 60 * 60 * 1000;
+ private static final String STATS_QUERY = "SELECT " +
+ COL_EVENT_USER_ID + ", " +
+ COL_PKG + ", " +
+ // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
+ "CAST(((%d - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
+ "AS day, " +
+ "COUNT(*) AS cnt, " +
+ "SUM(" + COL_MUTED + ") as muted, " +
+ "SUM(" + COL_NOISY + ") as noisy, " +
+ "SUM(" + COL_DEMOTED + ") as demoted " +
+ "FROM " + TAB_LOG + " " +
+ "WHERE " +
+ COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
+ " AND " + COL_EVENT_TIME + " > %d " +
+ " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
public SQLiteLog(Context context) {
HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
@@ -828,7 +977,11 @@
COL_WHEN_MS + " INT," +
COL_DEFAULTS + " INT," +
COL_FLAGS + " INT," +
- COL_PRIORITY + " INT," +
+ COL_IMPORTANCE_REQ + " INT," +
+ COL_IMPORTANCE_FINAL + " INT," +
+ COL_NOISY + " INT," +
+ COL_MUTED + " INT," +
+ COL_DEMOTED + " INT," +
COL_CATEGORY + " TEXT," +
COL_ACTION_COUNT + " INT," +
COL_POSTTIME_MS + " INT," +
@@ -841,8 +994,7 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion <= 3) {
- // Version 3 creation left 'log' in a weird state. Just reset for now.
+ if (oldVersion != newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
onCreate(db);
}
@@ -866,22 +1018,11 @@
mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
}
- private JSONArray JsonPostFrequencies(DumpFilter filter) throws JSONException {
+ private JSONArray jsonPostFrequencies(DumpFilter filter) throws JSONException {
JSONArray frequencies = new JSONArray();
SQLiteDatabase db = mHelper.getReadableDatabase();
long midnight = getMidnightMs();
- String q = "SELECT " +
- COL_EVENT_USER_ID + ", " +
- COL_PKG + ", " +
- // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
- "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
- "AS day, " +
- "COUNT(*) AS cnt " +
- "FROM " + TAB_LOG + " " +
- "WHERE " +
- COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
- " AND " + COL_EVENT_TIME + " > " + filter.since +
- " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+ String q = String.format(STATS_QUERY, midnight, filter.since);
Cursor cursor = db.rawQuery(q, null);
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
@@ -890,11 +1031,17 @@
if (filter != null && !filter.matches(pkg)) continue;
int day = cursor.getInt(2);
int count = cursor.getInt(3);
+ int muted = cursor.getInt(4);
+ int noisy = cursor.getInt(5);
+ int demoted = cursor.getInt(6);
JSONObject row = new JSONObject();
row.put("user_id", userId);
row.put("package", pkg);
row.put("day", day);
row.put("count", count);
+ row.put("noisy", noisy);
+ row.put("muted", muted);
+ row.put("demoted", demoted);
frequencies.put(row);
}
} finally {
@@ -906,17 +1053,7 @@
public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) {
SQLiteDatabase db = mHelper.getReadableDatabase();
long midnight = getMidnightMs();
- String q = "SELECT " +
- COL_EVENT_USER_ID + ", " +
- COL_PKG + ", " +
- // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
- "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
- "AS day, " +
- "COUNT(*) AS cnt " +
- "FROM " + TAB_LOG + " " +
- "WHERE " +
- COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " +
- "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+ String q = String.format(STATS_QUERY, midnight, filter.since);
Cursor cursor = db.rawQuery(q, null);
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
@@ -925,8 +1062,12 @@
if (filter != null && !filter.matches(pkg)) continue;
int day = cursor.getInt(2);
int count = cursor.getInt(3);
+ int muted = cursor.getInt(4);
+ int noisy = cursor.getInt(5);
+ int demoted = cursor.getInt(6);
pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
- ",day=" + day + ",count=" + count + "}");
+ ",day=" + day + ",count=" + count + ",muted=" + muted + "/" + noisy +
+ ",demoted=" + demoted + "}");
}
} finally {
cursor.close();
@@ -985,7 +1126,18 @@
}
outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
outCv.put(COL_FLAGS, r.getNotification().flags);
- outCv.put(COL_PRIORITY, r.getNotification().priority);
+ final int before = r.stats.requestedImportance;
+ final int after = r.getImportance();
+ final boolean noisy = r.stats.isNoisy;
+ outCv.put(COL_IMPORTANCE_REQ, before);
+ outCv.put(COL_IMPORTANCE_FINAL, after);
+ outCv.put(COL_DEMOTED, after < before ? 1 : 0);
+ outCv.put(COL_NOISY, noisy);
+ if (noisy && after < IMPORTANCE_HIGH) {
+ outCv.put(COL_MUTED, 1);
+ } else {
+ outCv.put(COL_MUTED, 0);
+ }
if (r.getNotification().category != null) {
outCv.put(COL_CATEGORY, r.getNotification().category);
}
@@ -1008,7 +1160,9 @@
public JSONObject dumpJson(DumpFilter filter) {
JSONObject dump = new JSONObject();
try {
- dump.put("post_frequency", JsonPostFrequencies(filter));
+ dump.put("post_frequency", jsonPostFrequencies(filter));
+ dump.put("since", filter.since);
+ dump.put("now", System.currentTimeMillis());
} catch (JSONException e) {
// pass
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 9b10ef2..7f85e1f 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -23,20 +23,18 @@
List<Notification.Topic> getTopics(String packageName, int uid);
- int getTopicPriority(String packageName, int uid, Notification.Topic topic);
+ int getPriority(String packageName, int uid, Notification.Topic topic);
- void setTopicPriority(String packageName, int uid, Notification.Topic topic, int priority);
+ void setPriority(String packageName, int uid, Notification.Topic topic, int priority);
- int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic);
+ int getVisibilityOverride(String packageName, int uid, Notification.Topic topic);
- void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic,
+ void setVisibilityOverride(String packageName, int uid, Notification.Topic topic,
int visibility);
- void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
+ void setImportance(String packageName, int uid, Notification.Topic topic, int importance);
- int getTopicImportance(String packageName, int uid, Notification.Topic topic);
-
- void setAppImportance(String packageName, int uid, int importance);
+ int getImportance(String packageName, int uid, Notification.Topic topic);
boolean doesAppUseTopics(String packageName, int uid);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index ce4ecd3..827482f 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -164,6 +164,8 @@
r = getOrCreateRecord(name, uid);
}
r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+ r.priority = priority;
+ r.visibility = vis;
// Migrate package level settings to the default topic.
// Might be overwritten by parseTopics.
@@ -245,7 +247,15 @@
}
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
- out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+ }
if (!forBackup) {
out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -373,66 +383,109 @@
public List<Notification.Topic> getTopics(String packageName, int uid) {
final Record r = getOrCreateRecord(packageName, uid);
List<Notification.Topic> topics = new ArrayList<>();
- for (Topic t : r.topics.values()) {
+ for (Topic t : r.topics.values()) {
topics.add(t.topic);
}
return topics;
}
+ /**
+ * Gets priority. If a topic is given, returns the priority of that topic. Otherwise, the
+ * priority of the app.
+ */
@Override
- public int getTopicPriority(String packageName, int uid, Notification.Topic topic) {
+ public int getPriority(String packageName, int uid, Notification.Topic topic) {
final Record r = getOrCreateRecord(packageName, uid);
+ if (topic == null) {
+ return r.priority;
+ }
return getOrCreateTopic(r, topic).priority;
}
+ /**
+ * Sets priority. If a topic is given, sets the priority of that topic. If not,
+ * sets the default priority for all new topics that appear in the future, and resets
+ * the priority of all current topics.
+ */
@Override
- public void setTopicPriority(String packageName, int uid, Notification.Topic topic,
+ public void setPriority(String packageName, int uid, Notification.Topic topic,
int priority) {
final Record r = getOrCreateRecord(packageName, uid);
- getOrCreateTopic(r, topic).priority = priority;
- updateConfig();
- }
-
- @Override
- public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
- final Record r = getOrCreateRecord(packageName, uid);
- return getOrCreateTopic(r, topic).visibility;
- }
-
- @Override
- public void setTopicVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
- int visibility) {
- final Record r = getOrCreateRecord(pkgName, uid);
- getOrCreateTopic(r, topic).visibility = visibility;
- updateConfig();
- }
-
- @Override
- public int getTopicImportance(String packageName, int uid, Notification.Topic topic) {
- final Record r = getOrCreateRecord(packageName, uid);
- return getOrCreateTopic(r, topic).importance;
- }
-
- @Override
- public void setTopicImportance(String pkgName, int uid, Notification.Topic topic,
- int importance) {
- final Record r = getOrCreateRecord(pkgName, uid);
- getOrCreateTopic(r, topic).importance = importance;
+ if (topic == null) {
+ r.priority = priority;
+ for (Topic t : r.topics.values()) {
+ t.priority = priority;
+ }
+ } else {
+ getOrCreateTopic(r, topic).priority = priority;
+ }
updateConfig();
}
/**
- * Sets the default importance for all new topics that appear in the future, and resets
+ * Gets visual override. If a topic is given, returns the override of that topic. Otherwise, the
+ * override of the app.
+ */
+ @Override
+ public int getVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ if (topic == null) {
+ return r.visibility;
+ }
+ return getOrCreateTopic(r, topic).visibility;
+ }
+
+ /**
+ * Sets visibility override. If a topic is given, sets the override of that topic. If not,
+ * sets the default override for all new topics that appear in the future, and resets
+ * the override of all current topics.
+ */
+ @Override
+ public void setVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
+ int visibility) {
+ final Record r = getOrCreateRecord(pkgName, uid);
+ if (topic == null) {
+ r.visibility = visibility;
+ for (Topic t : r.topics.values()) {
+ t.visibility = visibility;
+ }
+ } else {
+ getOrCreateTopic(r, topic).visibility = visibility;
+ }
+ updateConfig();
+ }
+
+ /**
+ * Gets importance. If a topic is given, returns the importance of that topic. Otherwise, the
+ * importance of the app.
+ */
+ @Override
+ public int getImportance(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ if (topic == null) {
+ return r.importance;
+ }
+ return getOrCreateTopic(r, topic).importance;
+ }
+
+ /**
+ * Sets importance. If a topic is given, sets the importance of that topic. If not, sets the
+ * default importance for all new topics that appear in the future, and resets
* the importance of all current topics (unless the app is being blocked).
*/
@Override
- public void setAppImportance(String pkgName, int uid, int importance) {
+ public void setImportance(String pkgName, int uid, Notification.Topic topic,
+ int importance) {
final Record r = getOrCreateRecord(pkgName, uid);
- r.importance = importance;
- if (Ranking.IMPORTANCE_NONE != importance) {
- for (Topic t : r.topics.values()) {
- t.importance = importance;
+ if (topic == null) {
+ r.importance = importance;
+ if (Ranking.IMPORTANCE_NONE != importance) {
+ for (Topic t : r.topics.values()) {
+ t.importance = importance;
+ }
}
+ } else {
+ getOrCreateTopic(r, topic).importance = importance;
}
updateConfig();
}
@@ -459,6 +512,8 @@
} else {
t = new Topic(topic);
t.importance = r.importance;
+ t.priority = r.priority;
+ t.visibility = r.visibility;
r.topics.put(topic.getId(), t);
return t;
}
@@ -503,8 +558,18 @@
pw.print(" (");
pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
- pw.print(" importance=");
- pw.print(Ranking.importanceToString(r.importance));
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ pw.print(" importance=");
+ pw.print(Ranking.importanceToString(r.importance));
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ pw.print(" priority=");
+ pw.print(Ranking.importanceToString(r.priority));
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ pw.print(" visibility=");
+ pw.print(Ranking.importanceToString(r.visibility));
+ }
pw.println();
for (Topic t : r.topics.values()) {
pw.print(prefix);
@@ -561,6 +626,8 @@
String pkg;
int uid = UNKNOWN_UID;
int importance = DEFAULT_IMPORTANCE;
+ int priority = DEFAULT_PRIORITY;
+ int visibility = DEFAULT_VISIBILITY;
Map<String, Topic> topics = new ArrayMap<>();
}
diff --git a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
index 01770d0..c6b3e0f 100644
--- a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
@@ -42,7 +42,7 @@
return null;
}
- final int topicImportance = mConfig.getTopicImportance(record.sbn.getPackageName(),
+ final int topicImportance = mConfig.getImportance(record.sbn.getPackageName(),
record.sbn.getUid(), record.sbn.getNotification().getTopic());
record.setTopicImportance(topicImportance);
diff --git a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
index 5bf989ae..1df5c2b 100644
--- a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
@@ -42,7 +42,7 @@
return null;
}
- final int packagePriority = mConfig.getTopicPriority(record.sbn.getPackageName(),
+ final int packagePriority = mConfig.getPriority(record.sbn.getPackageName(),
record.sbn.getUid(), record.sbn.getNotification().getTopic());
record.setPackagePriority(packagePriority);
diff --git a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
index e053382..eaa3ed3 100644
--- a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
@@ -42,7 +42,7 @@
return null;
}
- final int packageVisibility = mConfig.getTopicVisibilityOverride(
+ final int packageVisibility = mConfig.getVisibilityOverride(
record.sbn.getPackageName(), record.sbn.getUid(),
record.sbn.getNotification().getTopic());
record.setPackageVisibilityOverride(packageVisibility);
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..04820a1 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/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 5abb6e7..e6f4177 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -20,6 +20,7 @@
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.content.ComponentName;
@@ -47,11 +48,11 @@
import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import java.io.FileDescriptor;
@@ -108,6 +109,10 @@
@Override
public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
PrintAttributes attributes, String packageName, int appId, int userId) {
+ printJobName = Preconditions.checkStringNotEmpty(printJobName);
+ adapter = Preconditions.checkNotNull(adapter);
+ packageName = Preconditions.checkStringNotEmpty(packageName);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
final UserState userState;
@@ -153,6 +158,10 @@
@Override
public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
+ if (printJobId == null) {
+ return null;
+ }
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
final UserState userState;
@@ -174,6 +183,8 @@
@Override
public Icon getCustomPrinterIcon(PrinterId printerId, int userId) {
+ printerId = Preconditions.checkNotNull(printerId);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -193,6 +204,10 @@
@Override
public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
+ if (printJobId == null) {
+ return;
+ }
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
final UserState userState;
@@ -214,6 +229,10 @@
@Override
public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
+ if (printJobId == null) {
+ return;
+ }
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
final UserState userState;
@@ -279,6 +298,8 @@
@Override
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
int userId) {
+ observer = Preconditions.checkNotNull(observer);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -299,6 +320,8 @@
@Override
public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
int userId) {
+ observer = Preconditions.checkNotNull(observer);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -319,6 +342,12 @@
@Override
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
List<PrinterId> priorityList, int userId) {
+ observer = Preconditions.checkNotNull(observer);
+ if (priorityList != null) {
+ priorityList = Preconditions.checkCollectionElementsNotNull(priorityList,
+ "PrinterId");
+ }
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -338,6 +367,8 @@
@Override
public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
+ observer = Preconditions.checkNotNull(observer);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -357,6 +388,8 @@
@Override
public void validatePrinters(List<PrinterId> printerIds, int userId) {
+ printerIds = Preconditions.checkCollectionElementsNotNull(printerIds, "PrinterId");
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -376,6 +409,8 @@
@Override
public void startPrinterStateTracking(PrinterId printerId, int userId) {
+ printerId = Preconditions.checkNotNull(printerId);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -395,6 +430,8 @@
@Override
public void stopPrinterStateTracking(PrinterId printerId, int userId) {
+ printerId = Preconditions.checkNotNull(printerId);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -415,6 +452,8 @@
@Override
public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
int appId, int userId) throws RemoteException {
+ listener = Preconditions.checkNotNull(listener);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
final UserState userState;
@@ -437,6 +476,8 @@
@Override
public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
int userId) {
+ listener = Preconditions.checkNotNull(listener);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -456,6 +497,9 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ fd = Preconditions.checkNotNull(fd);
+ pw = Preconditions.checkNotNull(pw);
+
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump PrintManager from from pid="
@@ -707,10 +751,8 @@
return userId;
}
- private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
- if (TextUtils.isEmpty(packageName)) {
- return null;
- }
+ private @NonNull String resolveCallingPackageNameEnforcingSecurity(
+ @NonNull String packageName) {
String[] packages = mContext.getPackageManager().getPackagesForUid(
Binder.getCallingUid());
final int packageCount = packages.length;
@@ -719,7 +761,7 @@
return packageName;
}
}
- return null;
+ throw new IllegalArgumentException("packageName has to belong to the caller");
}
private int getCurrentUserId () {
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 5ee4066..9b99c67 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -409,7 +409,7 @@
}
}
- public void startPrinterStateTracking(PrinterId printerId) {
+ public void startPrinterStateTracking(@NonNull PrinterId printerId) {
mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
printerId).sendToTarget();
}
@@ -420,7 +420,7 @@
* @param printerId the id of the printer the icon should be loaded for
* @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
*/
- public void requestCustomPrinterIcon(PrinterId printerId) {
+ public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
try {
if (isBound()) {
mPrintService.requestCustomPrinterIcon(printerId);
@@ -430,7 +430,7 @@
}
}
- private void handleStartPrinterStateTracking(final PrinterId printerId) {
+ private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) {
throwIfDestroyed();
// Take a note we are tracking the printer.
if (mTrackedPrinterList == null) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 78edc4d..fcf2fc8 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -20,6 +20,7 @@
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -176,8 +177,8 @@
}
@SuppressWarnings("deprecation")
- public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
- PrintAttributes attributes, String packageName, int appId) {
+ public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter,
+ @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) {
// Create print job place holder.
final PrintJobInfo printJob = new PrintJobInfo();
printJob.setId(new PrintJobId());
@@ -267,7 +268,7 @@
return new ArrayList<PrintJobInfo>(result.values());
}
- public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
+ public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) {
PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
if (printJob == null) {
printJob = mSpooler.getPrintJobInfo(printJobId, appId);
@@ -290,7 +291,7 @@
* not yet available
* @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
*/
- public Icon getCustomPrinterIcon(PrinterId printerId) {
+ public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) {
Icon icon = mSpooler.getCustomPrinterIcon(printerId);
if (icon == null) {
@@ -303,7 +304,7 @@
return icon;
}
- public void cancelPrintJob(PrintJobId printJobId, int appId) {
+ public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) {
PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId);
if (printJobInfo == null) {
return;
@@ -313,15 +314,19 @@
mSpooler.setPrintJobCancelling(printJobId, true);
if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
- ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
- RemotePrintService printService = null;
- synchronized (mLock) {
- printService = mActiveServices.get(printServiceName);
+ PrinterId printerId = printJobInfo.getPrinterId();
+
+ if (printerId != null) {
+ ComponentName printServiceName = printerId.getServiceName();
+ RemotePrintService printService = null;
+ synchronized (mLock) {
+ printService = mActiveServices.get(printServiceName);
+ }
+ if (printService == null) {
+ return;
+ }
+ printService.onRequestCancelPrintJob(printJobInfo);
}
- if (printService == null) {
- return;
- }
- printService.onRequestCancelPrintJob(printJobInfo);
} else {
// If the print job is failed we do not need cooperation
// from the print service.
@@ -329,7 +334,7 @@
}
}
- public void restartPrintJob(PrintJobId printJobId, int appId) {
+ public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) {
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
return;
@@ -363,7 +368,7 @@
}
}
- public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
+ public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -386,7 +391,7 @@
}
}
- public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
+ public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
// Already destroyed - nothing to do.
if (mPrinterDiscoverySession == null) {
@@ -397,8 +402,8 @@
}
}
- public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
- List<PrinterId> printerIds) {
+ public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer,
+ @Nullable List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -415,7 +420,7 @@
}
}
- public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -431,7 +436,7 @@
}
}
- public void validatePrinters(List<PrinterId> printerIds) {
+ public void validatePrinters(@NonNull List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -447,7 +452,7 @@
}
}
- public void startPrinterStateTracking(PrinterId printerId) {
+ public void startPrinterStateTracking(@NonNull PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -479,7 +484,7 @@
}
}
- public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
+ public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
int appId) throws RemoteException {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -497,7 +502,7 @@
}
}
- public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) {
+ public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) {
synchronized (mLock) {
throwIfDestroyedLocked();
if (mPrintJobStateChangeListenerRecords == null) {
@@ -613,7 +618,7 @@
mDestroyed = true;
}
- public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
pw.println();
@@ -991,10 +996,10 @@
}
private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
- final IPrintJobStateChangeListener listener;
+ @NonNull final IPrintJobStateChangeListener listener;
final int appId;
- public PrintJobStateChangeListenerRecord(IPrintJobStateChangeListener listener,
+ public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener,
int appId) throws RemoteException {
this.listener = listener;
this.appId = appId;
@@ -1043,7 +1048,7 @@
.sendToTarget();
}
- public void addObserverLocked(IPrinterDiscoveryObserver observer) {
+ public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
// Add the observer.
mDiscoveryObservers.register(observer);
@@ -1058,7 +1063,7 @@
}
}
- public void removeObserverLocked(IPrinterDiscoveryObserver observer) {
+ public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
// Remove the observer.
mDiscoveryObservers.unregister(observer);
// No one else observing - then kill it.
@@ -1067,8 +1072,8 @@
}
}
- public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer,
- List<PrinterId> priorityList) {
+ public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer,
+ @Nullable List<PrinterId> priorityList) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not starting dicovery - session destroyed");
return;
@@ -1101,7 +1106,7 @@
.sendToTarget();
}
- public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) {
+ public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not stopping dicovery - session destroyed");
return;
@@ -1121,7 +1126,7 @@
.sendToTarget();
}
- public void validatePrintersLocked(List<PrinterId> printerIds) {
+ public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not validating pritners - session destroyed");
return;
@@ -1135,13 +1140,15 @@
ComponentName serviceName = null;
while (iterator.hasNext()) {
PrinterId printerId = iterator.next();
- if (updateList.isEmpty()) {
- updateList.add(printerId);
- serviceName = printerId.getServiceName();
- iterator.remove();
- } else if (printerId.getServiceName().equals(serviceName)) {
- updateList.add(printerId);
- iterator.remove();
+ if (printerId != null) {
+ if (updateList.isEmpty()) {
+ updateList.add(printerId);
+ serviceName = printerId.getServiceName();
+ iterator.remove();
+ } else if (printerId.getServiceName().equals(serviceName)) {
+ updateList.add(printerId);
+ iterator.remove();
+ }
}
}
// Schedule a notification of the service.
@@ -1157,7 +1164,7 @@
}
}
- public final void startPrinterStateTrackingLocked(PrinterId printerId) {
+ public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
return;
@@ -1500,8 +1507,8 @@
service.validatePrinters(printerIds);
}
- private void handleStartPrinterStateTracking(RemotePrintService service,
- PrinterId printerId) {
+ private void handleStartPrinterStateTracking(@NonNull RemotePrintService service,
+ @NonNull PrinterId printerId) {
service.startPrinterStateTracking(printerId);
}
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/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index 2640889..31182fc 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -155,16 +155,12 @@
@SmallTest
public void testTopicImportanceExtractor() throws Exception {
- mHelper.setTopicImportance("package", 0, new Notification.Topic("A", "a"),
- IMPORTANCE_MAX);
+ mHelper.setImportance("package", 0, new Notification.Topic("A", "a"), IMPORTANCE_MAX);
// There is no B. There never was a b. Moving on...
- mHelper.setTopicImportance("package", 0, new Notification.Topic("C", "c"),
- IMPORTANCE_HIGH);
- mHelper.setTopicImportance("package", 0, new Notification.Topic("D", "d"),
- IMPORTANCE_LOW);
+ mHelper.setImportance("package", 0, new Notification.Topic("C", "c"), IMPORTANCE_HIGH);
+ mHelper.setImportance("package", 0, new Notification.Topic("D", "d"), IMPORTANCE_LOW);
// watch out: different package.
- mHelper.setTopicImportance("package2", 0, new Notification.Topic("E", "e"),
- IMPORTANCE_NONE);
+ mHelper.setImportance("package2", 0, new Notification.Topic("E", "e"), IMPORTANCE_NONE);
TopicImportanceExtractor validator = mHelper.findExtractor(TopicImportanceExtractor.class);
validator.process(mRecordGroupGSortA);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index aa95e1d..3859294 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -24,7 +24,6 @@
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -210,7 +209,22 @@
* Call sends responses through connection.
* @hide
*/
- public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+ public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
+
+ /**
+ * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
+ * <p>
+ * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+ * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+ * downgraded from a video call back to a VideoState of
+ * {@link VideoProfile#STATE_AUDIO_ONLY}.
+ * <p>
+ * Intuitively, a call which can be downgraded to audio should also have local and remote
+ * video
+ * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+ * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+ */
+ public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
//******************************************************************************************
// Next CAPABILITY value: 0x00800000
@@ -332,6 +346,9 @@
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
}
+ if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+ builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+ }
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index deb98f4..fa7a59d 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -255,8 +255,23 @@
*/
public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+ /**
+ * When set, prevents a video call from being downgraded to an audio-only call.
+ * <p>
+ * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+ * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+ * downgraded from a video call back to a VideoState of
+ * {@link VideoProfile#STATE_AUDIO_ONLY}.
+ * <p>
+ * Intuitively, a call which can be downgraded to audio should also have local and remote
+ * video
+ * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+ * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+ */
+ public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x00800000
+ // Next CAPABILITY value: 0x01000000
//**********************************************************************************************
/**
@@ -371,6 +386,9 @@
if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
}
+ if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+ builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+ }
if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 320d274..630dacc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -746,7 +746,7 @@
*/
@Nullable
public PersistableBundle getConfig() {
- return getConfigForSubId(SubscriptionManager.getDefaultSubId());
+ return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
}
/**
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
index 96069213..f71f58d 100644
--- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,10 @@
public class DataConnectionRealTimeInfo implements Parcelable {
private long mTime; // Time the info was collected since boot in nanos;
- public static int DC_POWER_STATE_LOW = 1;
- public static int DC_POWER_STATE_MEDIUM = 2;
- public static int DC_POWER_STATE_HIGH = 3;
- public static int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE;
+ public static final int DC_POWER_STATE_LOW = 1;
+ public static final int DC_POWER_STATE_MEDIUM = 2;
+ public static final int DC_POWER_STATE_HIGH = 3;
+ public static final int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE;
private int mDcPowerState; // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 553221d..b089387 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2082,7 +2082,7 @@
* to read the VM number.
*/
public static boolean isVoiceMailNumber(String number) {
- return isVoiceMailNumber(SubscriptionManager.getDefaultSubId(), number);
+ return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
}
/**
@@ -2977,7 +2977,7 @@
* Returns Default voice subscription Id.
*/
private static int getDefaultVoiceSubId() {
- return SubscriptionManager.getDefaultVoiceSubId();
+ return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
//==== End of utility methods used only in compareStrictly() =====
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 16472c8..ae130d4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -194,10 +194,12 @@
* {@more}
* Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE}
- *
* @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+ *
+ * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
* @hide
*/
+ @Deprecated
public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 0x00002000;
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9998937..ff8c71c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
private static final boolean VDBG = false;
/** An invalid subscription identifier */
- /** @hide */
public static final int INVALID_SUBSCRIPTION_ID = -1;
/** Base value for Dummy SUBSCRIPTION_ID's. */
@@ -455,8 +454,9 @@
}
/**
- * Get the active SubscriptionInfo with the subId key
- * @param subId The unique SubscriptionInfo key in database
+ * Get the active SubscriptionInfo with the input subId.
+ *
+ * @param subId The unique SubscriptionInfo key in database.
* @return SubscriptionInfo, maybe null if its not active.
*/
public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
@@ -898,12 +898,15 @@
}
/**
- * @return the "system" defaultSubId on a voice capable device this
- * will be getDefaultVoiceSubId() and on a data only device it will be
- * getDefaultDataSubId().
- * @hide
+ * Returns the system's default subscription id.
+ *
+ * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
+ * For a data only device, it will return the getDefaultDataSubscriptionId.
+ * May return an INVALID_SUBSCRIPTION_ID on error.
+ *
+ * @return the "system" default subscription id.
*/
- public static int getDefaultSubId() {
+ public static int getDefaultSubscriptionId() {
int subId = INVALID_SUBSCRIPTION_ID;
try {
@@ -919,8 +922,14 @@
return subId;
}
- /** @hide */
- public static int getDefaultVoiceSubId() {
+ /**
+ * Returns the system's default voice subscription id.
+ *
+ * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default voice subscription Id.
+ */
+ public static int getDefaultVoiceSubscriptionId() {
int subId = INVALID_SUBSCRIPTION_ID;
try {
@@ -932,7 +941,7 @@
// ignore it
}
- if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
return subId;
}
@@ -949,23 +958,31 @@
}
}
- /** @hide */
+ /**
+ * Return the SubscriptionInfo for default voice subscription.
+ *
+ * Will return null on data only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default voice subscription.
+ * @hide
+ */
public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
- return getActiveSubscriptionInfo(getDefaultVoiceSubId());
+ return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
}
/** @hide */
public static int getDefaultVoicePhoneId() {
- return getPhoneId(getDefaultVoiceSubId());
+ return getPhoneId(getDefaultVoiceSubscriptionId());
}
/**
- * @return subId of the DefaultSms subscription or
- * a value < 0 if an error.
+ * Returns the system's default SMS subscription id.
*
- * @hide
+ * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default SMS subscription Id.
*/
- public static int getDefaultSmsSubId() {
+ public static int getDefaultSmsSubscriptionId() {
int subId = INVALID_SUBSCRIPTION_ID;
try {
@@ -977,7 +994,7 @@
// ignore it
}
- if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
return subId;
}
@@ -994,18 +1011,31 @@
}
}
- /** @hide */
+ /**
+ * Return the SubscriptionInfo for default voice subscription.
+ *
+ * Will return null on data only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default SMS subscription.
+ * @hide
+ */
public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
- return getActiveSubscriptionInfo(getDefaultSmsSubId());
+ return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
}
/** @hide */
public int getDefaultSmsPhoneId() {
- return getPhoneId(getDefaultSmsSubId());
+ return getPhoneId(getDefaultSmsSubscriptionId());
}
- /** @hide */
- public static int getDefaultDataSubId() {
+ /**
+ * Returns the system's default data subscription id.
+ *
+ * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default data subscription Id.
+ */
+ public static int getDefaultDataSubscriptionId() {
int subId = INVALID_SUBSCRIPTION_ID;
try {
@@ -1017,7 +1047,7 @@
// ignore it
}
- if (VDBG) logd("getDefaultDataSubId, sub id = " + subId);
+ if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
return subId;
}
@@ -1034,14 +1064,21 @@
}
}
- /** @hide */
+ /**
+ * Return the SubscriptionInfo for default data subscription.
+ *
+ * Will return null on voice only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default data subscription.
+ * @hide
+ */
public SubscriptionInfo getDefaultDataSubscriptionInfo() {
- return getActiveSubscriptionInfo(getDefaultDataSubId());
+ return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
}
/** @hide */
public int getDefaultDataPhoneId() {
- return getPhoneId(getDefaultDataSubId());
+ return getPhoneId(getDefaultDataSubscriptionId());
}
/** @hide */
@@ -1061,13 +1098,13 @@
//FIXME this is vulnerable to race conditions
/** @hide */
public boolean allDefaultsSelected() {
- if (!isValidSubscriptionId(getDefaultDataSubId())) {
+ if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
return false;
}
- if (!isValidSubscriptionId(getDefaultSmsSubId())) {
+ if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
return false;
}
- if (!isValidSubscriptionId(getDefaultVoiceSubId())) {
+ if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
return false;
}
return true;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7eaa6a6..fcb42a4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1232,7 +1232,6 @@
* on a CDMA network).
* @param subId
*/
- /** {@hide} */
public String getNetworkOperatorName(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
@@ -1259,8 +1258,7 @@
*
* @param subId
*/
- /** {@hide} */
- public String getNetworkOperatorForSubscription(int subId) {
+ public String getNetworkOperator(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getNetworkOperatorForPhone(phoneId);
}
@@ -1276,7 +1274,7 @@
* @param phoneId
* @hide
**/
- public String getNetworkOperatorForPhone(int phoneId) {
+ public String getNetworkOperatorForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
}
@@ -1298,7 +1296,6 @@
*
* @param subId
*/
- /** {@hide} */
public boolean isNetworkRoaming(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return Boolean.parseBoolean(getTelephonyProperty(phoneId,
@@ -1327,8 +1324,7 @@
*
* @param subId for which Network CountryIso is returned
*/
- /** {@hide} */
- public String getNetworkCountryIsoForSubscription(int subId) {
+ public String getNetworkCountryIso(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getNetworkCountryIsoForPhone(phoneId);
}
@@ -1436,7 +1432,6 @@
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide} */
public int getNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
@@ -1497,7 +1492,6 @@
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide} */
public int getDataNetworkType(int subId) {
try{
ITelephony telephony = getITelephony();
@@ -1535,7 +1529,6 @@
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide} */
public int getVoiceNetworkType(int subId) {
try{
ITelephony telephony = getITelephony();
@@ -1804,10 +1797,9 @@
* @see #getSimState
*
* @param subId for which SimOperator is returned
- * @hide
*/
public String getSimOperator(int subId) {
- return getSimOperatorNumericForSubscription(subId);
+ return getSimOperatorNumeric(subId);
}
/**
@@ -1820,17 +1812,17 @@
* @hide
*/
public String getSimOperatorNumeric() {
- int subId = SubscriptionManager.getDefaultDataSubId();
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSmsSubId();
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultVoiceSubId();
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSubId();
+ subId = SubscriptionManager.getDefaultSubscriptionId();
}
}
}
- return getSimOperatorNumericForSubscription(subId);
+ return getSimOperatorNumeric(subId);
}
/**
@@ -1844,7 +1836,7 @@
* @param subId for which SimOperator is returned
* @hide
*/
- public String getSimOperatorNumericForSubscription(int subId) {
+ public String getSimOperatorNumeric(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimOperatorNumericForPhone(phoneId);
}
@@ -1881,9 +1873,8 @@
* @see #getSimState
*
* @param subId for which SimOperatorName is returned
- * @hide
*/
- public String getSimOperatorNameForSubscription(int subId) {
+ public String getSimOperatorName(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimOperatorNameForPhone(phoneId);
}
@@ -1909,21 +1900,8 @@
* Returns the ISO country code equivalent for the SIM provider's country code.
*
* @param subId for which SimCountryIso is returned
- *
- * @hide
*/
public String getSimCountryIso(int subId) {
- return getSimCountryIsoForSubscription(subId);
- }
-
- /**
- * Returns the ISO country code equivalent for the SIM provider's country code.
- *
- * @param subId for which SimCountryIso is returned
- *
- * @hide
- */
- public String getSimCountryIsoForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimCountryIsoForPhone(phoneId);
}
@@ -1957,7 +1935,6 @@
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide} */
public String getSimSerialNumber(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2046,7 +2023,6 @@
*
* @param subId whose subscriber id is returned
*/
- /** {@hide} */
public String getSubscriberId(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2089,9 +2065,8 @@
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*
- * @param subscription whose subscriber id is returned
+ * @param subId whose subscriber id is returned
*/
- /** {@hide} */
public String getGroupIdLevel1(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2118,7 +2093,7 @@
* The default SMS app can also use this.
*/
public String getLine1Number() {
- return getLine1NumberForSubscriber(getDefaultSubscription());
+ return getLine1Number(getDefaultSubscription());
}
/**
@@ -2134,8 +2109,7 @@
*
* @param subId whose phone number for line 1 is returned
*/
- /** {@hide} */
- public String getLine1NumberForSubscriber(int subId) {
+ public String getLine1Number(int subId) {
String number = null;
try {
ITelephony telephony = getITelephony();
@@ -2174,7 +2148,7 @@
* @return true if the operation was executed correctly.
*/
public boolean setLine1NumberForDisplay(String alphaTag, String number) {
- return setLine1NumberForDisplayForSubscriber(getDefaultSubscription(), alphaTag, number);
+ return setLine1NumberForDisplay(getDefaultSubscription(), alphaTag, number);
}
/**
@@ -2190,9 +2164,8 @@
* @param alphaTag alpha-tagging of the dailing nubmer
* @param number The dialing number
* @return true if the operation was executed correctly.
- * @hide
*/
- public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) {
+ public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
@@ -2213,7 +2186,7 @@
* nobody seems to call this.
*/
public String getLine1AlphaTag() {
- return getLine1AlphaTagForSubscriber(getDefaultSubscription());
+ return getLine1AlphaTag(getDefaultSubscription());
}
/**
@@ -2226,8 +2199,7 @@
* @param subId whose alphabetic identifier associated with line 1 is returned
* nobody seems to call this.
*/
- /** {@hide} */
- public String getLine1AlphaTagForSubscriber(int subId) {
+ public String getLine1AlphaTag(int subId) {
String alphaTag = null;
try {
ITelephony telephony = getITelephony();
@@ -2327,7 +2299,6 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* @param subId whose voice mail number is returned
*/
- /** {@hide} */
public String getVoiceMailNumber(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2400,7 +2371,6 @@
* @param alphaTag The alpha tag to display.
* @param number The voicemail number.
*/
- /** {@hide} */
public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
try {
ITelephony telephony = getITelephony();
@@ -2466,7 +2436,6 @@
* @param subId whose alphabetic identifier associated with the
* voice mail number is returned
*/
- /** {@hide} */
public String getVoiceMailAlphaTag(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -2584,7 +2553,6 @@
*
* @param subId whose call state is returned
*/
- /** {@hide} */
public int getCallState(int subId) {
try {
ITelephony telephony = getITelephony();
@@ -3225,19 +3193,19 @@
* Returns Default subscription.
*/
private static int getDefaultSubscription() {
- return SubscriptionManager.getDefaultSubId();
+ return SubscriptionManager.getDefaultSubscriptionId();
}
/**
* Returns Default phone.
*/
private static int getDefaultPhone() {
- return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubId());
+ return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId());
}
/** {@hide} */
public int getDefaultSim() {
- return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubId());
+ return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubscriptionId());
}
/**
@@ -4182,7 +4150,7 @@
/** @hide */
@SystemApi
public void setDataEnabled(boolean enable) {
- setDataEnabled(SubscriptionManager.getDefaultDataSubId(), enable);
+ setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable);
}
/** @hide */
@@ -4201,7 +4169,7 @@
/** @hide */
@SystemApi
public boolean getDataEnabled() {
- return getDataEnabled(SubscriptionManager.getDefaultDataSubId());
+ return getDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId());
}
/** @hide */
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index b9d7297..5f3f773 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -327,7 +327,7 @@
public static CallerInfo getCallerInfo(Context context, String number) {
if (VDBG) Rlog.v(TAG, "getCallerInfo() based on number...");
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
return getCallerInfo(context, number, subId);
}
@@ -444,7 +444,7 @@
// string in the phone number field.
/* package */ CallerInfo markAsVoiceMail() {
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
return markAsVoiceMail(subId);
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index c754068..05cb31e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -387,7 +387,7 @@
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
OnQueryCompleteListener listener, Object cookie) {
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
return startQuery(token, context, number, listener, cookie, subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76b69ce..907d76e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -21,7 +21,6 @@
import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.telephony.CellInfo;
-import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
@@ -65,7 +64,6 @@
void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
String failCause);
void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
- void notifyDataConnectionRealTimeInfo(in DataConnectionRealTimeInfo dcRtInfo);
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 0c36063..4c12c2d 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -16,8 +16,9 @@
package android.security.net.config;
-import java.util.Set;
+import android.util.ArraySet;
import java.security.cert.X509Certificate;
+import java.util.Set;
import com.android.org.conscrypt.TrustedCertificateIndex;
@@ -33,10 +34,12 @@
}
}
+ @Override
public Set<X509Certificate> getCertificates() {
return mCertificates;
}
+ @Override
public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
if (anchor == null) {
@@ -45,6 +48,7 @@
return anchor.getTrustedCert();
}
+ @Override
public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
if (anchor == null) {
@@ -52,4 +56,13 @@
}
return anchor.getTrustedCert();
}
+
+ @Override
+ public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+ Set<X509Certificate> certs = new ArraySet<X509Certificate>();
+ for (java.security.cert.TrustAnchor anchor : mIndex.findAllByIssuerAndSignature(cert)) {
+ certs.add(anchor.getTrustedCert());
+ }
+ return certs;
+ }
}
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;
}
+
}
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
deleted file mode 100644
index 50bec33..0000000
--- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.ScanResult;
-import android.net.wifi.passpoint.WifiPasspointPolicy;
-import android.net.wifi.passpoint.WifiPasspointCredential;
-import android.os.Messenger;
-
-/**
- * Interface that allows controlling and querying Wifi Passpoint connectivity.
- *
- * {@hide}
- */
-interface IWifiPasspointManager
-{
- Messenger getMessenger();
-
- int getPasspointState();
-
- List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
-
- List<WifiPasspointCredential> getCredentials();
-
- boolean addCredential(in WifiPasspointCredential cred);
-
- boolean updateCredential(in WifiPasspointCredential cred);
-
- boolean removeCredential(in WifiPasspointCredential cred);
-}
-
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
deleted file mode 100644
index cfd3605..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointCredential;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
deleted file mode 100644
index a100aed..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.WifiEnterpriseConfig;
-import android.os.Parcelable;
-import android.os.Parcel;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * A class representing a Wi-Fi Passpoint credential.
- * @hide
- */
-public class WifiPasspointCredential implements Parcelable {
-
- private final static String TAG = "PasspointCredential";
- private final static boolean DBG = true;
-
- /** Wi-Fi nodes**/
- private String mWifiSpFqdn;
-
- /** PerProviderSubscription nodes **/
- private String mCredentialName;
-
- /** SubscriptionUpdate nodes **/
- private String mSubscriptionUpdateInterval;
- private String mSubscriptionUpdateMethod;
- private String mSubscriptionUpdateRestriction;
- private String mSubscriptionUpdateURI;
- private String mSubscriptionUpdateUsername;
- private String mSubscriptionUpdatePassword;
-
- /** HomeSP nodes **/
- private String mHomeSpFqdn;
- private String mFriendlyName;
- private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
- private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList;
-
- /** SubscriptionParameters nodes**/
- private String mCreationDate;
- private String mExpirationDate;
-
- /** Credential nodes **/
- private String mType;
- private String mInnerMethod;
- private String mCertType;
- private String mCertSha256Fingerprint;
- private String mUpdateIdentifier;
- private String mUsername;
- private String mPasswd;
- private String mRealm;
- private String mImsi;
- private String mMcc;
- private String mMnc;
- private String mCaRootCert;
- private String mClientCert;
- private boolean mCheckAaaServerCertStatus;
-
- /** Policy nodes **/
- private String mPolicyUpdateUri;
- private String mPolicyUpdateInterval;
- private String mPolicyUpdateUsername;
- private String mPolicyUpdatePassword;
- private String mPolicyUpdateRestriction;
- private String mPolicyUpdateMethod;
- private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
- private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
- private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
- private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
- private String mMaxBssLoad;
-
- /** CrednetialPriority node **/
- private int mCrednetialPriority;
-
- /** AAAServerTrustRoot nodes **/
- private String mAaaCertUrl;
- private String mAaaSha256Fingerprint;
-
- /** Others **/
- private boolean mIsMachineRemediation;
- private boolean mUserPreferred = false;
- private String mWifiTreePath;
- private WifiEnterpriseConfig mEnterpriseConfig;
-
- /** @hide */
- public WifiPasspointCredential() {}
-
- /**
- * Constructor
- * @param realm Realm of the passpoint credential
- * @param fqdn Fully qualified domain name (FQDN) of the credential
- * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
- * @see WifiEnterpriseConfig
- */
- public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
- mRealm = realm;
- switch (config.getEapMethod()) {
- case WifiEnterpriseConfig.Eap.TLS:
- case WifiEnterpriseConfig.Eap.TTLS:
- mEnterpriseConfig = new WifiEnterpriseConfig(config);
- break;
- default:
- // ignore
- }
- }
-
- /** @hide */
- public WifiPasspointCredential(String type,
- String caroot,
- String clientcert,
- String mcc,
- String mnc,
- WifiPasspointDmTree.SpFqdn sp,
- WifiPasspointDmTree.CredentialInfo credinfo) {
-
- if (credinfo == null) {
- return;
- }
-
- mType = type;
- mCaRootCert = caroot;
- mClientCert = clientcert;
-
- mWifiSpFqdn = sp.nodeName;
- mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
-
- mCredentialName = credinfo.nodeName;
- mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values();
-
- Set set = credinfo.aAAServerTrustRoot.entrySet();
- Iterator i = set.iterator();
- if (i.hasNext()) {
- Map.Entry entry3 = (Map.Entry) i.next();
- WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
- mAaaCertUrl = aaa.CertURL;
- mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint;
- }
-
- mCertType = credinfo.credential.digitalCertificate.CertificateType;
- mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint;
- mUsername = credinfo.credential.usernamePassword.Username;
- mPasswd = credinfo.credential.usernamePassword.Password;
- mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged;
- mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod;
- mImsi = credinfo.credential.sim.IMSI;
- mMcc = mcc;
- mMnc = mnc;
- mCreationDate = credinfo.credential.CreationDate;
- mExpirationDate = credinfo.credential.ExpirationDate;
- mRealm = credinfo.credential.Realm;
-
- if (credinfo.credentialPriority == null) {
- mCrednetialPriority = 128;
- } else {
- mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority);
- }
-
- mHomeSpFqdn = credinfo.homeSP.FQDN;
-
- mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
- mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
- mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
- mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
- mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
- mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password;
-
- mPolicyUpdateUri = credinfo.policy.policyUpdate.URI;
- mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
- mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
- mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password;
- mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
- mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
- mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
- mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values();
- mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values();
- mMaxBssLoad = credinfo.policy.maximumBSSLoadValue;
- mSpExclusionList = credinfo.policy.sPExclusionList.values();
-
- mHomeOIList = credinfo.homeSP.homeOIList.values();
- mFriendlyName = credinfo.homeSP.FriendlyName;
- mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
- }
-
- /** @hide */
- public String getUpdateIdentifier() {
- return mUpdateIdentifier;
- }
-
- /** @hide */
- public String getUpdateMethod() {
- return mSubscriptionUpdateMethod;
- }
-
- /** @hide */
- public void setUpdateMethod(String method) {
- mSubscriptionUpdateMethod = method;
- }
-
- /** @hide */
- public String getWifiSpFqdn() {
- return mWifiSpFqdn;
- }
-
- /** @hide */
- public String getCredName() {
- return mCredentialName;
- }
-
- /** @hide */
- public String getType() {
- return mType;
- }
-
- /**
- * Get enterprise config of this Passpoint credential.
- * @return Enterprise config
- * @see WifiEnterpriseConfig
- */
- public WifiEnterpriseConfig getEnterpriseConfig() {
- return new WifiEnterpriseConfig(mEnterpriseConfig);
- }
-
- /**
- * Set enterprise config of this Passpoint credential.
- * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
- * @see WifiEnterpriseConfig
- */
- public void setEnterpriseConfig(WifiEnterpriseConfig config) {
- // TODO
- }
-
- /** @hide */
- public String getCertType() {
- return mCertType;
- }
-
- /** @hide */
- public String getCertSha256Fingerprint() {
- return mCertSha256Fingerprint;
- }
-
- /** @hide */
- public String getUserName() {
- return mUsername;
- }
-
- /** @hide */
- public String getPassword() {
- // TODO: guarded by connectivity internal
- return mPasswd;
- }
-
- /** @hide */
- public String getImsi() {
- return mImsi;
- }
-
- /** @hide */
- public String getMcc() {
- return mMcc;
- }
-
- /** @hide */
- public String getMnc() {
- return mMnc;
- }
-
- /** @hide */
- public String getCaRootCertPath() {
- return mCaRootCert;
- }
-
- /** @hide */
- public String getClientCertPath() {
- return mClientCert;
- }
-
- /**
- * Get the realm of this Passpoint credential.
- * @return Realm
- */
- public String getRealm() {
- return mRealm;
- }
-
- /**
- * Set the ream of this Passpoint credential.
- * @param realm Realm
- */
- public void setRealm(String realm) {
- mRealm = realm;
- }
-
- /** @hide */
- public int getPriority() {
- if (mUserPreferred) {
- return 0;
- }
-
- return mCrednetialPriority;
- }
-
- /**
- * Get the fully qualified domain name (FQDN) of this Passpoint credential.
- * @return FQDN
- */
- public String getHomeSpFqdn() {
- return mHomeSpFqdn;
- }
-
- /**
- * Set the fully qualified domain name (FQDN) of this Passpoint credential.
- * @param fqdn FQDN
- */
- public void setHomeFqdn(String fqdn) {
- mHomeSpFqdn = fqdn;
- }
-
-
- /** @hide */
- public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() {
- return mOtherHomePartnerList;
- }
-
- /** @hide */
- public String getSubscriptionUpdateUsername() {
- return mSubscriptionUpdateUsername;
- }
-
- /** @hide */
- public String getSubscriptionUpdatePassword() {
- return mSubscriptionUpdatePassword;
- }
-
- /** @hide */
- public String getPolicyUpdateUri() {
- return mPolicyUpdateUri;
- }
-
- /** @hide */
- public String getPolicyUpdateInterval() {
- return mPolicyUpdateInterval;
- }
-
- /** @hide */
- public String getPolicyUpdateUsername() {
- return mPolicyUpdateUsername;
- }
-
- /** @hide */
- public String getPolicyUpdatePassword() {
- return mPolicyUpdatePassword;
- }
-
- /** @hide */
- public String getPolicyUpdateRestriction() {
- return mPolicyUpdateRestriction;
- }
-
- /** @hide */
- public String getPolicyUpdateMethod() {
- return mPolicyUpdateMethod;
- }
-
- /** @hide */
- public String getCreationDate() {
- return mCreationDate;
- }
-
- /** @hide */
- public String getExpirationDate() {
- return mExpirationDate;
- }
-
- /** @hide */
- public void setExpirationDate(String expirationdate) {
- mExpirationDate = expirationdate;
- }
-
- /** @hide */
- public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() {
- return mPreferredRoamingPartnerList;
- }
-
- /** @hide */
- public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() {
- return mHomeOIList;
- }
-
- /** @hide */
- public Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> getBackhaulThresholdList() {
- return mMinBackhaulThresholdNetwork;
- }
-
- /** @hide */
- public Collection<WifiPasspointDmTree.RequiredProtoPortTuple> getRequiredProtoPortList() {
- return mRequiredProtoPortTuple;
- }
-
- /** @hide */
- public Collection<WifiPasspointDmTree.SPExclusionList> getSPExclusionList() {
- return mSpExclusionList;
- }
-
- /** @hide */
- public boolean getIsMachineRemediation() {
- return mIsMachineRemediation;
- }
-
- /** @hide */
- public String getAaaCertUrl() {
- return mAaaCertUrl;
- }
-
- /** @hide */
- public String getAaaSha256Fingerprint() {
- return mAaaSha256Fingerprint;
- }
-
- /** @hide */
- public String getSubscriptionUpdateRestriction() {
- return mSubscriptionUpdateRestriction;
- }
-
- /** @hide */
- public String getSubscriptionUpdateURI() {
- return mSubscriptionUpdateURI;
- }
-
- /** @hide */
- public String getSubscriptionUpdateInterval() {
- return mSubscriptionUpdateInterval;
- }
-
- /** @hide */
- public String getFriendlyName() {
- return mFriendlyName;
- }
-
- /** @hide */
- public String getMaxBssLoad() {
- return mMaxBssLoad;
- }
-
- /** @hide */
- public boolean getUserPreference() {
- return mUserPreferred;
- }
-
- /** @hide */
- public boolean getCheckAaaServerCertStatus() {
- return mCheckAaaServerCertStatus;
- }
-
- /** @hide */
- public void setUserPreference(boolean value) {
- mUserPreferred = value;
- }
-
- @Override
- /** @hide */
- public boolean equals(Object obj) {
- boolean result = false;
- if (obj instanceof WifiPasspointCredential) {
- final WifiPasspointCredential other = (WifiPasspointCredential) obj;
- if (this.mType.equals(other.mType)) {
- if (this.mType.equals("TTLS")) {
- result = this.mUsername.equals(other.mUsername) &&
- this.mPasswd.equals(other.mPasswd) &&
- this.mRealm.equals(other.mRealm) &&
- this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
- }
- if (this.mType.equals("TLS")) {
- result = this.mRealm.equals(other.mRealm) &&
- this.mHomeSpFqdn.equals(other.mHomeSpFqdn) &&
- this.mClientCert.equals(other.mClientCert);
- }
- if (this.mType.equals("SIM")) {
- result = this.mMcc.equals(other.mMcc) &&
- this.mMnc.equals(other.mMnc) &&
- this.mImsi.equals(other.mImsi) &&
- this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
- }
- }
- }
- return result;
- }
-
- @Override
- /** @hide */
- public String toString() {
- StringBuffer sb = new StringBuffer();
- String none = "<none>";
-
- if (!DBG) {
- sb.append(none);
- } else {
- sb.append(", UpdateIdentifier: ")
- .append(mUpdateIdentifier == null ? none : mUpdateIdentifier)
- .append(", SubscriptionUpdateMethod: ")
- .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod)
- .append(", Type: ").append(mType == null ? none : mType)
- .append(", Username: ").append(mUsername == null ? none : mUsername)
- .append(", Passwd: ").append(mPasswd == null ? none : mPasswd)
- .append(", SubDMAccUsername: ")
- .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername)
- .append(", SubDMAccPassword: ")
- .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword)
- .append(", PolDMAccUsername: ")
- .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername)
- .append(", PolDMAccPassword: ")
- .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword)
- .append(", Imsi: ").append(mImsi == null ? none : mImsi)
- .append(", Mcc: ").append(mMcc == null ? none : mMcc)
- .append(", Mnc: ").append(mMnc == null ? none : mMnc)
- .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert)
- .append(", Realm: ").append(mRealm == null ? none : mRealm)
- .append(", Priority: ").append(mCrednetialPriority)
- .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn)
- .append(", Otherhomepartners: ")
- .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList)
- .append(", ExpirationDate: ")
- .append(mExpirationDate == null ? none : mExpirationDate)
- .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad)
- .append(", SPExclusionList: ").append(mSpExclusionList);
-
- if (mPreferredRoamingPartnerList != null) {
- sb.append("PreferredRoamingPartnerList:");
- for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
- sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
- append(", priority:").append(prpListItem.Priority).
- append(", country:").append(prpListItem.Country).append("]");
- }
- }
-
- if (mHomeOIList != null) {
- sb.append("HomeOIList:");
- for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
- sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
- append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
- append("]");
- }
- }
-
- if (mMinBackhaulThresholdNetwork != null) {
- sb.append("BackHaulThreshold:");
- for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
- sb.append("[networkType:").append(BhtListItem.NetworkType).
- append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
- append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
- append("]");
- }
- }
-
- if (mRequiredProtoPortTuple != null) {
- sb.append("WifiMORequiredProtoPortTupleList:");
- for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
- sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
- append(", PortNumber:").append(RpptListItem.PortNumber).
- append("]");
- }
- }
- }
- return sb.toString();
- }
-
- /** Implement the Parcelable interface {@hide} */
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface {@hide} */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mWifiSpFqdn);
- dest.writeString(mCredentialName);
- dest.writeString(mType);
- dest.writeInt(mCrednetialPriority);
- dest.writeString(mHomeSpFqdn);
- dest.writeString(mRealm);
- }
-
- /** Implement the Parcelable interface {@hide} */
- public void readFromParcel(Parcel in) {
- mWifiSpFqdn = in.readString();
- mCredentialName = in.readString();
- mType = in.readString();
- mCrednetialPriority = in.readInt();
- mHomeSpFqdn = in.readString();
- mRealm = in.readString();
- }
-
- /** Implement the Parcelable interface {@hide} */
- public static final Creator<WifiPasspointCredential> CREATOR =
- new Creator<WifiPasspointCredential>() {
- public WifiPasspointCredential createFromParcel(Parcel in) {
- WifiPasspointCredential pc = new WifiPasspointCredential();
- pc.mWifiSpFqdn = in.readString();
- pc.mCredentialName = in.readString();
- pc.mType = in.readString();
- pc.mCrednetialPriority = in.readInt();
- pc.mHomeSpFqdn = in.readString();
- pc.mRealm = in.readString();
- return pc;
- }
-
- public WifiPasspointCredential[] newArray(int size) {
- return new WifiPasspointCredential[size];
- }
- };
-
- /** @hide */
- public int compareTo(WifiPasspointCredential another) {
-
- //The smaller the higher
- if (mCrednetialPriority < another.mCrednetialPriority) {
- return -1;
- } else if (mCrednetialPriority == another.mCrednetialPriority) {
- return this.mType.compareTo(another.mType);
- } else {
- return 1;
- }
- }
-
- @Override
- /** @hide */
- public int hashCode() {
- int hash = 208;
- if (mType != null) {
- hash += mType.hashCode();
- }
- if (mRealm != null) {
- hash += mRealm.hashCode();
- }
- if (mHomeSpFqdn != null) {
- hash += mHomeSpFqdn.hashCode();
- }
- if (mUsername != null) {
- hash += mUsername.hashCode();
- }
- if (mPasswd != null) {
- hash += mPasswd.hashCode();
- }
-
- return hash;
- }
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
deleted file mode 100644
index 6a88b2e..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointDmTree;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
deleted file mode 100644
index 427c84c..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
+++ /dev/null
@@ -1,1375 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-import java.util.HashMap;
-
-/**
- * Required Mobile Device Management Tree Structure
- *
- * +----------+
- * | ./(Root) |
- * +----+-----+
- * |
- * +---------+ | +---------+ +---------+
- * | DevInfo |-----------+---------| Wi-Fi |--|SP FQDN* |
- * +---------+ | +---------+ +---------+
- * +---------+ | |
- * |DevDetail|-----------+ +-----------------------+
- * +---------+ |PerproviderSubscription|--<X>+
- * +-----------------------+
- *
- * This class contains all nodes start from Wi-Fi
- * @hide
- **/
-public class WifiPasspointDmTree implements Parcelable {
- private final static String TAG = "WifiTree";
- public int PpsMoId;//plugfest used only
- public HashMap<String, SpFqdn> spFqdn = new HashMap<String, SpFqdn>();//Maps.newHashMap();
-
- public SpFqdn createSpFqdn(String name) {
- SpFqdn obj = new SpFqdn(name);
- spFqdn.put(name, obj);
- return obj;
- }
-
- public static class SpFqdn implements Parcelable {
- public String nodeName;
- public PerProviderSubscription perProviderSubscription = new PerProviderSubscription();
-
- public SpFqdn(String name) {
- nodeName = name;
- }
-
- public SpFqdn() {
- }
-
- public SpFqdn(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeParcelable(perProviderSubscription, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- perProviderSubscription = in.readParcelable(PerProviderSubscription.class
- .getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<SpFqdn> CREATOR = new Parcelable.Creator<SpFqdn>() {
- public SpFqdn createFromParcel(Parcel in) {
- return new SpFqdn(in);
- }
-
- public SpFqdn[] newArray(int size) {
- return new SpFqdn[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription
- **/
- public static class PerProviderSubscription implements Parcelable {
- /**
- * PerProviderSubscription/UpdateIdentifier
- **/
- public String UpdateIdentifier;
- public HashMap<String, CredentialInfo> credentialInfo = new HashMap<String, CredentialInfo>();
-
- public CredentialInfo createCredentialInfo(String name) {
- CredentialInfo obj = new CredentialInfo(name);
- credentialInfo.put(name, obj);
- return obj;
- }
-
- public PerProviderSubscription() {
- }
-
- public PerProviderSubscription(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(UpdateIdentifier);
- out.writeMap(credentialInfo);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- UpdateIdentifier = in.readString();
- in.readMap(credentialInfo, CredentialInfo.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<PerProviderSubscription> CREATOR = new Parcelable.Creator<PerProviderSubscription>() {
- public PerProviderSubscription createFromParcel(Parcel in) {
- return new PerProviderSubscription(in);
- }
-
- public PerProviderSubscription[] newArray(int size) {
- return new PerProviderSubscription[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>
- * This interior node contains the Home SP information, subscription policy, management and credential information.
- **/
- public static class CredentialInfo implements Parcelable {
- public String nodeName;
- public Policy policy = new Policy();
- public String credentialPriority;
- public HashMap<String, AAAServerTrustRoot> aAAServerTrustRoot = new HashMap<String, AAAServerTrustRoot>();
- public SubscriptionUpdate subscriptionUpdate = new SubscriptionUpdate();
- public HomeSP homeSP = new HomeSP();
- public SubscriptionParameters subscriptionParameters = new SubscriptionParameters();
- public Credential credential = new Credential();
- public Extension extension = new Extension();
-
- public CredentialInfo(String nn) {
- nodeName = nn;
- }
-
- public AAAServerTrustRoot createAAAServerTrustRoot(String name, String url, String fp) {
- AAAServerTrustRoot obj = new AAAServerTrustRoot(name, url, fp);
- aAAServerTrustRoot.put(name, obj);
- return obj;
- }
-
- public CredentialInfo() {
- }
-
- public CredentialInfo(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeParcelable(policy, flags);
- out.writeString(credentialPriority);
- out.writeMap(aAAServerTrustRoot);
- out.writeParcelable(subscriptionUpdate, flags);
- out.writeParcelable(homeSP, flags);
- out.writeParcelable(subscriptionParameters, flags);
- out.writeParcelable(credential, flags);
- //out.writeParcelable(extension, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- policy = in.readParcelable(Policy.class.getClassLoader());
- credentialPriority = in.readString();
- in.readMap(aAAServerTrustRoot, AAAServerTrustRoot.class.getClassLoader());
- subscriptionUpdate = in.readParcelable(SubscriptionUpdate.class.getClassLoader());
- homeSP = in.readParcelable(HomeSP.class.getClassLoader());
- subscriptionParameters = in.readParcelable(SubscriptionParameters.class
- .getClassLoader());
- credential = in.readParcelable(Credential.class.getClassLoader());
- //extension = in.readParcelable(Extension.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<CredentialInfo> CREATOR = new Parcelable.Creator<CredentialInfo>() {
- public CredentialInfo createFromParcel(Parcel in) {
- return new CredentialInfo(in);
- }
-
- public CredentialInfo[] newArray(int size) {
- return new CredentialInfo[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy
- **/
- public static class Policy implements Parcelable {
- public HashMap<String, PreferredRoamingPartnerList> preferredRoamingPartnerList = new HashMap<String, PreferredRoamingPartnerList>();
- public HashMap<String, MinBackhaulThresholdNetwork> minBackhaulThreshold = new HashMap<String, MinBackhaulThresholdNetwork>();
- public PolicyUpdate policyUpdate = new PolicyUpdate();
- public HashMap<String, SPExclusionList> sPExclusionList = new HashMap<String, SPExclusionList>();
- public HashMap<String, RequiredProtoPortTuple> requiredProtoPortTuple = new HashMap<String, RequiredProtoPortTuple>();
- public String maximumBSSLoadValue;
-
- public PreferredRoamingPartnerList createPreferredRoamingPartnerList(String name,
- String fqdn, String priority, String country) {
- PreferredRoamingPartnerList obj = new PreferredRoamingPartnerList(name, fqdn, priority,
- country);
- preferredRoamingPartnerList.put(name, obj);
- return obj;
- }
-
- public MinBackhaulThresholdNetwork createMinBackhaulThreshold(String name, String type,
- String dl, String ul) {
- MinBackhaulThresholdNetwork obj = new MinBackhaulThresholdNetwork(name, type, dl, ul);
- minBackhaulThreshold.put(name, obj);
- return obj;
- }
-
- public SPExclusionList createSPExclusionList(String name, String ssid) {
- SPExclusionList obj = new SPExclusionList(name, ssid);
- sPExclusionList.put(name, obj);
- return obj;
- }
-
- public RequiredProtoPortTuple createRequiredProtoPortTuple(String name, String proto,
- String port) {
- RequiredProtoPortTuple obj = new RequiredProtoPortTuple(name, proto, port);
- requiredProtoPortTuple.put(name, obj);
- return obj;
- }
-
- public Policy() {
- }
-
- public Policy(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeMap(preferredRoamingPartnerList);
- out.writeMap(minBackhaulThreshold);
- out.writeParcelable(policyUpdate, flags);
- out.writeMap(sPExclusionList);
- out.writeMap(requiredProtoPortTuple);
- out.writeString(maximumBSSLoadValue);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- in.readMap(preferredRoamingPartnerList,
- PreferredRoamingPartnerList.class.getClassLoader());
- in.readMap(minBackhaulThreshold, MinBackhaulThresholdNetwork.class.getClassLoader());
- policyUpdate = in.readParcelable(PolicyUpdate.class.getClassLoader());
- in.readMap(sPExclusionList, SPExclusionList.class.getClassLoader());
- in.readMap(requiredProtoPortTuple, RequiredProtoPortTuple.class.getClassLoader());
- maximumBSSLoadValue = in.readString();
-
- }
- }
-
- public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
- public Policy createFromParcel(Parcel in) {
- return new Policy(in);
- }
-
- public Policy[] newArray(int size) {
- return new Policy[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>
- **/
- public static class PreferredRoamingPartnerList implements Parcelable {
- public String nodeName;
- public String FQDN_Match; //maximum 255 + ",includeSubdomains", equals 273
- public String Priority;
- public String Country; // maximum 600 octets
-
- public PreferredRoamingPartnerList(String nn, String f, String p, String c) {
- nodeName = nn;
- FQDN_Match = f;
- Priority = p;
- Country = c;
- }
-
- public PreferredRoamingPartnerList() {
- }
-
- public PreferredRoamingPartnerList(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(FQDN_Match);
- out.writeString(Priority);
- out.writeString(Country);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- FQDN_Match = in.readString();
- Priority = in.readString();
- Country = in.readString();
- }
- }
-
- public static final Parcelable.Creator<PreferredRoamingPartnerList> CREATOR = new Parcelable.Creator<PreferredRoamingPartnerList>() {
- public PreferredRoamingPartnerList createFromParcel(Parcel in) {
- return new PreferredRoamingPartnerList(in);
- }
-
- public PreferredRoamingPartnerList[] newArray(int size) {
- return new PreferredRoamingPartnerList[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/MinBackhaulThreshold
- **/
- public static class MinBackhaulThresholdNetwork implements Parcelable {
- public String nodeName;
- public String NetworkType;
- public String DLBandwidth;
- public String ULBandwidth;
-
- public MinBackhaulThresholdNetwork(String nn, String nt, String d, String u) {
- nodeName = nn;
- NetworkType = nt;
- DLBandwidth = d;
- ULBandwidth = u;
- }
-
- public MinBackhaulThresholdNetwork() {
- }
-
- public MinBackhaulThresholdNetwork(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(NetworkType);
- out.writeString(DLBandwidth);
- out.writeString(ULBandwidth);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- NetworkType = in.readString();
- DLBandwidth = in.readString();
- ULBandwidth = in.readString();
- }
- }
-
- public static final Parcelable.Creator<MinBackhaulThresholdNetwork> CREATOR = new Parcelable.Creator<MinBackhaulThresholdNetwork>() {
- public MinBackhaulThresholdNetwork createFromParcel(Parcel in) {
- return new MinBackhaulThresholdNetwork(in);
- }
-
- public MinBackhaulThresholdNetwork[] newArray(int size) {
- return new MinBackhaulThresholdNetwork[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/PolicyUpdate
- **/
- public static class PolicyUpdate implements Parcelable {
- public String UpdateInterval;
- public String UpdateMethod;
- public String Restriction;
- public String URI;
- public UsernamePassword usernamePassword = new UsernamePassword();
- public String Other;
- public TrustRoot trustRoot = new TrustRoot();
-
- public PolicyUpdate() {
- }
-
- public PolicyUpdate(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(UpdateInterval);
- out.writeString(UpdateMethod);
- out.writeString(Restriction);
- out.writeString(URI);
- out.writeParcelable(usernamePassword, flags);
- out.writeString(Other);
- out.writeParcelable(trustRoot, flags);
-
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- UpdateInterval = in.readString();
- UpdateMethod = in.readString();
- Restriction = in.readString();
- URI = in.readString();
- usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
- Other = in.readString();
- trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<PolicyUpdate> CREATOR = new Parcelable.Creator<PolicyUpdate>() {
- public PolicyUpdate createFromParcel(Parcel in) {
- return new PolicyUpdate(in);
- }
-
- public PolicyUpdate[] newArray(int size) {
- return new PolicyUpdate[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/SPExclusionList
- **/
- public static class SPExclusionList implements Parcelable {
- public String nodeName;
- public String SSID;
-
- public SPExclusionList(String nn, String s) {
- nodeName = nn;
- SSID = s;
- }
-
- public SPExclusionList() {
- }
-
- public SPExclusionList(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(SSID);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- SSID = in.readString();
- }
- }
-
- public static final Parcelable.Creator<SPExclusionList> CREATOR = new Parcelable.Creator<SPExclusionList>() {
- public SPExclusionList createFromParcel(Parcel in) {
- return new SPExclusionList(in);
- }
-
- public SPExclusionList[] newArray(int size) {
- return new SPExclusionList[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/RequiredProtoPortTuple
- **/
- public static class RequiredProtoPortTuple implements Parcelable {
- public String nodeName;
- public String IPProtocol;
- public String PortNumber;
-
- public RequiredProtoPortTuple() {
- }
-
- public RequiredProtoPortTuple(String nn, String protocol, String port) {
- nodeName = nn;
- IPProtocol = protocol;
- PortNumber = port;
- }
-
- public RequiredProtoPortTuple(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(IPProtocol);
- out.writeString(PortNumber);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- IPProtocol = in.readString();
- PortNumber = in.readString();
- }
- }
-
- public static final Parcelable.Creator<RequiredProtoPortTuple> CREATOR = new Parcelable.Creator<RequiredProtoPortTuple>() {
- public RequiredProtoPortTuple createFromParcel(Parcel in) {
- return new RequiredProtoPortTuple(in);
- }
-
- public RequiredProtoPortTuple[] newArray(int size) {
- return new RequiredProtoPortTuple[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/AAAServerTrustRoot
- **/
- public static class AAAServerTrustRoot implements Parcelable {
- public String nodeName;
- public String CertURL;
- public String CertSHA256Fingerprint;
-
- public AAAServerTrustRoot(String nn, String url, String fp) {
- nodeName = nn;
- CertURL = url;
- CertSHA256Fingerprint = fp;
- }
-
- public AAAServerTrustRoot() {
- }
-
- public AAAServerTrustRoot(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(CertURL);
- out.writeString(CertSHA256Fingerprint);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- CertURL = in.readString();
- CertSHA256Fingerprint = in.readString();
- }
- }
-
- public static final Parcelable.Creator<AAAServerTrustRoot> CREATOR = new Parcelable.Creator<AAAServerTrustRoot>() {
- public AAAServerTrustRoot createFromParcel(Parcel in) {
- return new AAAServerTrustRoot(in);
- }
-
- public AAAServerTrustRoot[] newArray(int size) {
- return new AAAServerTrustRoot[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/SubscriptionUpdate
- **/
- public static class SubscriptionUpdate implements Parcelable {
- public String UpdateInterval;
- public String UpdateMethod;
- public String Restriction;
- public String URI;
- public UsernamePassword usernamePassword = new UsernamePassword();
- public String Other;
- public TrustRoot trustRoot = new TrustRoot();
-
- public SubscriptionUpdate() {
- }
-
- public SubscriptionUpdate(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(UpdateInterval);
- out.writeString(UpdateMethod);
- out.writeString(Restriction);
- out.writeString(URI);
- out.writeParcelable(usernamePassword, flags);
- out.writeString(Other);
- out.writeParcelable(trustRoot, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- UpdateInterval = in.readString();
- UpdateMethod = in.readString();
- Restriction = in.readString();
- URI = in.readString();
- usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
- Other = in.readString();
- trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<SubscriptionUpdate> CREATOR = new Parcelable.Creator<SubscriptionUpdate>() {
- public SubscriptionUpdate createFromParcel(Parcel in) {
- return new SubscriptionUpdate(in);
- }
-
- public SubscriptionUpdate[] newArray(int size) {
- return new SubscriptionUpdate[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/PolicyUpdate/TrustRoot
- * PerProviderSubscription/<X+>/SubscriptionUpdate/TrustRoot
- * PerProviderSubscription/<X+>/AAAServerTrustRoot/<X+>
- **/
- public static class TrustRoot implements Parcelable {
- public String CertURL;
- public String CertSHA256Fingerprint;
-
- public TrustRoot() {
- }
-
- public TrustRoot(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(CertURL);
- out.writeString(CertSHA256Fingerprint);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- CertURL = in.readString();
- CertSHA256Fingerprint = in.readString();
- }
- }
-
- public static final Parcelable.Creator<TrustRoot> CREATOR = new Parcelable.Creator<TrustRoot>() {
- public TrustRoot createFromParcel(Parcel in) {
- return new TrustRoot(in);
- }
-
- public TrustRoot[] newArray(int size) {
- return new TrustRoot[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Policy/PolicyUpdate/UsernamePassword
- * PerProviderSubscription/<X+>/SubscriptionUpdate/UsernamePassword
- * PerProviderSubscription/<X+>/Credential/UsernamePassword
- **/
- public static class UsernamePassword implements Parcelable {
- public String Username;
- public String Password;
- //following are Credential node used only
- public boolean MachineManaged;
- public String SoftTokenApp;
- public String AbleToShare;
- public EAPMethod eAPMethod = new EAPMethod();
-
- public UsernamePassword() {
- }
-
- public UsernamePassword(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(Username);
- out.writeString(Password);
- out.writeInt(MachineManaged ? 1 : 0);
- out.writeString(SoftTokenApp);
- out.writeString(AbleToShare);
- out.writeParcelable(eAPMethod, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- Username = in.readString();
- Password = in.readString();
- MachineManaged = (in.readInt() == 1) ? true : false;
- SoftTokenApp = in.readString();
- AbleToShare = in.readString();
- eAPMethod = in.readParcelable(EAPMethod.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<UsernamePassword> CREATOR = new Parcelable.Creator<UsernamePassword>() {
- public UsernamePassword createFromParcel(Parcel in) {
- return new UsernamePassword(in);
- }
-
- public UsernamePassword[] newArray(int size) {
- return new UsernamePassword[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Credential/UsernamePassword/EAPMethod
- **/
- public static class EAPMethod implements Parcelable {
- public String EAPType;
- public String VendorId;
- public String VendorType;
- public String InnerEAPType;
- public String InnerVendorId;
- public String InnerVendorType;
- public String InnerMethod;
-
- public EAPMethod() {
- }
-
- public EAPMethod(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(EAPType);
- out.writeString(VendorId);
- out.writeString(VendorType);
- out.writeString(InnerEAPType);
- out.writeString(InnerVendorId);
- out.writeString(InnerVendorType);
- out.writeString(InnerMethod);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- EAPType = in.readString();
- VendorId = in.readString();
- VendorType = in.readString();
- InnerEAPType = in.readString();
- InnerVendorId = in.readString();
- InnerVendorType = in.readString();
- InnerMethod = in.readString();
- }
- }
-
- public static final Parcelable.Creator<EAPMethod> CREATOR = new Parcelable.Creator<EAPMethod>() {
- public EAPMethod createFromParcel(Parcel in) {
- return new EAPMethod(in);
- }
-
- public EAPMethod[] newArray(int size) {
- return new EAPMethod[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/HomeSP
- **/
- public static class HomeSP implements Parcelable {
- public HashMap<String, NetworkID> networkID = new HashMap<String, NetworkID>();
- public String FriendlyName;
- public String IconURL;
- public String FQDN;
- public HashMap<String, HomeOIList> homeOIList = new HashMap<String, HomeOIList>();
- public HashMap<String, OtherHomePartners> otherHomePartners = new HashMap<String, OtherHomePartners>();
- public String RoamingConsortiumOI;
-
- public NetworkID createNetworkID(String name, String ssid, String hessid) {
- NetworkID obj = new NetworkID(name, ssid, hessid);
- networkID.put(name, obj);
- return obj;
- }
-
- public HomeOIList createHomeOIList(String name, String homeoi, boolean required) {
- HomeOIList obj = new HomeOIList(name, homeoi, required);
- homeOIList.put(name, obj);
- return obj;
- }
-
- public OtherHomePartners createOtherHomePartners(String name, String fqdn) {
- OtherHomePartners obj = new OtherHomePartners(name, fqdn);
- otherHomePartners.put(name, obj);
- return obj;
- }
-
- public HomeSP() {
- }
-
- public HomeSP(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeMap(networkID);
- out.writeString(FriendlyName);
- out.writeString(IconURL);
- out.writeString(FQDN);
- out.writeMap(homeOIList);
- out.writeMap(otherHomePartners);
- out.writeString(RoamingConsortiumOI);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- in.readMap(networkID, NetworkID.class.getClassLoader());
- FriendlyName = in.readString();
- IconURL = in.readString();
- FQDN = in.readString();
- in.readMap(homeOIList, HomeOIList.class.getClassLoader());
- in.readMap(otherHomePartners, OtherHomePartners.class.getClassLoader());
- RoamingConsortiumOI = in.readString();
- }
- }
-
- public static final Parcelable.Creator<HomeSP> CREATOR = new Parcelable.Creator<HomeSP>() {
- public HomeSP createFromParcel(Parcel in) {
- return new HomeSP(in);
- }
-
- public HomeSP[] newArray(int size) {
- return new HomeSP[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/HomeSP/NetworkID
- **/
- public static class NetworkID implements Parcelable {
- public String nodeName;
- public String SSID;
- public String HESSID;
-
- public NetworkID(String nn, String s, String h) {
- nodeName = nn;
- SSID = s;
- HESSID = h;
- }
-
- public NetworkID() {
- }
-
- public NetworkID(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(SSID);
- out.writeString(HESSID);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- SSID = in.readString();
- HESSID = in.readString();
- }
- }
-
- public static final Parcelable.Creator<NetworkID> CREATOR = new Parcelable.Creator<NetworkID>() {
- public NetworkID createFromParcel(Parcel in) {
- return new NetworkID(in);
- }
-
- public NetworkID[] newArray(int size) {
- return new NetworkID[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/HomeSP/HomeOIList
- **/
- public static class HomeOIList implements Parcelable {
- public String nodeName;
- public String HomeOI;
- public boolean HomeOIRequired;
-
- public HomeOIList(String nn, String h, boolean r) {
- nodeName = nn;
- HomeOI = h;
- HomeOIRequired = r;
- }
-
- public HomeOIList() {
- }
-
- public HomeOIList(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(HomeOI);
- out.writeInt(HomeOIRequired ? 1 : 0);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- HomeOI = in.readString();
- HomeOIRequired = (in.readInt() == 1) ? true : false;
- }
- }
-
- public static final Parcelable.Creator<HomeOIList> CREATOR = new Parcelable.Creator<HomeOIList>() {
- public HomeOIList createFromParcel(Parcel in) {
- return new HomeOIList(in);
- }
-
- public HomeOIList[] newArray(int size) {
- return new HomeOIList[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/HomeSP/OtherHomePartners
- **/
- public static class OtherHomePartners implements Parcelable {
- public String nodeName;
- public String FQDN;
-
- public OtherHomePartners(String nn, String f) {
- nodeName = nn;
- FQDN = f;
- }
-
- public OtherHomePartners() {
- }
-
- public OtherHomePartners(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(nodeName);
- out.writeString(FQDN);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- nodeName = in.readString();
- FQDN = in.readString();
- }
- }
-
- public static final Parcelable.Creator<OtherHomePartners> CREATOR = new Parcelable.Creator<OtherHomePartners>() {
- public OtherHomePartners createFromParcel(Parcel in) {
- return new OtherHomePartners(in);
- }
-
- public OtherHomePartners[] newArray(int size) {
- return new OtherHomePartners[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/SubscriptionParameters
- **/
- public static class SubscriptionParameters implements Parcelable {
- public String CreationDate;
- public String ExpirationDate;
- public String TypeOfSubscription;
- public UsageLimits usageLimits = new UsageLimits();
-
- public SubscriptionParameters() {
- }
-
- public SubscriptionParameters(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(CreationDate);
- out.writeString(ExpirationDate);
- out.writeString(TypeOfSubscription);
- out.writeParcelable(usageLimits, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- CreationDate = in.readString();
- ExpirationDate = in.readString();
- TypeOfSubscription = in.readString();
- usageLimits = in.readParcelable(UsageLimits.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<SubscriptionParameters> CREATOR = new Parcelable.Creator<SubscriptionParameters>() {
- public SubscriptionParameters createFromParcel(Parcel in) {
- return new SubscriptionParameters(in);
- }
-
- public SubscriptionParameters[] newArray(int size) {
- return new SubscriptionParameters[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/SubscriptionParameters/UsageLimits
- **/
- public static class UsageLimits implements Parcelable {
- public String DataLimit;
- public String StartDate;
- public String TimeLimit;
- public String UsageTimePeriod;
-
- public UsageLimits() {
- }
-
- public UsageLimits(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(DataLimit);
- out.writeString(StartDate);
- out.writeString(TimeLimit);
- out.writeString(UsageTimePeriod);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- DataLimit = in.readString();
- StartDate = in.readString();
- TimeLimit = in.readString();
- UsageTimePeriod = in.readString();
- }
- }
-
- public static final Parcelable.Creator<UsageLimits> CREATOR = new Parcelable.Creator<UsageLimits>() {
- public UsageLimits createFromParcel(Parcel in) {
- return new UsageLimits(in);
- }
-
- public UsageLimits[] newArray(int size) {
- return new UsageLimits[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Credential
- **/
- public static class Credential implements Parcelable {
- public String CreationDate;
- public String ExpirationDate;
- public UsernamePassword usernamePassword = new UsernamePassword();
- public DigitalCertificate digitalCertificate = new DigitalCertificate();
- public String Realm;
- public boolean CheckAAAServerCertStatus;
- public SIM sim = new SIM();
-
- public Credential() {
- }
-
- public Credential(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(CreationDate);
- out.writeString(ExpirationDate);
- out.writeParcelable(usernamePassword, flags);
- out.writeParcelable(digitalCertificate, flags);
- out.writeString(Realm);
- out.writeInt(CheckAAAServerCertStatus ? 1 : 0);
- out.writeParcelable(sim, flags);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- CreationDate = in.readString();
- ExpirationDate = in.readString();
- usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
- digitalCertificate = in.readParcelable(DigitalCertificate.class.getClassLoader());
- Realm = in.readString();
- CheckAAAServerCertStatus = (in.readInt() == 1) ? true : false;
- sim = in.readParcelable(SIM.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<Credential> CREATOR = new Parcelable.Creator<Credential>() {
- public Credential createFromParcel(Parcel in) {
- return new Credential(in);
- }
-
- public Credential[] newArray(int size) {
- return new Credential[size];
- }
- };
- }
-
- /**
- * PerProviderSubscription/<X+>/Credential/DigitalCertificate
- **/
- public static class DigitalCertificate implements Parcelable {
- public String CertificateType;
- public String CertSHA256Fingerprint;
-
- public DigitalCertificate() {
- }
-
- public DigitalCertificate(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(CertificateType);
- out.writeString(CertSHA256Fingerprint);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- CertificateType = in.readString();
- CertSHA256Fingerprint = in.readString();
- }
- }
-
- public static final Parcelable.Creator<DigitalCertificate> CREATOR = new Parcelable.Creator<DigitalCertificate>() {
- public DigitalCertificate createFromParcel(Parcel in) {
- return new DigitalCertificate(in);
- }
-
- public DigitalCertificate[] newArray(int size) {
- return new DigitalCertificate[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Credential/SIM
- **/
- public static class SIM implements Parcelable {
- public String IMSI;
- public String EAPType;
-
- public SIM() {
- }
-
- public SIM(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(IMSI);
- out.writeString(EAPType);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- IMSI = in.readString();
- EAPType = in.readString();
- }
- }
-
- public static final Parcelable.Creator<SIM> CREATOR = new Parcelable.Creator<SIM>() {
- public SIM createFromParcel(Parcel in) {
- return new SIM(in);
- }
-
- public SIM[] newArray(int size) {
- return new SIM[size];
- }
- };
-
- }
-
- /**
- * PerProviderSubscription/<X+>/Extension
- **/
- public static class Extension {
- public String empty;
- }
-
- public WifiPasspointDmTree() {
- }
-
- public WifiPasspointDmTree(Parcel in) {
- readFromParcel(in);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeMap(spFqdn);
- }
-
- public void readFromParcel(Parcel in) {
- if (in == null) {
- //log here
- } else {
- in.readMap(spFqdn, SpFqdn.class.getClassLoader());
- }
- }
-
- public static final Parcelable.Creator<WifiPasspointDmTree> CREATOR = new Parcelable.Creator<WifiPasspointDmTree>() {
- public WifiPasspointDmTree createFromParcel(Parcel in) {
- return new WifiPasspointDmTree(in);
- }
-
- public WifiPasspointDmTree[] newArray(int size) {
- return new WifiPasspointDmTree[size];
- }
- };
-
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
deleted file mode 100644
index 27f23bc..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointInfo;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
deleted file mode 100644
index 33db3f5..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** @hide */
-public class WifiPasspointInfo implements Parcelable {
-
- /** TODO doc */
- public static final int ANQP_CAPABILITY = 1 << 0;
-
- /** TODO doc */
- public static final int VENUE_NAME = 1 << 1;
-
- /** TODO doc */
- public static final int NETWORK_AUTH_TYPE = 1 << 2;
-
- /** TODO doc */
- public static final int ROAMING_CONSORTIUM = 1 << 3;
-
- /** TODO doc */
- public static final int IP_ADDR_TYPE_AVAILABILITY = 1 << 4;
-
- /** TODO doc */
- public static final int NAI_REALM = 1 << 5;
-
- /** TODO doc */
- public static final int CELLULAR_NETWORK = 1 << 6;
-
- /** TODO doc */
- public static final int DOMAIN_NAME = 1 << 7;
-
- /** TODO doc */
- public static final int HOTSPOT_CAPABILITY = 1 << 8;
-
- /** TODO doc */
- public static final int OPERATOR_FRIENDLY_NAME = 1 << 9;
-
- /** TODO doc */
- public static final int WAN_METRICS = 1 << 10;
-
- /** TODO doc */
- public static final int CONNECTION_CAPABILITY = 1 << 11;
-
- /** TODO doc */
- public static final int OSU_PROVIDER = 1 << 12;
-
- /** TODO doc */
- public static final int PRESET_CRED_MATCH =
- ANQP_CAPABILITY |
- HOTSPOT_CAPABILITY |
- NAI_REALM |
- CELLULAR_NETWORK |
- DOMAIN_NAME;
-
- /** TODO doc */
- public static final int PRESET_ALL =
- ANQP_CAPABILITY |
- VENUE_NAME |
- NETWORK_AUTH_TYPE |
- ROAMING_CONSORTIUM |
- IP_ADDR_TYPE_AVAILABILITY |
- NAI_REALM |
- CELLULAR_NETWORK |
- DOMAIN_NAME |
- HOTSPOT_CAPABILITY |
- OPERATOR_FRIENDLY_NAME |
- WAN_METRICS |
- CONNECTION_CAPABILITY |
- OSU_PROVIDER;
-
-
- public static class WanMetrics {
- public static final int STATUS_RESERVED = 0;
- public static final int STATUS_UP = 1;
- public static final int STATUS_DOWN = 2;
- public static final int STATUS_TEST = 3;
-
- public int wanInfo;
- public long downlinkSpeed;
- public long uplinkSpeed;
- public int downlinkLoad;
- public int uplinkLoad;
- public int lmd;
-
- public int getLinkStatus() {
- return wanInfo & 0x3;
- }
-
- public boolean getSymmetricLink() {
- return (wanInfo & (1 << 2)) != 0;
- }
-
- public boolean getAtCapacity() {
- return (wanInfo & (1 << 3)) != 0;
- }
-
- @Override
- public String toString() {
- return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," +
- downlinkLoad + "," + uplinkLoad + "," + lmd;
- }
- }
-
- public static class IpProtoPort {
- public static final int STATUS_CLOSED = 0;
- public static final int STATUS_OPEN = 1;
- public static final int STATUS_UNKNOWN = 2;
-
- public int proto;
- public int port;
- public int status;
-
- @Override
- public String toString() {
- return proto + "," + port + "," + status;
- }
- }
-
- public static class NetworkAuthType {
- public static final int TYPE_TERMS_AND_CONDITION = 0;
- public static final int TYPE_ONLINE_ENROLLMENT = 1;
- public static final int TYPE_HTTP_REDIRECTION = 2;
- public static final int TYPE_DNS_REDIRECTION = 3;
-
- public int type;
- public String redirectUrl;
-
- @Override
- public String toString() {
- return type + "," + redirectUrl;
- }
- }
-
- public static class IpAddressType {
- public static final int IPV6_NOT_AVAILABLE = 0;
- public static final int IPV6_AVAILABLE = 1;
- public static final int IPV6_UNKNOWN = 2;
-
- public static final int IPV4_NOT_AVAILABLE = 0;
- public static final int IPV4_PUBLIC = 1;
- public static final int IPV4_PORT_RESTRICTED = 2;
- public static final int IPV4_SINGLE_NAT = 3;
- public static final int IPV4_DOUBLE_NAT = 4;
- public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5;
- public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6;
- public static final int IPV4_PORT_UNKNOWN = 7;
-
- private static final int NULL_VALUE = -1;
-
- public int availability;
-
- public int getIpv6Availability() {
- return availability & 0x3;
- }
-
- public int getIpv4Availability() {
- return (availability & 0xFF) >> 2;
- }
-
- @Override
- public String toString() {
- return getIpv6Availability() + "," + getIpv4Availability();
- }
- }
-
- public static class NaiRealm {
- public static final int ENCODING_RFC4282 = 0;
- public static final int ENCODING_UTF8 = 1;
-
- public int encoding;
- public String realm;
-
- @Override
- public String toString() {
- return encoding + "," + realm;
- }
- }
-
- public static class CellularNetwork {
- public String mcc;
- public String mnc;
-
- @Override
- public String toString() {
- return mcc + "," + mnc;
- }
- }
-
- /** BSSID */
- public String bssid;
-
- /** venue name */
- public String venueName;
-
- /** list of network authentication types */
- public List<NetworkAuthType> networkAuthTypeList;
-
- /** list of roaming consortium OIs */
- public List<String> roamingConsortiumList;
-
- /** IP address availability */
- public IpAddressType ipAddrTypeAvailability;
-
- /** list of NAI realm */
- public List<NaiRealm> naiRealmList;
-
- /** list of 3GPP cellular network */
- public List<CellularNetwork> cellularNetworkList;
-
- /** list of fully qualified domain name (FQDN) */
- public List<String> domainNameList;
-
- /** HS 2.0 operator friendly name */
- public String operatorFriendlyName;
-
- /** HS 2.0 wan metrics */
- public WanMetrics wanMetrics;
-
- /** list of HS 2.0 IP proto port */
- public List<IpProtoPort> connectionCapabilityList;
-
- /** list of HS 2.0 OSU providers */
- public List<WifiPasspointOsuProvider> osuProviderList;
-
- /**
- * Convert mask to ANQP subtypes, for supplicant command use.
- *
- * @param mask The ANQP subtypes mask.
- * @return String of ANQP subtypes, good for supplicant command use
- * @hide
- */
- public static String toAnqpSubtypes(int mask) {
- StringBuilder sb = new StringBuilder();
- if ((mask & ANQP_CAPABILITY) != 0)
- sb.append("257,");
- if ((mask & VENUE_NAME) != 0)
- sb.append("258,");
- if ((mask & NETWORK_AUTH_TYPE) != 0)
- sb.append("260,");
- if ((mask & ROAMING_CONSORTIUM) != 0)
- sb.append("261,");
- if ((mask & IP_ADDR_TYPE_AVAILABILITY) != 0)
- sb.append("262,");
- if ((mask & NAI_REALM) != 0)
- sb.append("263,");
- if ((mask & CELLULAR_NETWORK) != 0)
- sb.append("264,");
- if ((mask & DOMAIN_NAME) != 0)
- sb.append("268,");
- if ((mask & HOTSPOT_CAPABILITY) != 0)
- sb.append("hs20:2,");
- if ((mask & OPERATOR_FRIENDLY_NAME) != 0)
- sb.append("hs20:3,");
- if ((mask & WAN_METRICS) != 0)
- sb.append("hs20:4,");
- if ((mask & CONNECTION_CAPABILITY) != 0)
- sb.append("hs20:5,");
- if ((mask & OSU_PROVIDER) != 0)
- sb.append("hs20:8,");
- if (sb.length() > 0)
- sb.deleteCharAt(sb.length() - 1);
- return sb.toString();
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
-
- sb.append("BSSID: ").append("(").append(bssid).append(")");
-
- if (venueName != null)
- sb.append(" venueName: ").append("(")
- .append(venueName.replace("\n", "\\n")).append(")");
-
- if (networkAuthTypeList != null) {
- sb.append(" networkAuthType: ");
- for (NetworkAuthType auth : networkAuthTypeList)
- sb.append("(").append(auth.toString()).append(")");
- }
-
- if (roamingConsortiumList != null) {
- sb.append(" roamingConsortium: ");
- for (String oi : roamingConsortiumList)
- sb.append("(").append(oi).append(")");
- }
-
- if (ipAddrTypeAvailability != null) {
- sb.append(" ipAddrTypeAvaibility: ").append("(")
- .append(ipAddrTypeAvailability.toString()).append(")");
- }
-
- if (naiRealmList != null) {
- sb.append(" naiRealm: ");
- for (NaiRealm realm : naiRealmList)
- sb.append("(").append(realm.toString()).append(")");
- }
-
- if (cellularNetworkList != null) {
- sb.append(" cellularNetwork: ");
- for (CellularNetwork plmn : cellularNetworkList)
- sb.append("(").append(plmn.toString()).append(")");
- }
-
- if (domainNameList != null) {
- sb.append(" domainName: ");
- for (String fqdn : domainNameList)
- sb.append("(").append(fqdn).append(")");
- }
-
- if (operatorFriendlyName != null)
- sb.append(" operatorFriendlyName: ").append("(")
- .append(operatorFriendlyName).append(")");
-
- if (wanMetrics != null)
- sb.append(" wanMetrics: ").append("(")
- .append(wanMetrics.toString()).append(")");
-
- if (connectionCapabilityList != null) {
- sb.append(" connectionCapability: ");
- for (IpProtoPort ip : connectionCapabilityList)
- sb.append("(").append(ip.toString()).append(")");
- }
-
- if (osuProviderList != null) {
- sb.append(" osuProviderList: ");
- for (WifiPasspointOsuProvider osu : osuProviderList)
- sb.append("(").append(osu.toString()).append(")");
- }
-
- return sb.toString();
- }
-
- /** Implement the Parcelable interface {@hide} */
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(bssid);
- out.writeString(venueName);
-
- if (networkAuthTypeList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(networkAuthTypeList.size());
- for (NetworkAuthType auth : networkAuthTypeList) {
- out.writeInt(auth.type);
- out.writeString(auth.redirectUrl);
- }
- }
-
- if (roamingConsortiumList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(roamingConsortiumList.size());
- for (String oi : roamingConsortiumList)
- out.writeString(oi);
- }
-
- if (ipAddrTypeAvailability == null) {
- out.writeInt(IpAddressType.NULL_VALUE);
- } else {
- out.writeInt(ipAddrTypeAvailability.availability);
- }
-
- if (naiRealmList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(naiRealmList.size());
- for (NaiRealm realm : naiRealmList) {
- out.writeInt(realm.encoding);
- out.writeString(realm.realm);
- }
- }
-
- if (cellularNetworkList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(cellularNetworkList.size());
- for (CellularNetwork plmn : cellularNetworkList) {
- out.writeString(plmn.mcc);
- out.writeString(plmn.mnc);
- }
- }
-
-
- if (domainNameList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(domainNameList.size());
- for (String fqdn : domainNameList)
- out.writeString(fqdn);
- }
-
- out.writeString(operatorFriendlyName);
-
- if (wanMetrics == null) {
- out.writeInt(0);
- } else {
- out.writeInt(1);
- out.writeInt(wanMetrics.wanInfo);
- out.writeLong(wanMetrics.downlinkSpeed);
- out.writeLong(wanMetrics.uplinkSpeed);
- out.writeInt(wanMetrics.downlinkLoad);
- out.writeInt(wanMetrics.uplinkLoad);
- out.writeInt(wanMetrics.lmd);
- }
-
- if (connectionCapabilityList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(connectionCapabilityList.size());
- for (IpProtoPort ip : connectionCapabilityList) {
- out.writeInt(ip.proto);
- out.writeInt(ip.port);
- out.writeInt(ip.status);
- }
- }
-
- if (osuProviderList == null) {
- out.writeInt(0);
- } else {
- out.writeInt(osuProviderList.size());
- for (WifiPasspointOsuProvider osu : osuProviderList)
- osu.writeToParcel(out, flags);
- }
- }
-
- /** Implement the Parcelable interface {@hide} */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface {@hide} */
- public static final Parcelable.Creator<WifiPasspointInfo> CREATOR =
- new Parcelable.Creator<WifiPasspointInfo>() {
- @Override
- public WifiPasspointInfo createFromParcel(Parcel in) {
- WifiPasspointInfo p = new WifiPasspointInfo();
- int n;
-
- p.bssid = in.readString();
- p.venueName = in.readString();
-
- n = in.readInt();
- if (n > 0) {
- p.networkAuthTypeList = new ArrayList<NetworkAuthType>();
- for (int i = 0; i < n; i++) {
- NetworkAuthType auth = new NetworkAuthType();
- auth.type = in.readInt();
- auth.redirectUrl = in.readString();
- p.networkAuthTypeList.add(auth);
- }
- }
-
- n = in.readInt();
- if (n > 0) {
- p.roamingConsortiumList = new ArrayList<String>();
- for (int i = 0; i < n; i++)
- p.roamingConsortiumList.add(in.readString());
- }
-
- n = in.readInt();
- if (n != IpAddressType.NULL_VALUE) {
- p.ipAddrTypeAvailability = new IpAddressType();
- p.ipAddrTypeAvailability.availability = n;
- }
-
- n = in.readInt();
- if (n > 0) {
- p.naiRealmList = new ArrayList<NaiRealm>();
- for (int i = 0; i < n; i++) {
- NaiRealm realm = new NaiRealm();
- realm.encoding = in.readInt();
- realm.realm = in.readString();
- p.naiRealmList.add(realm);
- }
- }
-
- n = in.readInt();
- if (n > 0) {
- p.cellularNetworkList = new ArrayList<CellularNetwork>();
- for (int i = 0; i < n; i++) {
- CellularNetwork plmn = new CellularNetwork();
- plmn.mcc = in.readString();
- plmn.mnc = in.readString();
- p.cellularNetworkList.add(plmn);
- }
- }
-
- n = in.readInt();
- if (n > 0) {
- p.domainNameList = new ArrayList<String>();
- for (int i = 0; i < n; i++)
- p.domainNameList.add(in.readString());
- }
-
- p.operatorFriendlyName = in.readString();
-
- n = in.readInt();
- if (n > 0) {
- p.wanMetrics = new WanMetrics();
- p.wanMetrics.wanInfo = in.readInt();
- p.wanMetrics.downlinkSpeed = in.readLong();
- p.wanMetrics.uplinkSpeed = in.readLong();
- p.wanMetrics.downlinkLoad = in.readInt();
- p.wanMetrics.uplinkLoad = in.readInt();
- p.wanMetrics.lmd = in.readInt();
- }
-
- n = in.readInt();
- if (n > 0) {
- p.connectionCapabilityList = new ArrayList<IpProtoPort>();
- for (int i = 0; i < n; i++) {
- IpProtoPort ip = new IpProtoPort();
- ip.proto = in.readInt();
- ip.port = in.readInt();
- ip.status = in.readInt();
- p.connectionCapabilityList.add(ip);
- }
- }
-
- n = in.readInt();
- if (n > 0) {
- p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
- for (int i = 0; i < n; i++) {
- WifiPasspointOsuProvider osu = WifiPasspointOsuProvider.CREATOR
- .createFromParcel(in);
- p.osuProviderList.add(osu);
- }
- }
-
- return p;
- }
-
- @Override
- public WifiPasspointInfo[] newArray(int size) {
- return new WifiPasspointInfo[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
deleted file mode 100644
index 0245a3d..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.content.Context;
-import android.net.wifi.ScanResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Provides APIs for managing Wifi Passpoint credentials.
- * @hide
- */
-public class WifiPasspointManager {
-
- private static final String TAG = "PasspointManager";
-
- private static final boolean DBG = true;
-
- /* Passpoint states values */
-
- /** Passpoint is in an unknown state. This should only occur in boot time */
- public static final int PASSPOINT_STATE_UNKNOWN = 0;
-
- /** Passpoint is disabled. This occurs when wifi is disabled */
- public static final int PASSPOINT_STATE_DISABLED = 1;
-
- /** Passpoint is enabled and in discovery state */
- public static final int PASSPOINT_STATE_DISCOVERY = 2;
-
- /** Passpoint is enabled and in access state */
- public static final int PASSPOINT_STATE_ACCESS = 3;
-
- /** Passpoint is enabled and in provisioning state */
- public static final int PASSPOINT_STATE_PROVISION = 4;
-
- /* Passpoint callback error codes */
-
- /** Indicates that the operation failed due to an internal error */
- public static final int REASON_ERROR = 0;
-
- /** Indicates that the operation failed because wifi is disabled */
- public static final int REASON_WIFI_DISABLED = 1;
-
- /** Indicates that the operation failed because the framework is busy */
- public static final int REASON_BUSY = 2;
-
- /** Indicates that the operation failed because parameter is invalid */
- public static final int REASON_INVALID_PARAMETER = 3;
-
- /** Indicates that the operation failed because the server is not trusted */
- public static final int REASON_NOT_TRUSTED = 4;
-
- /**
- * protocol supported for Passpoint
- */
- public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated";
-
- /**
- * protocol supported for Passpoint
- */
- public static final String PROTOCOL_SOAP = "SPP-ClientInitiated";
-
- /* Passpoint broadcasts */
-
- /**
- * Broadcast intent action indicating that the state of Passpoint
- * connectivity has changed
- */
- public static final String PASSPOINT_STATE_CHANGED_ACTION =
- "android.net.wifi.passpoint.STATE_CHANGE";
-
- /**
- * Broadcast intent action indicating that the saved Passpoint credential
- * list has changed
- */
- public static final String PASSPOINT_CRED_CHANGED_ACTION =
- "android.net.wifi.passpoint.CRED_CHANGE";
-
- /**
- * Broadcast intent action indicating that Passpoint online sign up is
- * avaiable.
- */
- public static final String PASSPOINT_OSU_AVAILABLE_ACTION =
- "android.net.wifi.passpoint.OSU_AVAILABLE";
-
- /**
- * Broadcast intent action indicating that user remediation is required
- */
- public static final String PASSPOINT_USER_REM_REQ_ACTION =
- "android.net.wifi.passpoint.USER_REM_REQ";
-
- /**
- * Interface for callback invocation when framework channel is lost
- */
- public interface ChannelListener {
- /**
- * The channel to the framework has been disconnected. Application could
- * try re-initializing using {@link #initialize}
- */
- public void onChannelDisconnected();
- }
-
- /**
- * Interface for callback invocation on an application action
- */
- public interface ActionListener {
- /** The operation succeeded */
- public void onSuccess();
-
- /**
- * The operation failed
- *
- * @param reason The reason for failure could be one of
- * {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
- */
- public void onFailure(int reason);
- }
-
- /**
- * Interface for callback invocation when doing OSU or user remediation
- */
- public interface OsuRemListener {
- /** The operation succeeded */
- public void onSuccess();
-
- /**
- * The operation failed
- *
- * @param reason The reason for failure could be one of
- * {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
- */
- public void onFailure(int reason);
-
- /**
- * Browser launch is requried for user interaction. When this callback
- * is called, app should launch browser / webview to the given URI.
- *
- * @param uri URI for browser launch
- */
- public void onBrowserLaunch(String uri);
-
- /**
- * When this is called, app should dismiss the previously lanched browser.
- */
- public void onBrowserDismiss();
- }
-
- /**
- * A channel that connects the application to the wifi passpoint framework.
- * Most passpoint operations require a Channel as an argument.
- * An instance of Channel is obtained by doing a call on {@link #initialize}
- */
- public static class Channel {
- private final static int INVALID_LISTENER_KEY = 0;
-
- private ChannelListener mChannelListener;
-
- private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
- private HashMap<Integer, Integer> mListenerMapCount = new HashMap<Integer, Integer>();
- private Object mListenerMapLock = new Object();
- private int mListenerKey = 0;
-
- private List<ScanResult> mAnqpRequest = new LinkedList<ScanResult>();
- private Object mAnqpRequestLock = new Object();
-
- private AsyncChannel mAsyncChannel;
- private PasspointHandler mHandler;
- Context mContext;
-
- Channel(Context context, Looper looper, ChannelListener l) {
- mAsyncChannel = new AsyncChannel();
- mHandler = new PasspointHandler(looper);
- mChannelListener = l;
- mContext = context;
- }
-
- private int putListener(Object listener) {
- return putListener(listener, 1);
- }
-
- private int putListener(Object listener, int count) {
- if (listener == null || count <= 0)
- return INVALID_LISTENER_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_LISTENER_KEY);
- mListenerMap.put(key, listener);
- mListenerMapCount.put(key, count);
- }
- return key;
- }
-
- private Object peekListener(int key) {
- Log.d(TAG, "peekListener() key=" + key);
- if (key == INVALID_LISTENER_KEY)
- return null;
- synchronized (mListenerMapLock) {
- return mListenerMap.get(key);
- }
- }
-
-
- private Object getListener(int key, boolean forceRemove) {
- Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove);
- if (key == INVALID_LISTENER_KEY)
- return null;
- synchronized (mListenerMapLock) {
- if (!forceRemove) {
- int count = mListenerMapCount.get(key);
- Log.d(TAG, "count=" + count);
- mListenerMapCount.put(key, --count);
- if (count > 0)
- return null;
- }
- Log.d(TAG, "remove key");
- mListenerMapCount.remove(key);
- return mListenerMap.remove(key);
- }
- }
-
- private void anqpRequestStart(ScanResult sr) {
- Log.d(TAG, "anqpRequestStart sr.bssid=" + sr.BSSID);
- synchronized (mAnqpRequestLock) {
- mAnqpRequest.add(sr);
- }
- }
-
- private void anqpRequestFinish(WifiPasspointInfo result) {
- Log.d(TAG, "anqpRequestFinish pi.bssid=" + result.bssid);
- synchronized (mAnqpRequestLock) {
- for (ScanResult sr : mAnqpRequest)
- if (sr.BSSID.equals(result.bssid)) {
- Log.d(TAG, "find hit " + result.bssid);
- /* sr.passpoint = result; */
- mAnqpRequest.remove(sr);
- Log.d(TAG, "mAnqpRequest.len=" + mAnqpRequest.size());
- break;
- }
- }
- }
-
- private void anqpRequestFinish(ScanResult sr) {
- Log.d(TAG, "anqpRequestFinish sr.bssid=" + sr.BSSID);
- synchronized (mAnqpRequestLock) {
- for (ScanResult sr1 : mAnqpRequest)
- if (sr1.BSSID.equals(sr.BSSID)) {
- mAnqpRequest.remove(sr1);
- break;
- }
- }
- }
-
- class PasspointHandler extends Handler {
- PasspointHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- Object listener = null;
-
- switch (message.what) {
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- if (mChannelListener != null) {
- mChannelListener.onChannelDisconnected();
- mChannelListener = null;
- }
- break;
-
- case REQUEST_ANQP_INFO_SUCCEEDED:
- WifiPasspointInfo result = (WifiPasspointInfo) message.obj;
- anqpRequestFinish(result);
- listener = getListener(message.arg2, false);
- if (listener != null) {
- ((ActionListener) listener).onSuccess();
- }
- break;
-
- case REQUEST_ANQP_INFO_FAILED:
- anqpRequestFinish((ScanResult) message.obj);
- listener = getListener(message.arg2, false);
- if (listener == null)
- getListener(message.arg2, true);
- if (listener != null) {
- ((ActionListener) listener).onFailure(message.arg1);
- }
- break;
-
- case START_OSU_SUCCEEDED:
- listener = getListener(message.arg2, true);
- if (listener != null) {
- ((OsuRemListener) listener).onSuccess();
- }
- break;
-
- case START_OSU_FAILED:
- listener = getListener(message.arg2, true);
- if (listener != null) {
- ((OsuRemListener) listener).onFailure(message.arg1);
- }
- break;
-
- case START_OSU_BROWSER:
- listener = peekListener(message.arg2);
- if (listener != null) {
- ParcelableString str = (ParcelableString) message.obj;
- if (str == null || str.string == null)
- ((OsuRemListener) listener).onBrowserDismiss();
- else
- ((OsuRemListener) listener).onBrowserLaunch(str.string);
- }
- break;
-
- default:
- Log.d(TAG, "Ignored " + message);
- break;
- }
- }
- }
-
- }
-
- public static class ParcelableString implements Parcelable {
- public String string;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(string);
- }
-
- public static final Parcelable.Creator<ParcelableString> CREATOR =
- new Parcelable.Creator<ParcelableString>() {
- @Override
- public ParcelableString createFromParcel(Parcel in) {
- ParcelableString ret = new ParcelableString();
- ret.string = in.readString();
- return ret;
- }
- @Override
- public ParcelableString[] newArray(int size) {
- return new ParcelableString[size];
- }
- };
- }
-
- private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER;
-
- public static final int REQUEST_ANQP_INFO = BASE + 1;
- public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2;
- public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3;
- public static final int REQUEST_OSU_ICON = BASE + 4;
- public static final int REQUEST_OSU_ICON_FAILED = BASE + 5;
- public static final int REQUEST_OSU_ICON_SUCCEEDED = BASE + 6;
- public static final int START_OSU = BASE + 7;
- public static final int START_OSU_BROWSER = BASE + 8;
- public static final int START_OSU_FAILED = BASE + 9;
- public static final int START_OSU_SUCCEEDED = BASE + 10;
-
- private Context mContext;
- IWifiPasspointManager mService;
-
- /**
- * TODO: doc
- * @param context
- * @param service
- */
- public WifiPasspointManager(Context context, IWifiPasspointManager service) {
- mContext = context;
- mService = service;
- }
-
- /**
- * Registers the application with the framework. This function must be the
- * first to be called before any async passpoint operations are performed.
- *
- * @param srcContext is the context of the source
- * @param srcLooper is the Looper on which the callbacks are receivied
- * @param listener for callback at loss of framework communication. Can be
- * null.
- * @return Channel instance that is necessary for performing any further
- * passpoint operations
- *
- */
- public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
- Messenger messenger = getMessenger();
- if (messenger == null)
- return null;
-
- Channel c = new Channel(srcContext, srcLooper, listener);
- if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
- == AsyncChannel.STATUS_SUCCESSFUL) {
- return c;
- } else {
- return null;
- }
- }
-
- /**
- * STOPSHIP: temp solution, should use supplicant manager instead, check
- * with b/13931972
- */
- public Messenger getMessenger() {
- try {
- return mService.getMessenger();
- } catch (RemoteException e) {
- return null;
- }
- }
-
- public int getPasspointState() {
- try {
- return mService.getPasspointState();
- } catch (RemoteException e) {
- return PASSPOINT_STATE_UNKNOWN;
- }
- }
-
- public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask,
- ActionListener listener) {
- Log.d(TAG, "requestAnqpInfo start");
- Log.d(TAG, "requested.size=" + requested.size());
- checkChannel(c);
- List<ScanResult> list = new ArrayList<ScanResult>();
- for (ScanResult sr : requested)
- if (sr.capabilities.contains("[HS20]")) {
- list.add(sr);
- c.anqpRequestStart(sr);
- Log.d(TAG, "adding " + sr.BSSID);
- }
- int count = list.size();
- Log.d(TAG, "after filter, count=" + count);
- if (count == 0) {
- if (DBG)
- Log.d(TAG, "ANQP info request contains no HS20 APs, skipped");
- listener.onSuccess();
- return;
- }
- int key = c.putListener(listener, count);
- for (ScanResult sr : list)
- c.mAsyncChannel.sendMessage(REQUEST_ANQP_INFO, mask, key, sr);
- Log.d(TAG, "requestAnqpInfo end");
- }
-
- public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested,
- int resolution, ActionListener listener) {
- }
-
- public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) {
- try {
- return mService.requestCredentialMatch(requested);
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * Get a list of saved Passpoint credentials. Only those credentials owned
- * by the caller will be returned.
- *
- * @return The list of credentials
- */
- public List<WifiPasspointCredential> getCredentials() {
- try {
- return mService.getCredentials();
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * Add a new Passpoint credential.
- *
- * @param cred The credential to be added
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean addCredential(WifiPasspointCredential cred) {
- try {
- return mService.addCredential(cred);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Update an existing Passpoint credential. Only system or the owner of this
- * credential has the permission to do this.
- *
- * @param cred The credential to be updated
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean updateCredential(WifiPasspointCredential cred) {
- try {
- return mService.updateCredential(cred);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Remove an existing Passpoint credential. Only system or the owner of this
- * credential has the permission to do this.
- *
- * @param cred The credential to be removed
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean removeCredential(WifiPasspointCredential cred) {
- try {
- return mService.removeCredential(cred);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {
- Log.d(TAG, "startOsu start");
- checkChannel(c);
- int key = c.putListener(listener);
- c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu);
- Log.d(TAG, "startOsu end");
- }
-
- public void startRemediation(Channel c, OsuRemListener listener) {
- }
-
- public void connect(WifiPasspointPolicy policy) {
- }
-
- private static void checkChannel(Channel c) {
- if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
- }
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
deleted file mode 100644
index 088136f..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointOsuProvider;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
deleted file mode 100644
index b54b70c..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class WifiPasspointOsuProvider implements Parcelable {
-
- /** TODO: doc
- * @hide
- */
- public static final int OSU_METHOD_UNKNOWN = -1;
-
- /** TODO: doc
- * @hide
- */
- public static final int OSU_METHOD_OMADM = 0;
-
- /** TODO: doc
- * @hide
- */
- public static final int OSU_METHOD_SOAP = 1;
-
- /** TODO: doc */
- public String ssid;
-
- /** TODO: doc */
- public String friendlyName;
-
- /** TODO: doc
- * @hide
- */
- public String serverUri;
-
- /** TODO: doc
- * @hide
- */
- public int osuMethod = OSU_METHOD_UNKNOWN;
-
- /** TODO: doc */
- public int iconWidth;
-
- /** TODO: doc */
- public int iconHeight;
-
- /** TODO: doc */
- public String iconType;
-
- /** TODO: doc */
- public String iconFileName;
-
- /** TODO: doc */
- public Object icon; // TODO: should change to image format
-
- /** TODO: doc */
- public String osuNai;
-
- /** TODO: doc */
- public String osuService;
-
- /** default constructor @hide */
- public WifiPasspointOsuProvider() {
- // TODO
- }
-
- /** copy constructor @hide */
- public WifiPasspointOsuProvider(WifiPasspointOsuProvider source) {
- // TODO
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("SSID: ").append("<").append(ssid).append(">");
- if (friendlyName != null)
- sb.append(" friendlyName: ").append("<").append(friendlyName).append(">");
- if (serverUri != null)
- sb.append(" serverUri: ").append("<").append(serverUri).append(">");
- sb.append(" osuMethod: ").append("<").append(osuMethod).append(">");
- if (iconFileName != null) {
- sb.append(" icon: <").append(iconWidth).append("x")
- .append(iconHeight).append(" ")
- .append(iconType).append(" ")
- .append(iconFileName).append(">");
- }
- if (osuNai != null)
- sb.append(" osuNai: ").append("<").append(osuNai).append(">");
- if (osuService != null)
- sb.append(" osuService: ").append("<").append(osuService).append(">");
- return sb.toString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(ssid);
- out.writeString(friendlyName);
- out.writeString(serverUri);
- out.writeInt(osuMethod);
- out.writeInt(iconWidth);
- out.writeInt(iconHeight);
- out.writeString(iconType);
- out.writeString(iconFileName);
- out.writeString(osuNai);
- out.writeString(osuService);
- // TODO: icon image?
- }
-
- public static final Parcelable.Creator<WifiPasspointOsuProvider> CREATOR =
- new Parcelable.Creator<WifiPasspointOsuProvider>() {
- @Override
- public WifiPasspointOsuProvider createFromParcel(Parcel in) {
- WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider();
- osu.ssid = in.readString();
- osu.friendlyName = in.readString();
- osu.serverUri = in.readString();
- osu.osuMethod = in.readInt();
- osu.iconWidth = in.readInt();
- osu.iconHeight = in.readInt();
- osu.iconType = in.readString();
- osu.iconFileName = in.readString();
- osu.osuNai = in.readString();
- osu.osuService = in.readString();
- return osu;
- }
-
- @Override
- public WifiPasspointOsuProvider[] newArray(int size) {
- return new WifiPasspointOsuProvider[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
deleted file mode 100644
index 1d61da0..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointPolicy;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
deleted file mode 100644
index c08877e..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.WifiConfiguration;
-import android.os.Parcelable;
-import android.os.Parcel;
-import android.security.Credentials;
-import android.util.Log;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-
-/** @hide */
-public class WifiPasspointPolicy implements Parcelable {
-
- private final static String TAG = "PasspointPolicy";
-
- /** @hide */
- public static final int HOME_SP = 0;
-
- /** @hide */
- public static final int ROAMING_PARTNER = 1;
-
- /** @hide */
- public static final int UNRESTRICTED = 2;
-
- private String mName;
- private int mCredentialPriority;
- private int mRoamingPriority;
- private String mBssid;
- private String mSsid;
- private WifiPasspointCredential mCredential;
- private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
- private boolean mIsHomeSp;
-
- private final String INT_PRIVATE_KEY = "private_key";
- private final String INT_PHASE2 = "phase2";
- private final String INT_PASSWORD = "password";
- private final String INT_IDENTITY = "identity";
- private final String INT_EAP = "eap";
- private final String INT_CLIENT_CERT = "client_cert";
- private final String INT_CA_CERT = "ca_cert";
- private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
- private final String INT_SIM_SLOT = "sim_slot";
- private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
- private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
- private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
- private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
-
- /** @hide */
- public WifiPasspointPolicy(String name, String ssid,
- String bssid, WifiPasspointCredential pc,
- int restriction, boolean ishomesp) {
- mName = name;
- if (pc != null) {
- mCredentialPriority = pc.getPriority();
- }
- //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
- mRoamingPriority = 128; //default priority value of 128
- mSsid = ssid;
- mCredential = pc;
- mBssid = bssid;
- mRestriction = restriction;
- mIsHomeSp = ishomesp;
- }
-
- public String getSsid() {
- return mSsid;
- }
-
- /** @hide */
- public void setBssid(String bssid) {
- mBssid = bssid;
- }
-
- public String getBssid() {
- return mBssid;
- }
-
- /** @hide */
- public void setRestriction(int r) {
- mRestriction = r;
- }
-
- /** @hide */
- public int getRestriction() {
- return mRestriction;
- }
-
- /** @hide */
- public void setHomeSp(boolean b) {
- mIsHomeSp = b;
- }
-
- /** @hide */
- public boolean isHomeSp() {
- return mIsHomeSp;
- }
-
- /** @hide */
- public void setCredential(WifiPasspointCredential newCredential) {
- mCredential = newCredential;
- }
-
- public WifiPasspointCredential getCredential() {
- // TODO: return a copy
- return mCredential;
- }
-
- /** @hide */
- public void setCredentialPriority(int priority) {
- mCredentialPriority = priority;
- }
-
- /** @hide */
- public void setRoamingPriority(int priority) {
- mRoamingPriority = priority;
- }
-
- public int getCredentialPriority() {
- return mCredentialPriority;
- }
-
- public int getRoamingPriority() {
- return mRoamingPriority;
- }
-
- public WifiConfiguration createWifiConfiguration() {
- WifiConfiguration wfg = new WifiConfiguration();
- if (mBssid != null) {
- Log.d(TAG, "create bssid:" + mBssid);
- wfg.BSSID = mBssid;
- }
-
- if (mSsid != null) {
- Log.d(TAG, "create ssid:" + mSsid);
- wfg.SSID = mSsid;
- }
- //TODO: 1. add pmf configuration
- // 2. add ocsp configuration
- // 3. add eap-sim configuration
- /*Key management*/
- wfg.status = WifiConfiguration.Status.ENABLED;
- wfg.allowedKeyManagement.clear();
- wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
-
- /*Group Ciphers*/
- wfg.allowedGroupCiphers.clear();
- wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
- wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
-
- /*Protocols*/
- wfg.allowedProtocols.clear();
- wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
- wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
-
- Class[] enterpriseFieldArray = WifiConfiguration.class.getClasses();
- Class<?> enterpriseFieldClass = null;
-
-
- for(Class<?> myClass : enterpriseFieldArray) {
- if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
- enterpriseFieldClass = myClass;
- break;
- }
- }
- Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
-
-
- Field anonymousId = null, caCert = null, clientCert = null,
- eap = null, identity = null, password = null,
- phase2 = null, privateKey = null;
-
- Field[] fields = WifiConfiguration.class.getFields();
-
-
- for (Field tempField : fields) {
- if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
- anonymousId = tempField;
- Log.d(TAG, "field " + anonymousId.getName() );
- } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
- caCert = tempField;
- } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
- clientCert = tempField;
- Log.d(TAG, "field " + clientCert.getName() );
- } else if (tempField.getName().trim().equals(INT_EAP)) {
- eap = tempField;
- Log.d(TAG, "field " + eap.getName() );
- } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
- identity = tempField;
- Log.d(TAG, "field " + identity.getName() );
- } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
- password = tempField;
- Log.d(TAG, "field " + password.getName() );
- } else if (tempField.getName().trim().equals(INT_PHASE2)) {
- phase2 = tempField;
- Log.d(TAG, "field " + phase2.getName() );
-
- } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
- privateKey = tempField;
- }
- }
-
-
- Method setValue = null;
-
- for(Method m: enterpriseFieldClass.getMethods()) {
- if(m.getName().trim().equals("setValue")) {
- Log.d(TAG, "method " + m.getName() );
- setValue = m;
- break;
- }
- }
-
- try {
- // EAP
- String eapmethod = mCredential.getType();
- Log.d(TAG, "eapmethod:" + eapmethod);
- setValue.invoke(eap.get(wfg), eapmethod);
-
- // Username, password, EAP Phase 2
- if ("TTLS".equals(eapmethod)) {
- setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
- setValue.invoke(identity.get(wfg), mCredential.getUserName());
- setValue.invoke(password.get(wfg), mCredential.getPassword());
- setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
- }
-
- // EAP CA Certificate
- String cacertificate = null;
- String rootCA = mCredential.getCaRootCertPath();
- if (rootCA == null){
- cacertificate = null;
- } else {
- cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
- }
- Log.d(TAG, "cacertificate:" + cacertificate);
- setValue.invoke(caCert.get(wfg), cacertificate);
-
- //User certificate
- if ("TLS".equals(eapmethod)) {
- String usercertificate = null;
- String privatekey = null;
- String clientCertPath = mCredential.getClientCertPath();
- if (clientCertPath != null){
- privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
- usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
- }
- Log.d(TAG, "privatekey:" + privatekey);
- Log.d(TAG, "usercertificate:" + usercertificate);
- if (privatekey != null && usercertificate != null) {
- setValue.invoke(privateKey.get(wfg), privatekey);
- setValue.invoke(clientCert.get(wfg), usercertificate);
- }
- }
- } catch (Exception e) {
- Log.d(TAG, "createWifiConfiguration err:" + e);
- }
-
- return wfg;
- }
-
- /** {@inheritDoc} @hide */
- public int compareTo(WifiPasspointPolicy another) {
- Log.d(TAG, "this:" + this);
- Log.d(TAG, "another:" + another);
-
- if (another == null) {
- return -1;
- } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
- //home sp priority is higher then roaming
- Log.d(TAG, "compare HomeSP first, this is HomeSP, another isn't");
- return -1;
- } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
- Log.d(TAG, "both HomeSP");
- //if both home sp, compare credential priority
- if (this.mCredentialPriority < another.getCredentialPriority()) {
- Log.d(TAG, "this priority is higher");
- return -1;
- } else if (this.mCredentialPriority == another.getCredentialPriority()) {
- Log.d(TAG, "both priorities equal");
- //if priority still the same, compare name(ssid)
- if (this.mName.compareTo(another.mName) != 0) {
- Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
- return this.mName.compareTo(another.mName);
- }
- /**
- *if name still the same, compare credential
- *the device may has two more credentials(TLS,SIM..etc)
- *it can associate to one AP(same ssid). so we should compare by credential
- */
- if (this.mCredential != null && another.mCredential != null) {
- if (this.mCredential.compareTo(another.mCredential) != 0) {
- Log.d(TAG,
- "compare mCredential return:" + this.mName.compareTo(another.mName));
- return this.mCredential.compareTo(another.mCredential);
- }
- }
- } else {
- return 1;
- }
- } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
- Log.d(TAG, "both RoamingSp");
- //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
- if (this.mRoamingPriority < another.getRoamingPriority()) {
- Log.d(TAG, "this priority is higher");
- return -1;
- } else if (this.mRoamingPriority == another.getRoamingPriority()) {//priority equals, compare name
- Log.d(TAG, "both priorities equal");
- //if priority still the same, compare name(ssid)
- if (this.mName.compareTo(another.mName) != 0) {
- Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
- return this.mName.compareTo(another.mName);
- }
- //if name still the same, compare credential
- if (this.mCredential != null && another.mCredential != null) {
- if (this.mCredential.compareTo(another.mCredential) != 0) {
- Log.d(TAG,
- "compare mCredential return:"
- + this.mCredential.compareTo(another.mCredential));
- return this.mCredential.compareTo(another.mCredential);
- }
- }
- } else {
- return 1;
- }
- }
-
- Log.d(TAG, "both policies equal");
- return 0;
- }
-
- @Override
- /** @hide */
- public String toString() {
- return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
- " mRoamingPriority" + mRoamingPriority +
- " ssid=" + mSsid + " restriction=" + mRestriction +
- " ishomesp=" + mIsHomeSp + " Credential=" + mCredential;
- }
-
- /** Implement the Parcelable interface {@hide} */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface {@hide} */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- // TODO
- }
-
- /** Implement the Parcelable interface {@hide} */
- public static final Creator<WifiPasspointPolicy> CREATOR =
- new Creator<WifiPasspointPolicy>() {
- @Override
- public WifiPasspointPolicy createFromParcel(Parcel in) {
- return null;
- }
-
- @Override
- public WifiPasspointPolicy[] newArray(int size) {
- return new WifiPasspointPolicy[size];
- }
- };
-}