Merge "Add getFormat to AudioTrack and AudioRecord"
diff --git a/api/current.txt b/api/current.txt
index f660cd4..69f18e2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,6 +299,7 @@
field public static final int anyDensity = 16843372; // 0x101026c
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
+ field public static final int assistBlocked = 16844020; // 0x10104f4
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -1723,6 +1724,7 @@
field public static final int selectAll = 16908319; // 0x102001f
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
+ field public static final int shareText = 16908343; // 0x1020037
field public static final int startSelectingText = 16908328; // 0x1020028
field public static final int statusBarBackground = 16908335; // 0x102002f
field public static final int stopSelectingText = 16908329; // 0x1020029
@@ -4070,6 +4072,7 @@
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -5008,6 +5011,7 @@
field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+ field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
@@ -5651,6 +5655,7 @@
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
+ method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -14920,6 +14925,7 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -14930,7 +14936,9 @@
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -14941,8 +14949,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -14983,6 +14993,7 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -15002,6 +15013,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -15010,6 +15022,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -15276,6 +15289,7 @@
method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException;
method public final android.view.Surface createInputSurface();
+ method public static android.view.Surface createPersistentInputSurface();
method public final int dequeueInputBuffer(long);
method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
method public final void flush();
@@ -15296,12 +15310,16 @@
method public final void releaseOutputBuffer(int, boolean);
method public final void releaseOutputBuffer(int, long);
method public final void reset();
+ method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
+ method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler);
method public final void setParameters(android.os.Bundle);
+ method public void setSurface(android.view.Surface);
method public final void setVideoScalingMode(int);
method public final void signalEndOfInputStream();
method public final void start();
method public final void stop();
+ method public void usePersistentInputSurface(android.view.Surface);
field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -15365,6 +15383,10 @@
field public int numSubSamples;
}
+ public static abstract interface MediaCodec.OnFrameRenderedListener {
+ method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
+ }
+
public final class MediaCodecInfo {
method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
method public final java.lang.String getName();
@@ -15572,6 +15594,7 @@
public static final class MediaCodecInfo.VideoCapabilities {
method public boolean areSizeAndRateSupported(int, int, double);
+ method public android.util.Range<java.lang.Double> getAchievableFrameRatesFor(int, int);
method public android.util.Range<java.lang.Integer> getBitrateRange();
method public int getHeightAlignment();
method public android.util.Range<java.lang.Integer> getSupportedFrameRates();
@@ -15990,7 +16013,10 @@
method public int getAudioSessionId();
method public int getCurrentPosition();
method public int getDuration();
+ method public android.media.PlaybackSettings getPlaybackSettings();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
+ method public android.media.SyncSettings getSyncSettings();
+ method public android.media.MediaTimestamp getTimestamp();
method public android.media.MediaPlayer.TrackInfo[] getTrackInfo() throws java.lang.IllegalStateException;
method public int getVideoHeight();
method public int getVideoWidth();
@@ -16026,8 +16052,10 @@
method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
method public void setPlaybackRate(float, int);
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
method public void setScreenOnWhilePlaying(boolean);
method public void setSurface(android.view.Surface);
+ method public void setSyncSettings(android.media.SyncSettings);
method public void setVideoScalingMode(int);
method public void setVolume(float, float);
method public void setWakeMode(android.content.Context, int);
@@ -16051,7 +16079,9 @@
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
- field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
}
@@ -16139,6 +16169,7 @@
method public void setVideoSource(int) throws java.lang.IllegalStateException;
method public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ method public void usePersistentSurface(android.view.Surface);
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
@@ -16341,15 +16372,22 @@
public final class MediaSync {
ctor public MediaSync();
- method public void configureAudioTrack(android.media.AudioTrack);
- method public void configureSurface(android.view.Surface);
method public final android.view.Surface createInputSurface();
- method public boolean getTimestamp(android.media.MediaTimestamp);
+ method public void flush();
+ method public android.media.PlaybackSettings getPlaybackSettings();
+ method public android.media.SyncSettings getSyncSettings();
+ method public android.media.MediaTimestamp getTimestamp();
method public void queueAudio(java.nio.ByteBuffer, int, int, long);
method public final void release();
+ method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
method public void setPlaybackRate(float, int);
- field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
+ method public void setSurface(android.view.Surface);
+ method public void setSyncSettings(android.media.SyncSettings);
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
}
public static abstract class MediaSync.Callback {
@@ -16367,10 +16405,9 @@
}
public final class MediaTimestamp {
- ctor public MediaTimestamp();
- field public float clockRate;
- field public long mediaTimeUs;
- field public long nanoTime;
+ field public final float clockRate;
+ field public final long mediaTimeUs;
+ field public final long nanoTime;
}
public final class NotProvisionedException extends android.media.MediaDrmException {
@@ -16399,6 +16436,14 @@
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -16584,6 +16629,26 @@
method public abstract void onLoadComplete(android.media.SoundPool, int, int);
}
+ public final class SyncSettings {
+ ctor public SyncSettings();
+ method public android.media.SyncSettings allowDefaults();
+ method public int getAudioAdjustMode();
+ method public float getFrameRate();
+ method public int getSyncSource();
+ method public float getTolerance();
+ method public android.media.SyncSettings setAudioAdjustMode(int);
+ method public android.media.SyncSettings setFrameRate(float);
+ method public android.media.SyncSettings setSyncSource(int);
+ method public android.media.SyncSettings setTolerance(float);
+ field public static final int AUDIO_ADJUST_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_ADJUST_MODE_RESAMPLE = 2; // 0x2
+ field public static final int AUDIO_ADJUST_MODE_STRETCH = 1; // 0x1
+ field public static final int SYNC_SOURCE_AUDIO = 2; // 0x2
+ field public static final int SYNC_SOURCE_DEFAULT = 0; // 0x0
+ field public static final int SYNC_SOURCE_SYSTEM_CLOCK = 1; // 0x1
+ field public static final int SYNC_SOURCE_VSYNC = 3; // 0x3
+ }
+
public class ThumbnailUtils {
ctor public ThumbnailUtils();
method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
@@ -18237,11 +18302,8 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
- method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
- method public deprecated int startUsingNetworkFeature(int, java.lang.String);
- method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
@@ -18260,9 +18322,9 @@
field public static final int TYPE_ETHERNET = 9; // 0x9
field public static final int TYPE_MOBILE = 0; // 0x0
field public static final int TYPE_MOBILE_DUN = 4; // 0x4
- field public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
- field public static final int TYPE_MOBILE_MMS = 2; // 0x2
- field public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+ field public static final deprecated int TYPE_MOBILE_HIPRI = 5; // 0x5
+ field public static final deprecated int TYPE_MOBILE_MMS = 2; // 0x2
+ field public static final deprecated int TYPE_MOBILE_SUPL = 3; // 0x3
field public static final int TYPE_VPN = 17; // 0x11
field public static final int TYPE_WIFI = 1; // 0x1
field public static final int TYPE_WIMAX = 6; // 0x6
@@ -26641,6 +26703,8 @@
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
+ field public static final java.lang.String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
+ field public static final java.lang.String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -30057,8 +30121,8 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -30118,20 +30182,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -30548,6 +30598,7 @@
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
method public void silenceRinger();
@@ -30612,6 +30663,7 @@
method public android.os.Bundle getConfigForSubId(int);
method public void reloadCarrierConfigForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
@@ -36008,6 +36060,7 @@
method public void invalidateOutline();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isAttachedToWindow();
method public boolean isClickable();
method public boolean isDirty();
@@ -36150,6 +36203,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAssistBlocked(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -36512,6 +36566,7 @@
method public abstract android.view.ViewAssistStructure newChild(int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
+ method public abstract void setAssistBlocked(boolean);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
diff --git a/api/removed.txt b/api/removed.txt
index 326b05d..a722e17 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -35,6 +35,13 @@
package android.net {
+ public class ConnectivityManager {
+ method public deprecated boolean requestRouteToHost(int, int);
+ method public deprecated boolean requestRouteToHostAddress(int, java.net.InetAddress);
+ method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
+ }
+
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index b789627..e873452 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -372,6 +372,7 @@
field public static final int anyDensity = 16843372; // 0x101026c
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
+ field public static final int assistBlocked = 16844020; // 0x10104f4
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -1800,6 +1801,7 @@
field public static final int selectAll = 16908319; // 0x102001f
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
+ field public static final int shareText = 16908343; // 0x1020037
field public static final int startSelectingText = 16908328; // 0x1020028
field public static final int statusBarBackground = 16908335; // 0x102002f
field public static final int stopSelectingText = 16908329; // 0x1020029
@@ -4161,6 +4163,7 @@
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -5099,6 +5102,7 @@
field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+ field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
@@ -5746,6 +5750,7 @@
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
+ method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -5851,6 +5856,7 @@
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
+ method public void notifyPendingSystemUpdate(long);
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
@@ -16130,6 +16136,7 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -16140,7 +16147,9 @@
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -16151,8 +16160,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -16195,6 +16206,7 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -16214,6 +16226,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -16222,6 +16235,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -16488,6 +16502,7 @@
method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException;
method public final android.view.Surface createInputSurface();
+ method public static android.view.Surface createPersistentInputSurface();
method public final int dequeueInputBuffer(long);
method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
method public final void flush();
@@ -16508,12 +16523,16 @@
method public final void releaseOutputBuffer(int, boolean);
method public final void releaseOutputBuffer(int, long);
method public final void reset();
+ method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
+ method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler);
method public final void setParameters(android.os.Bundle);
+ method public void setSurface(android.view.Surface);
method public final void setVideoScalingMode(int);
method public final void signalEndOfInputStream();
method public final void start();
method public final void stop();
+ method public void usePersistentInputSurface(android.view.Surface);
field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -16577,6 +16596,10 @@
field public int numSubSamples;
}
+ public static abstract interface MediaCodec.OnFrameRenderedListener {
+ method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
+ }
+
public final class MediaCodecInfo {
method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
method public final java.lang.String getName();
@@ -16784,6 +16807,7 @@
public static final class MediaCodecInfo.VideoCapabilities {
method public boolean areSizeAndRateSupported(int, int, double);
+ method public android.util.Range<java.lang.Double> getAchievableFrameRatesFor(int, int);
method public android.util.Range<java.lang.Integer> getBitrateRange();
method public int getHeightAlignment();
method public android.util.Range<java.lang.Integer> getSupportedFrameRates();
@@ -17203,7 +17227,10 @@
method public int getAudioSessionId();
method public int getCurrentPosition();
method public int getDuration();
+ method public android.media.PlaybackSettings getPlaybackSettings();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
+ method public android.media.SyncSettings getSyncSettings();
+ method public android.media.MediaTimestamp getTimestamp();
method public android.media.MediaPlayer.TrackInfo[] getTrackInfo() throws java.lang.IllegalStateException;
method public int getVideoHeight();
method public int getVideoWidth();
@@ -17239,8 +17266,10 @@
method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
method public void setPlaybackRate(float, int);
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
method public void setScreenOnWhilePlaying(boolean);
method public void setSurface(android.view.Surface);
+ method public void setSyncSettings(android.media.SyncSettings);
method public void setVideoScalingMode(int);
method public void setVolume(float, float);
method public void setWakeMode(android.content.Context, int);
@@ -17264,7 +17293,9 @@
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
- field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
}
@@ -17352,6 +17383,7 @@
method public void setVideoSource(int) throws java.lang.IllegalStateException;
method public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ method public void usePersistentSurface(android.view.Surface);
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
@@ -17556,15 +17588,22 @@
public final class MediaSync {
ctor public MediaSync();
- method public void configureAudioTrack(android.media.AudioTrack);
- method public void configureSurface(android.view.Surface);
method public final android.view.Surface createInputSurface();
- method public boolean getTimestamp(android.media.MediaTimestamp);
+ method public void flush();
+ method public android.media.PlaybackSettings getPlaybackSettings();
+ method public android.media.SyncSettings getSyncSettings();
+ method public android.media.MediaTimestamp getTimestamp();
method public void queueAudio(java.nio.ByteBuffer, int, int, long);
method public final void release();
+ method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
method public void setPlaybackRate(float, int);
- field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
+ method public void setSurface(android.view.Surface);
+ method public void setSyncSettings(android.media.SyncSettings);
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
+ field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
}
public static abstract class MediaSync.Callback {
@@ -17582,10 +17621,9 @@
}
public final class MediaTimestamp {
- ctor public MediaTimestamp();
- field public float clockRate;
- field public long mediaTimeUs;
- field public long nanoTime;
+ field public final float clockRate;
+ field public final long mediaTimeUs;
+ field public final long nanoTime;
}
public final class NotProvisionedException extends android.media.MediaDrmException {
@@ -17614,6 +17652,14 @@
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -17799,6 +17845,26 @@
method public abstract void onLoadComplete(android.media.SoundPool, int, int);
}
+ public final class SyncSettings {
+ ctor public SyncSettings();
+ method public android.media.SyncSettings allowDefaults();
+ method public int getAudioAdjustMode();
+ method public float getFrameRate();
+ method public int getSyncSource();
+ method public float getTolerance();
+ method public android.media.SyncSettings setAudioAdjustMode(int);
+ method public android.media.SyncSettings setFrameRate(float);
+ method public android.media.SyncSettings setSyncSource(int);
+ method public android.media.SyncSettings setTolerance(float);
+ field public static final int AUDIO_ADJUST_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_ADJUST_MODE_RESAMPLE = 2; // 0x2
+ field public static final int AUDIO_ADJUST_MODE_STRETCH = 1; // 0x1
+ field public static final int SYNC_SOURCE_AUDIO = 2; // 0x2
+ field public static final int SYNC_SOURCE_DEFAULT = 0; // 0x0
+ field public static final int SYNC_SOURCE_SYSTEM_CLOCK = 1; // 0x1
+ field public static final int SYNC_SOURCE_VSYNC = 3; // 0x3
+ }
+
public class ThumbnailUtils {
ctor public ThumbnailUtils();
method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
@@ -19695,11 +19761,8 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
- method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
- method public deprecated int startUsingNetworkFeature(int, java.lang.String);
- method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
@@ -19718,9 +19781,9 @@
field public static final int TYPE_ETHERNET = 9; // 0x9
field public static final int TYPE_MOBILE = 0; // 0x0
field public static final int TYPE_MOBILE_DUN = 4; // 0x4
- field public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
- field public static final int TYPE_MOBILE_MMS = 2; // 0x2
- field public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+ field public static final deprecated int TYPE_MOBILE_HIPRI = 5; // 0x5
+ field public static final deprecated int TYPE_MOBILE_MMS = 2; // 0x2
+ field public static final deprecated int TYPE_MOBILE_SUPL = 3; // 0x3
field public static final int TYPE_VPN = 17; // 0x11
field public static final int TYPE_WIFI = 1; // 0x1
field public static final int TYPE_WIMAX = 6; // 0x6
@@ -28643,6 +28706,8 @@
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
+ field public static final java.lang.String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
+ field public static final java.lang.String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -32162,8 +32227,9 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
+ field public static final deprecated int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -32227,20 +32293,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -32696,6 +32748,7 @@
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
method public void silenceRinger();
@@ -32765,6 +32818,7 @@
method public void reloadCarrierConfigForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
@@ -38207,6 +38261,7 @@
method public void invalidateOutline();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isAttachedToWindow();
method public boolean isClickable();
method public boolean isDirty();
@@ -38349,6 +38404,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAssistBlocked(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -38711,6 +38767,7 @@
method public abstract android.view.ViewAssistStructure newChild(int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
+ method public abstract void setAssistBlocked(boolean);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 326b05d..a722e17 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -35,6 +35,13 @@
package android.net {
+ public class ConnectivityManager {
+ method public deprecated boolean requestRouteToHost(int, int);
+ method public deprecated boolean requestRouteToHostAddress(int, java.net.InetAddress);
+ method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
+ }
+
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
}
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
new file mode 100644
index 0000000..4aed5c1
--- /dev/null
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -0,0 +1,104 @@
+/*
+ * 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated element requires (or may require) one or more permissions.
+ * <p/>
+ * Example of requiring a single permission:
+ * <pre>{@code
+ * @RequiresPermission(Manifest.permission.SET_WALLPAPER)
+ * public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+ *
+ * @RequiresPermission(ACCESS_COARSE_LOCATION)
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring at least one permission from a set:
+ * <pre>{@code
+ * @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring multiple permissions:
+ * <pre>{@code
+ * @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring separate read and write permissions for a content provider:
+ * <pre>{@code
+ * @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * }</pre>
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+public @interface RequiresPermission {
+ /**
+ * The name of the permission that is required, if precisely one permission
+ * is required. If more than one permission is required, specify either
+ * {@link #allOf()} or {@link #anyOf()} instead.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
+ */
+ String value() default "";
+
+ /**
+ * Specifies a list of permission names that are all required.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #value()} must both be null.
+ */
+ String[] allOf() default {};
+
+ /**
+ * Specifies a list of permission names where at least one is required
+ * <p>
+ * If specified, {@link #allOf()} and {@link #value()} must both be null.
+ */
+ String[] anyOf() default {};
+
+ /**
+ * If true, the permission may not be required in all cases (e.g. it may only be
+ * enforced on certain platforms, or for certain call parameters, etc.
+ */
+ boolean conditional() default false;
+
+ /**
+ * Specifies that the given permission is required for read operations
+ */
+ @Target(FIELD)
+ @interface Read {
+ RequiresPermission value();
+ }
+
+ /**
+ * Specifies that the given permission is required for write operations
+ */
+ @Target(FIELD)
+ @interface Write {
+ RequiresPermission value();
+ }
+}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7f062d9..b11c509 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2513,6 +2513,14 @@
return true;
}
+ case UPDATE_DEVICE_OWNER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ updateDeviceOwner(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -5801,6 +5809,18 @@
}
@Override
+ public void updateDeviceOwner(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public int getPackageProcessState(String packageName) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 1e159a3..9946d79 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -218,6 +218,7 @@
static final int FLAGS_FOCUSED = 0x00000020;
static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000;
static final int FLAGS_SELECTED = 0x00000040;
+ static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
static final int FLAGS_ACTIVATED = 0x40000000;
static final int FLAGS_CHECKABLE = 0x00000100;
static final int FLAGS_CHECKED = 0x00000200;
@@ -356,6 +357,10 @@
return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
+ public boolean isAssistBlocked() {
+ return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0;
+ }
+
public boolean isEnabled() {
return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
@@ -484,6 +489,12 @@
}
@Override
+ public void setAssistBlocked(boolean state) {
+ mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
+ | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED);
+ }
+
+ @Override
public void setEnabled(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
| (state ? 0 : ViewNode.FLAGS_DISABLED);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 7e03faa..00558fe 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -495,6 +495,7 @@
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public void updateDeviceOwner(String packageName) throws RemoteException;
public int getPackageProcessState(String packageName) throws RemoteException;
@@ -837,4 +838,5 @@
int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
+ int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e7f8f6d..2cf23af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5648,6 +5648,13 @@
/**
* Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available currently as a pre-order, and the price
+ * value provided is the purchase price for the item.
+ */
+ public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
* referred by the notification item is available as part of a subscription based service,
* and the price value provided is the subscription price for the service.
*/
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index fe284ce..aea413d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -264,6 +264,20 @@
public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE";
/**
+ * Broadcast action: notify device owner that there is a pending system update.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";
+
+ /**
+ * A long type extra for {@link #onSystemUpdatePending} recording the system time as given by
+ * {@link System#currentTimeMillis()} when the current pending system update is first available.
+ * @hide
+ */
+ public static final String EXTRA_SYSTEM_UPDATE_RECEIVED_TIME = "android.app.extra.SYSTEM_UPDATE_RECEIVED_TIME";
+
+ /**
* Name under which a DevicePolicy component publishes information
* about itself. This meta-data must reference an XML resource containing
* a device-admin tag.
@@ -486,6 +500,22 @@
}
/**
+ * Allows the receiver to be notified when information about a pending system update is
+ * available from the system update service. The same pending system update can trigger multiple
+ * calls to this method, so it is necessary to examine the incoming parameters for details about
+ * the update.
+ * <p>
+ * This callback is only applicable to device owners.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param receivedTime The time as given by {@link System#currentTimeMillis()} indicating when
+ * the current pending update was first available. -1 if no pending update is available.
+ */
+ public void onSystemUpdatePending(Context context, Intent intent, long receivedTime) {
+ }
+
+ /**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
* convenience callbacks for each action.
@@ -530,6 +560,9 @@
onLockTaskModeExiting(context, intent);
} else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) {
onReadyForUserInitialization(context, intent);
+ } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) {
+ long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1);
+ onSystemUpdatePending(context, intent, receivedTime);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9e2da61..a20aa668 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4301,4 +4301,24 @@
Log.w(TAG, "Failed talking with device policy service", re);
}
}
+
+ /**
+ * Callable by the system update service to notify device owners about pending updates.
+ * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE}
+ * permission.
+ *
+ * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()} indicating
+ * when the current pending update was first available. -1 if no update is available.
+ * @hide
+ */
+ @SystemApi
+ public void notifyPendingSystemUpdate(long updateReceivedTime) {
+ if (mService != null) {
+ try {
+ mService.notifyPendingSystemUpdate(updateReceivedTime);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not notify device owner about pending system update", re);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1f7498e..087fc88 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -225,4 +225,6 @@
boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
void setStatusBarEnabledState(in ComponentName who, boolean enabled);
boolean getDoNotAskCredentialsOnBoot();
+
+ void notifyPendingSystemUpdate(in long updateReceivedTime);
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2418e82..79e560f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -792,7 +792,6 @@
// mService is null, handle that case
}
} catch (RemoteException e) {Log.e(TAG, "", e);}
- if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF");
return STATE_OFF;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 393cf8e..fd65d56 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -26,6 +26,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
@@ -204,8 +205,13 @@
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
- CancellationSignal.fromTransport(cancellationSignal));
+ // The caller has no access to the data, so return an empty cursor with
+ // the columns in the requested order. The caller may ask for an invalid
+ // column and we would not catch that but this is not a problem in practice.
+ // We do not call ContentProvider#query with a modified where clause since
+ // the implementation is not guaranteed to be backed by a SQL database, hence
+ // it may not handle properly the tautology where clause we would have created.
+ return new MatrixCursor(projection, 0);
}
final String original = setCallingPackage(callingPkg);
try {
@@ -817,31 +823,6 @@
}
/**
- * @hide
- * Implementation when a caller has performed a query on the content
- * provider, but that call has been rejected for the operation given
- * to {@link #setAppOps(int, int)}. The default implementation
- * rewrites the <var>selection</var> argument to include a condition
- * that is never true (so will always result in an empty cursor)
- * and calls through to {@link #query(android.net.Uri, String[], String, String[],
- * String, android.os.CancellationSignal)} with that.
- */
- public Cursor rejectQuery(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- // The read is not allowed... to fake it out, we replace the given
- // selection statement with a dummy one that will always be false.
- // This way we will get a cursor back that has the correct structure
- // but contains no rows.
- if (selection == null || selection.isEmpty()) {
- selection = "'A' = 'B'";
- } else {
- selection = "'A' = 'B' AND (" + selection + ")";
- }
- return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
- }
-
- /**
* Implement this to handle query requests from clients.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a48b324..63f48cf 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -305,6 +305,9 @@
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Multimedia Messaging Service servers.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
*/
public static final int TYPE_MOBILE_MMS = 2;
/**
@@ -312,6 +315,9 @@
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Secure User Plane Location servers for help locating the device.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
*/
public static final int TYPE_MOBILE_SUPL = 3;
/**
@@ -324,9 +330,10 @@
/**
* A High Priority Mobile data connection. This network type uses the
* same network interface as {@link #TYPE_MOBILE} but the routing setup
- * is different. Only requesting processes will have access to the
- * Mobile DNS servers and only IP's explicitly requested via {@link #requestRouteToHost}
- * will route over this interface if no default route exists.
+ * is different.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
*/
public static final int TYPE_MOBILE_HIPRI = 5;
/**
@@ -386,7 +393,7 @@
*/
public static final int TYPE_MOBILE_IA = 14;
-/**
+ /**
* Emergency PDN connection for emergency calls
* {@hide}
*/
@@ -736,7 +743,7 @@
}
/**
- * Returns an array of of {@link NetworkCapabilities} objects, representing
+ * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
* the Networks that applications run by the given user will use by default.
* @hide
*/
@@ -826,11 +833,11 @@
}
/**
- * Get the {@link NetworkCapabilities} for the given {@link Network}. This
+ * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
*
* @param network The {@link Network} object identifying the network in question.
- * @return The {@link NetworkCapabilities} for the network, or {@code null}.
+ * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
*/
public NetworkCapabilities getNetworkCapabilities(Network network) {
try {
@@ -854,6 +861,7 @@
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int startUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -901,6 +909,7 @@
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1179,6 +1188,7 @@
*
* @deprecated Deprecated in favor of the {@link #requestNetwork},
* {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
+ * @removed
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1197,6 +1207,7 @@
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
* {@link #bindProcessToNetwork} api.
+ * @removed
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
try {
@@ -2057,7 +2068,7 @@
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2299,7 +2310,7 @@
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
* {@link #unregisterNetworkCallback} or the calling application exits.
@@ -2318,7 +2329,7 @@
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
* This function behaves identically to the non-timedout version, but if a suitable
@@ -2365,7 +2376,7 @@
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This function behaves identically to the version that takes a NetworkCallback, but instead
* of {@link NetworkCallback} a {@link PendingIntent} is used. This means
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b302f95..931cd3e 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,11 +16,13 @@
package android.os;
+import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.webkit.MimeTypeMap;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -34,6 +36,7 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -533,4 +536,76 @@
}
return null;
}
+
+ /**
+ * Generates a unique file name under the given parent directory. If the display name doesn't
+ * have an extension that matches the requested MIME type, the default extension for that MIME
+ * type is appended. If a file already exists, the name is appended with a numerical value to
+ * make it unique.
+ *
+ * For example, the display name 'example' with 'text/plain' MIME might produce
+ * 'example.txt' or 'example (1).txt', etc.
+ *
+ * @throws FileNotFoundException
+ */
+ public static File buildUniqueFile(File parent, String mimeType, String displayName)
+ throws FileNotFoundException {
+ String name;
+ String ext;
+
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ name = displayName;
+ ext = null;
+ } else {
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+ }
+
+ File file = buildFile(parent, name, ext);
+
+ // If conflicting file, try adding counter suffix
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ throw new FileNotFoundException("Failed to create unique file");
+ }
+ file = buildFile(parent, name + " (" + n + ")", ext);
+ }
+
+ return file;
+ }
+
+ private static File buildFile(File parent, String name, String ext) {
+ if (TextUtils.isEmpty(ext)) {
+ return new File(parent, name);
+ } else {
+ return new File(parent, name + "." + ext);
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a622a21..00c851b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -189,6 +189,36 @@
"android.settings.USAGE_ACCESS_SETTINGS";
/**
+ * Activity Category: Show application settings related to usage access.
+ * <p>
+ * An activity that provides a user interface for adjusting usage access related
+ * preferences for its containing application. Optional but recommended for apps that
+ * use {@link android.Manifest.permission#PACKAGE_USAGE_STATS}.
+ * <p>
+ * The activity may define meta-data to describe what usage access is
+ * used for within their app with {@link #METADATA_USAGE_ACCESS_REASON}, which
+ * will be displayed in Settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG =
+ "android.intent.category.USAGE_ACCESS_CONFIG";
+
+ /**
+ * Metadata key: Reason for needing usage access.
+ * <p>
+ * A key for metadata attached to an activity that receives action
+ * {@link #INTENT_CATEGORY_USAGE_ACCESS_CONFIG}, shown to the
+ * user as description of how the app uses usage access.
+ * <p>
+ */
+ public static final String METADATA_USAGE_ACCESS_REASON =
+ "android.settings.metadata.USAGE_ACCESS_REASON";
+
+ /**
* Activity Action: Show settings to allow configuration of security and
* location privacy.
* <p>
@@ -5361,6 +5391,12 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the default dialer application
+ * @hide
+ */
+ public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
+
+ /**
* Specifies the package name currently configured to be the emergency assistance application
*
* @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index d21cc3c..f0ca276 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -78,7 +78,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the PendingIntent supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -113,7 +114,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the IntentSender supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -144,6 +146,32 @@
mIntentSender = intentSender;
}
+ /**
+ * Construct a deep link target for presentation by a chooser UI.
+ *
+ * <p>A target is composed of a title and an icon for presentation to the user.
+ * The UI presenting this target may truncate the title if it is too long to be presented
+ * in the available space, as well as crop, resize or overlay the supplied icon.</p>
+ *
+ * <p>The creator of a target may supply a ranking score. This score is assumed to be relative
+ * to the other targets supplied by the same
+ * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
+ *
+ * <p>Before being sent, the Intent supplied will be
+ * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
+ * to the chooser.</p>
+ *
+ * <p>Take care not to place custom {@link android.os.Parcelable} types into
+ * the Intent as extras, as the system will not be able to unparcel it to merge
+ * additional extras.</p>
+ *
+ * @param title title of this target that will be shown to a user
+ * @param icon icon to represent this target
+ * @param score ranking score for this target between 0.0f and 1.0f, inclusive
+ * @param intent Intent to fill in and send if the user chooses this target
+ */
public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) {
mTitle = title;
mIcon = icon;
@@ -358,6 +386,10 @@
}
dest.writeFloat(mScore);
IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest);
+ dest.writeInt(mIntent != null ? 1 : 0);
+ if (mIntent != null) {
+ mIntent.writeToParcel(dest, 0);
+ }
}
public static final Creator<ChooserTarget> CREATOR
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9741239..5c6ce76 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -631,6 +631,7 @@
* </p>
*
* @attr ref android.R.styleable#View_alpha
+ * @attr ref android.R.styleable#View_assistBlocked
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
* @attr ref android.R.styleable#View_contentDescription
@@ -2324,6 +2325,10 @@
* 1 PFLAG3_IS_LAID_OUT
* 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT
* 1 PFLAG3_CALLED_SUPER
+ * 1 PFLAG3_APPLYING_INSETS
+ * 1 PFLAG3_FITTING_SYSTEM_WINDOWS
+ * 1 PFLAG3_NESTED_SCROLLING_ENABLED
+ * 1 PFLAG3_ASSIST_BLOCKED
* |-------|-------|-------|-------|
*/
@@ -2381,6 +2386,12 @@
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
/**
+ * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse
+ * into this view.<p>
+ */
+ static final int PFLAG3_ASSIST_BLOCKED = 0x100;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3869,6 +3880,11 @@
viewFlagMasks |= SAVE_DISABLED_MASK;
}
break;
+ case com.android.internal.R.styleable.View_assistBlocked:
+ if (a.getBoolean(attr, false)) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ }
+ break;
case com.android.internal.R.styleable.View_duplicateParentState:
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
@@ -5775,7 +5791,7 @@
} else {
structure.setId(id, null, null, null);
}
- structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight-mLeft, mBottom-mTop);
+ structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
@@ -5890,8 +5906,13 @@
* {@link #onProvideVirtualAssistStructure}.
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
- onProvideAssistStructure(structure);
- onProvideVirtualAssistStructure(structure);
+ if (!isAssistBlocked()) {
+ onProvideAssistStructure(structure);
+ onProvideVirtualAssistStructure(structure);
+ } else {
+ structure.setClassName(getAccessibilityClassName().toString());
+ structure.setAssistBlocked(true);
+ }
}
/**
@@ -7458,6 +7479,42 @@
}
/**
+ * Indicates whether this view will participate in data collection through
+ * {@link android.view.ViewAssistStructure}. If true, it will not provide any data
+ * for itself or its children. If false, the normal data collection will be allowed.
+ *
+ * @return Returns false if assist data collection is not blocked, else true.
+ *
+ * @see #setAssistBlocked(boolean)
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public boolean isAssistBlocked() {
+ return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
+ }
+
+ /**
+ * Controls whether assist data collection from this view and its children is enabled
+ * (that is, whether {@link #onProvideAssistStructure} and
+ * {@link #onProvideVirtualAssistStructure} will be called). The default value is false,
+ * allowing normal assist collection. Setting this to false will disable assist collection.
+ *
+ * @param enabled Set to true to <em>disable</em> assist data collection, or false
+ * (the default) to allow it.
+ *
+ * @see #isAssistBlocked()
+ * @see #onProvideAssistStructure
+ * @see #onProvideVirtualAssistStructure
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public void setAssistBlocked(boolean enabled) {
+ if (enabled) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_ASSIST_BLOCKED;
+ }
+ }
+
+ /**
* Indicates whether this view will save its state (that is,
* whether its {@link #onSaveInstanceState} method will be called).
*
diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java
index 7d263c5..346b8ec 100644
--- a/core/java/android/view/ViewAssistStructure.java
+++ b/core/java/android/view/ViewAssistStructure.java
@@ -32,6 +32,8 @@
public abstract void setVisibility(int visibility);
+ public abstract void setAssistBlocked(boolean state);
+
public abstract void setEnabled(boolean state);
public abstract void setClickable(boolean state);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8f2be99..4324e75 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2881,21 +2881,23 @@
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
super.dispatchProvideAssistStructure(structure);
- if (structure.getChildCount() == 0) {
- final int childrenCount = getChildCount();
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- final ArrayList<View> preorderedList = buildOrderedChildList();
- final boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- final View[] children = mChildren;
- for (int i=0; i<childrenCount; i++) {
- final int childIndex = customOrder
- ? getChildDrawingOrder(childrenCount, i) : i;
- final View child = (preorderedList == null)
- ? children[childIndex] : preorderedList.get(childIndex);
- ViewAssistStructure cstructure = structure.newChild(i);
- child.dispatchProvideAssistStructure(cstructure);
+ if (!isAssistBlocked()) {
+ if (structure.getChildCount() == 0) {
+ final int childrenCount = getChildCount();
+ if (childrenCount > 0) {
+ structure.setChildCount(childrenCount);
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i=0; i<childrenCount; i++) {
+ final int childIndex = customOrder
+ ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = (preorderedList == null)
+ ? children[childIndex] : preorderedList.get(childIndex);
+ ViewAssistStructure cstructure = structure.newChild(i);
+ child.dispatchProvideAssistStructure(cstructure);
+ }
}
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 943beb0..453e4f5 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -900,7 +900,9 @@
* and therefore secure policy, this setting should be disabled.
* Note that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -920,7 +922,9 @@
* the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
* Note too, that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7d3a41e..b049e49 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3079,6 +3079,12 @@
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
+ if (mTextView.canShare()) {
+ menu.add(0, TextView.ID_SHARE, 0, com.android.internal.R.string.share).
+ setShowAsAction(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
setAlphabeticShortcut('a').
setShowAsAction(
@@ -4045,9 +4051,9 @@
positionCursor = true;
} else if (offset + mTouchWordOffset < mPreviousOffset) {
// User is shrinking the selection.
- if (currLine > mPrevLine) {
+ if (currLine < mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
- offset = getWordStart(offset);
+ offset = start;
}
offset += mTouchWordOffset;
positionCursor = true;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 726b89a..3e8df08 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8955,13 +8955,14 @@
static final int ID_CUT = android.R.id.cut;
static final int ID_COPY = android.R.id.copy;
static final int ID_PASTE = android.R.id.paste;
+ static final int ID_SHARE = android.R.id.shareText;
static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText;
static final int ID_REPLACE = android.R.id.replaceText;
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
- * {@link android.R.id#copy} or {@link android.R.id#paste}.
+ * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
*
* @return true if the context menu item action was performed.
*/
@@ -9014,6 +9015,10 @@
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
stopSelectionActionMode();
return true;
+
+ case ID_SHARE:
+ shareSelectedText();
+ return true;
}
return false;
}
@@ -9091,15 +9096,15 @@
* If provided, this ActionMode.Callback will be used to create the ActionMode when text
* selection is initiated in this View.
*
- * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
- * Paste actions, depending on what this View supports.
+ * The standard implementation populates the menu with a subset of Select All, Cut, Copy,
+ * Paste and Share actions, depending on what this View supports.
*
* A custom implementation can add new entries in the default menu in its
* {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
* default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
- * {@link android.R.id#cut}, {@link android.R.id#copy} or {@link android.R.id#paste} ids as
- * parameters.
+ * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste} or
+ * {@link android.R.id#shareText} ids as parameters.
*
* Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
@@ -9168,6 +9173,10 @@
return false;
}
+ boolean canShare() {
+ return canCopy();
+ }
+
boolean canPaste() {
return (mText instanceof Editable &&
mEditor != null && mEditor.mKeyListener != null &&
@@ -9241,6 +9250,18 @@
}
}
+ private void shareSelectedText() {
+ String selectedText = getSelectedText();
+ if (selectedText != null && !selectedText.isEmpty()) {
+ Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
+ sharingIntent.setType("text/plain");
+ sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
+ sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
+ getContext().startActivity(Intent.createChooser(sharingIntent, null));
+ stopSelectionActionMode();
+ }
+ }
+
private void setPrimaryClip(ClipData clip) {
ClipboardManager clipboard = (ClipboardManager) getContext().
getSystemService(Context.CLIPBOARD_SERVICE);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8403e77..a6c39e6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -401,6 +401,11 @@
}
@Override
+ public boolean shouldGetResolvedFilter() {
+ return true;
+ }
+
+ @Override
public int getCount() {
int count = super.getCount();
if (mServiceTargets != null) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3cd69a1..7f51d92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1062,7 +1062,7 @@
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
PackageManager.MATCH_DEFAULT_ONLY
- | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
);
// Filter out any activities that the launched uid does not
@@ -1188,6 +1188,10 @@
// This space for rent
}
+ public boolean shouldGetResolvedFilter() {
+ return mFilterLastUsed;
+ }
+
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
CharSequence roLabel) {
// Process labels from start to i
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index a14e98d..3a1e0ca 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -346,6 +346,17 @@
};
private final Region mTouchableRegion = new Region();
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+ new ViewTreeObserver.OnComputeInternalInsetsListener() {
+ public void onComputeInternalInsets(
+ ViewTreeObserver.InternalInsetsInfo info) {
+ info.contentInsets.setEmpty();
+ info.visibleInsets.setEmpty();
+ info.touchableRegion.set(mTouchableRegion);
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo
+ .TOUCHABLE_INSETS_REGION);
+ }
+ };
private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
private boolean mHidden; // tracks whether this popup is hidden or hiding.
@@ -382,21 +393,6 @@
mPopupWindow.dismiss();
}
});
- // Make the touchable area of this popup be the area specified by mTouchableRegion.
- mPopupWindow.getContentView()
- .getRootView()
- .getViewTreeObserver()
- .addOnComputeInternalInsetsListener(
- new ViewTreeObserver.OnComputeInternalInsetsListener() {
- public void onComputeInternalInsets(
- ViewTreeObserver.InternalInsetsInfo info) {
- info.contentInsets.setEmpty();
- info.visibleInsets.setEmpty();
- info.touchableRegion.set(mTouchableRegion);
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo
- .TOUCHABLE_INSETS_REGION);
- }
- });
mMarginHorizontal = parent.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
mMarginVertical = parent.getResources()
@@ -437,16 +433,15 @@
mHidden = false;
mDismissed = false;
- cancelAllAnimations();
+ cancelDismissAndHideAnimations();
+ cancelOverflowAnimations();
// Make sure a panel is set as the content.
if (mContentContainer.getChildCount() == 0) {
setMainPanelAsContent();
}
preparePopupContent();
- // If we're yet to show the popup, set the container visibility to zero.
- // The "show" animation will make this visible.
- mContentContainer.setAlpha(0);
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
+ setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -454,12 +449,13 @@
* Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
*/
public void dismiss() {
- if (!isShowing()) {
+ if (mDismissed) {
return;
}
mHidden = false;
mDismissed = true;
+ mHideAnimation.cancel();
runDismissAnimation();
setZeroTouchableSurface();
}
@@ -502,7 +498,7 @@
return;
}
- cancelAllAnimations();
+ cancelOverflowAnimations();
preparePopupContent();
mPopupWindow.update(x, y, getWidth(), getHeight());
}
@@ -566,10 +562,12 @@
mHideAnimation.start();
}
- private void cancelAllAnimations() {
- mShowAnimation.cancel();
+ private void cancelDismissAndHideAnimations() {
mDismissAnimation.cancel();
mHideAnimation.cancel();
+ }
+
+ private void cancelOverflowAnimations() {
mOpenOverflowAnimation.cancel();
mCloseOverflowAnimation.cancel();
}
@@ -804,6 +802,19 @@
(int) mContentContainer.getX() + width,
(int) mContentContainer.getY() + height);
}
+
+ /**
+ * Make the touchable area of this popup be the area specified by mTouchableRegion.
+ * This should be called after the popup window has been dismissed (dismiss/hide)
+ * and is probably being re-shown with a new content root view.
+ */
+ private void setTouchableSurfaceInsetsComputer() {
+ ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
+ .getRootView()
+ .getViewTreeObserver();
+ viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
+ viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
+ }
}
/**
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 23b0d50..d86f71a 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -584,6 +584,13 @@
return frameCount * channelCount * audio_bytes_per_sample(format);
}
+static jboolean android_media_AudioRecord_setInputDevice(
+ JNIEnv *env, jobject thiz, jint device_id) {
+
+// sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+// return lpRecorder->setInputDevice(device_id) == NO_ERROR;
+ return false;
+}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -616,6 +623,7 @@
"()I", (void *)android_media_AudioRecord_get_pos_update_period},
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
+ {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fa5e4ad..62685a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1116,6 +1116,11 @@
<permission android:name="android.permission.ACCESS_PDB_STATE"
android:protectionLevel="signature" />
+ <!-- @hide Allows system update service to notify device owner about pending updates.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- =========================================== -->
<!-- Permissions associated with camera and image capture -->
<!-- =========================================== -->
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 8321ea4..e511cc9 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index a189813..7256a3c 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a5c6f84..00c771d8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2329,7 +2329,7 @@
<!-- Defines whether this view reacts to long click events. -->
<attr name="longClickable" format="boolean" />
- <!-- If unset, no state will be saved for this view when it is being
+ <!-- If false, no state will be saved for this view when it is being
frozen. The default is true, allowing the view to be saved
(however it also must have an ID assigned to it for its
state to be saved). Setting this to false only disables the
@@ -2337,6 +2337,11 @@
be saved. -->
<attr name="saveEnabled" format="boolean" />
+ <!-- If true, no {@link android.view.ViewAssistStructure} data will be collected from
+ this view or any of its children. The default is false, allowing assist structure
+ to be reported by it. -->
+ <attr name="assistBlocked" format="boolean" />
+
<!-- Specifies whether to filter touches when the view's window is obscured by
another visible window. When set to true, the view will not receive touches
whenever a toast, dialog or other window appears above the view's window.
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index aaf252a..bdc8d9f 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -93,6 +93,7 @@
<item type="id" name="undo" />
<item type="id" name="redo" />
<item type="id" name="replaceText" />
+ <item type="id" name="shareText" />
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
<item type="id" name="accessibilityActionShowOnScreen" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7252584..baccafd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2650,6 +2650,7 @@
<public type="id" name="replaceText" />
<public type="id" name="accessibilityActionShowOnScreen" />
<public type="id" name="accessibilityActionScrollToPosition" />
+ <public type="id" name="shareText" />
<public type="attr" name="allowUndo" />
<public type="attr" name="colorBackgroundFloating" />
@@ -2674,4 +2675,6 @@
<public type="attr" name="showForAllUsers" />
<!-- NFC CardEmulation: dynamically load service resources -->
<public type="attr" name="dynamicResources" />
+
+ <public type="attr" name="assistBlocked" />
</resources>
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 5c9e813..ee9e2e4 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,6 +21,7 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import android.content.Context;
+import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
@@ -43,6 +44,8 @@
private File mDir;
private File mTestFile;
private File mCopyFile;
+ private File mTarget;
+
@Override
protected void setUp() throws Exception {
@@ -50,11 +53,15 @@
mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
mTestFile = new File(mDir, "test.file");
mCopyFile = new File(mDir, "copy.file");
+
+ mTarget = getContext().getFilesDir();
+ FileUtils.deleteContents(mTarget);
}
@Override
protected void tearDown() throws Exception {
IoUtils.deleteContents(mDir);
+ FileUtils.deleteContents(mTarget);
}
// TODO: test setPermissions(), getPermissions()
@@ -225,6 +232,63 @@
assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
}
+ public void testBuildUniqueFile_normal() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
+ assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png"));
+
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test"));
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
+ }
+
+ public void testBuildUniqueFile_unknown() throws Exception {
+ assertNameEquals("test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
+ assertNameEquals(".test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test"));
+
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test"));
+ assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
+ }
+
+ public void testBuildUniqueFile_dir() throws Exception {
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+ new File(mTarget, "test").mkdir();
+ assertNameEquals("test (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ new File(mTarget, "test.jpg").mkdir();
+ assertNameEquals("test.jpg (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ }
+
+ public void testBuildUniqueFile_increment() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test.jpg").createNewFile();
+ assertNameEquals("test (1).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test (1).jpg").createNewFile();
+ assertNameEquals("test (2).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ }
+
+ private static void assertNameEquals(String expected, File actual) {
+ assertEquals(expected, actual.getName());
+ }
+
private void touch(String name, long age) throws Exception {
final File file = new File(mDir, name);
file.createNewFile();
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index e31290a..d674f7f 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -88,7 +88,7 @@
<li><a href="https://developers.google.com/maps/documentation/android/wear"
class="external-link">Google Maps on Android Wear developer guide</a>
</li>
- <li><a href="https://github.com/googlemaps/android-samples"
+ <li><a href="https://github.com/googlemaps/android-samples/tree/master/BasicWearMap"
class="external-link">Google Maps on Android Wear sample</a>
</li>
<li><a href="https://developers.google.com/maps/documentation/android/releases"
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index cbfa82e..743e692 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -401,19 +401,35 @@
format and style your string resources.</p>
-<h3>Escaping apostrophes and quotes</h3>
+<h3 id="escaping_quotes">Escaping apostrophes and quotes</h3>
-<p>If you have an apostrophe or a quote in your string, you must either escape it or enclose the
-whole string in the other type of enclosing quotes. For example, here are some stings that
-do and don't work:</p>
+<p>
+ If you have an apostrophe (<code>'</code>) in your string, you must either
+ escape it with a backslash (<code>\'</code>) or enclose the string in
+ double-quotes (<code>""</code>). For example, here are some strings that do
+ and don't work:
+</p>
<pre>
-<string name="good_example">"This'll work"</string>
-<string name="good_example_2">This\'ll also work</string>
+<string name="good_example">This\'ll work</string>
+<string name="good_example_2">"This'll also work"</string>
<string name="bad_example">This doesn't work</string>
-<string name="bad_example_2">XML encodings don&apos;t work</string>
+ <!-- Causes a compile error -->
</pre>
+<p>
+ If you have a double-quote in your string, you must escape it
+ (<code>\"</code>). Surrounding the string with single-quotes does
+ <em>not</em> work.
+</p>
+
+<pre>
+<string name="good_example">This is a \"good string\".</string>
+<string name="bad_example">This is a "bad string".</string>
+ <!-- Quotes are stripped; displays as: This is a bad string. -->
+<string name="bad_example_2">'This is another "bad string".'</string>
+ <!-- Causes a compile error -->
+</pre>
<h3>Formatting strings</h3>
diff --git a/docs/html/reference/com/google/android/gms/fitness/data/Field.html b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
index 91bf861..0f97e7e 100644
--- a/docs/html/reference/com/google/android/gms/fitness/data/Field.html
+++ b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
@@ -151,7 +151,7 @@
<a name="top"></a>
-<!-- dialog to prompt lang pref change when loaded from hardcoded URL
+<!-- dialog to prompt lang pref change when loaded from hardcoded URL
<div id="langMessage" style="display:none">
<div>
<div class="lang en">
@@ -201,7 +201,7 @@
<div id="header-wrapper">
<div id="header">
-
+
<div class="wrap" id="header-wrap">
@@ -245,8 +245,8 @@
</ul>
-
-
+
+
<div class="menu-container">
<div class="moremenu">
<div id="more-btn"></div>
@@ -267,8 +267,8 @@
<li><a href="http://source.android.com">Android Open Source Project</a></li>
</ul>
-
-
+
+
<div class="header">Language</div>
<div id="language" class="locales">
<select name="language" onChange="changeLangPref(this.value, true)">
@@ -286,8 +286,8 @@
loadLangPref();
//-->
</script>
-
-
+
+
<br class="clearfix" />
</div><!-- end 'mid' -->
<div class="bottom"></div>
@@ -401,10 +401,10 @@
</li>
<li><a href="/google/index.html">Google Services</a>
</li>
-
+
<li><a href="/samples/index.html">Samples</a>
</li>
-
+
</ul>
</li>
<li class="distribute last">
@@ -424,14 +424,14 @@
</div><!-- end header-wrap.wrap -->
</div><!-- end header -->
-
+
<!-- Secondary x-nav -->
<div id="nav-x">
<div class="wrap" style="position:relative;z-index:1">
-
-
-
+
+
+
<ul class="nav-x col-9 develop" style="width:100%">
<li class="training"><a href="/training/index.html"
@@ -469,17 +469,17 @@
<li class="google"><a href="/google/index.html"
>Google Services</a>
</li>
-
+
<li class="samples"><a href="/samples/index.html"
>Samples</a>
</li>
-
+
</ul>
</div>
</div>
<!-- /Sendondary x-nav DEVELOP -->
-
+
<div id="searchResults" class="wrap" style="display:none;">
<h2 id="searchTitle">Results</h2>
@@ -492,7 +492,7 @@
<a class="logo" href="#top"></a>
<a class="top" href="#top"></a>
<ul class="breadcrumb">
-
+
<li class="current">Field</li>
</ul>
</div>
@@ -502,7 +502,7 @@
-
+
<div class="wrap clearfix" id="body-content">
<div class="col-4" id="side-nav" itemscope itemtype="http://schema.org/SiteNavigationElement">
<div id="devdoc-nav" class="scroll-pane">
@@ -759,12 +759,12 @@
</script>
-
+
</div>
<script type="text/javascript">
showGoogleRefTree();
-
+
</script>
</div> <!-- end side-nav -->
<script>
@@ -774,7 +774,7 @@
</script>
-
+
@@ -784,21 +784,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<div class="sum-details-links">
@@ -810,22 +810,22 @@
<a href="#constants">Constants</a>
-
+
| <a href="#inhconstants">Inherited Constants</a>
-
+
| <a href="#lfields">Fields</a>
-
+
| <a href="#pubmethods">Methods</a>
-
+
@@ -835,9 +835,9 @@
</div><!-- end sum-details-links -->
<div class="api-level">
-
-
-
+
+
+
</div>
</div><!-- end api-info-block -->
@@ -847,31 +847,31 @@
<div id="jd-header">
public
-
- final
-
+
+ final
+
class
<h1 itemprop="name">Field</h1>
-
+
extends Object<br/>
-
-
-
-
-
-
- implements
-
- Parcelable
-
-
-
-
+
+
+
+
+ implements
+
+ Parcelable
+
+
+
+
+
+
</div><!-- end header -->
@@ -883,18 +883,18 @@
<tr>
-
+
<td colspan="2" class="jd-inheritance-class-cell">java.lang.Object</td>
</tr>
-
+
<tr>
-
+
<td class="jd-inheritance-space"> ↳</td>
-
+
<td colspan="1" class="jd-inheritance-class-cell">com.google.android.gms.fitness.data.Field</td>
</tr>
-
+
</table>
@@ -962,31 +962,31 @@
<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_FLOAT">FORMAT_FLOAT</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds float values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_INT32">FORMAT_INT32</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds integer values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_MAP">FORMAT_MAP</a></td>
@@ -1305,33 +1305,33 @@
</div>
<div id="inherited-constants-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">CONTENTS_FILE_DESCRIPTOR</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">PARCELABLE_WRITE_RETURN_VALUE</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
</table>
</div>
</div>
@@ -1347,7 +1347,7 @@
<table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1357,13 +1357,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACCURACY">FIELD_ACCURACY</a></td>
<td class="jd-descrcol" width="100%">
The accuracy of an accompanied value (such as location).
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1373,13 +1373,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACTIVITY">FIELD_ACTIVITY</a></td>
<td class="jd-descrcol" width="100%">
An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1389,13 +1389,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ALTITUDE">FIELD_ALTITUDE</a></td>
<td class="jd-descrcol" width="100%">
An altitude of a location represented as a float, in meters above sea level.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1405,13 +1405,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_AVERAGE">FIELD_AVERAGE</a></td>
<td class="jd-descrcol" width="100%">
An average value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1421,13 +1421,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_BPM">FIELD_BPM</a></td>
<td class="jd-descrcol" width="100%">
A heart rate in beats per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1437,13 +1437,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CALORIES">FIELD_CALORIES</a></td>
<td class="jd-descrcol" width="100%">
Calories in kcal.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1469,13 +1469,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CONFIDENCE">FIELD_CONFIDENCE</a></td>
<td class="jd-descrcol" width="100%">
The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1485,13 +1485,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DISTANCE">FIELD_DISTANCE</a></td>
<td class="jd-descrcol" width="100%">
A distance in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1501,13 +1501,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DURATION">FIELD_DURATION</a></td>
<td class="jd-descrcol" width="100%">
A duration in milliseconds.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1533,13 +1533,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HEIGHT">FIELD_HEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A height in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1549,13 +1549,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LATITUDE">FIELD_HIGH_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1565,13 +1565,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LONGITUDE">FIELD_HIGH_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1581,13 +1581,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LATITUDE">FIELD_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A latitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1597,13 +1597,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LONGITUDE">FIELD_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A longitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1613,13 +1613,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LATITUDE">FIELD_LOW_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1629,13 +1629,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LONGITUDE">FIELD_LOW_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1645,13 +1645,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MAX">FIELD_MAX</a></td>
<td class="jd-descrcol" width="100%">
A maximum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1677,13 +1677,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MIN">FIELD_MIN</a></td>
<td class="jd-descrcol" width="100%">
A minimum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1725,13 +1725,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_PERCENTAGE">FIELD_PERCENTAGE</a></td>
<td class="jd-descrcol" width="100%">
A percentage value, between 0 and 100.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1741,13 +1741,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_REVOLUTIONS">FIELD_REVOLUTIONS</a></td>
<td class="jd-descrcol" width="100%">
A count of revolutions.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1757,13 +1757,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_RPM">FIELD_RPM</a></td>
<td class="jd-descrcol" width="100%">
Revolutions per minute or rate per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1773,13 +1773,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_SPEED">FIELD_SPEED</a></td>
<td class="jd-descrcol" width="100%">
A speed in meter/sec.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1789,13 +1789,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_STEPS">FIELD_STEPS</a></td>
<td class="jd-descrcol" width="100%">
A count of steps.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1805,13 +1805,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WATTS">FIELD_WATTS</a></td>
<td class="jd-descrcol" width="100%">
Power in watts.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1821,13 +1821,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WEIGHT">FIELD_WEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A weight in kilograms.
-
-
+
+
</td>
</tr>
-
-
+
+
</table>
@@ -1846,129 +1846,129 @@
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#describeContents()">describeContents</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#equals(java.lang.Object)">equals</a></span>(Object that)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getFormat()">getFormat</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the format of the field, as one of the format constant values.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getName()">getName</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the name of the field.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#hashCode()">hashCode</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#toString()">toString</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#writeToParcel(android.os.Parcel, int)">writeToParcel</a></span>(Parcel dest, int flags)</nobr>
-
+
</td></tr>
@@ -2003,182 +2003,182 @@
</div>
<div id="inherited-methods-java.lang.Object-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
Object</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">clone</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">equals</span>(Object arg0)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">finalize</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
Class<?></nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">getClass</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">hashCode</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notify</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notifyAll</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">toString</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0, int arg1)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0)</nobr>
-
+
</td></tr>
@@ -2205,7 +2205,7 @@
</div>
<div id="inherited-methods-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
+
@@ -2232,6 +2232,7 @@
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
@@ -3196,40 +3197,40 @@
<A NAME="NUTRIENT_TOTAL_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TOTAL_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Total fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.total"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3237,40 +3238,40 @@
<A NAME="NUTRIENT_TRANS_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TRANS_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Trans fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.trans"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3321,31 +3322,31 @@
<A NAME="NUTRIENT_VITAMIN_C"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_VITAMIN_C
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Vitamin C amount in milligrams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
@@ -3372,31 +3373,31 @@
<A NAME="FIELD_ACCURACY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACCURACY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The accuracy of an accompanied value (such as location).
</p></div>
-
+
</div>
</div>
@@ -3404,33 +3405,33 @@
<A NAME="FIELD_ACTIVITY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACTIVITY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency. The
activity value should be stored using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#setActivity(java.lang.String)">setActivity(String)</a></code>,
and read using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#asActivity()">asActivity()</a></code>
</p></div>
-
+
</div>
</div>
@@ -3438,32 +3439,32 @@
<A NAME="FIELD_ALTITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ALTITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An altitude of a location represented as a float, in meters above sea level.
Some location samples don't have an altitude value so this field might not be set.
</p></div>
-
+
</div>
</div>
@@ -3471,31 +3472,31 @@
<A NAME="FIELD_AVERAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_AVERAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An average value.
</p></div>
-
+
</div>
</div>
@@ -3503,31 +3504,31 @@
<A NAME="FIELD_BPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_BPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A heart rate in beats per minute.
</p></div>
-
+
</div>
</div>
@@ -3535,31 +3536,31 @@
<A NAME="FIELD_CALORIES"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CALORIES
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Calories in kcal.
</p></div>
-
+
</div>
</div>
@@ -3599,31 +3600,31 @@
<A NAME="FIELD_CONFIDENCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CONFIDENCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
</p></div>
-
+
</div>
</div>
@@ -3631,31 +3632,31 @@
<A NAME="FIELD_DISTANCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DISTANCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A distance in meters.
</p></div>
-
+
</div>
</div>
@@ -3663,31 +3664,31 @@
<A NAME="FIELD_DURATION"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DURATION
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A duration in milliseconds.
</p></div>
-
+
</div>
</div>
@@ -3727,31 +3728,31 @@
<A NAME="FIELD_HEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A height in meters.
</p></div>
-
+
</div>
</div>
@@ -3759,31 +3760,31 @@
<A NAME="FIELD_HIGH_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3791,31 +3792,31 @@
<A NAME="FIELD_HIGH_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3823,31 +3824,31 @@
<A NAME="FIELD_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A latitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3855,31 +3856,31 @@
<A NAME="FIELD_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A longitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3887,31 +3888,31 @@
<A NAME="FIELD_LOW_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3919,31 +3920,31 @@
<A NAME="FIELD_LOW_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3951,31 +3952,31 @@
<A NAME="FIELD_MAX"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MAX
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A maximum value.
</p></div>
-
+
</div>
</div>
@@ -4015,31 +4016,31 @@
<A NAME="FIELD_MIN"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MIN
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A minimum value.
</p></div>
-
+
</div>
</div>
@@ -4113,31 +4114,31 @@
<A NAME="FIELD_PERCENTAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_PERCENTAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A percentage value, between 0 and 100.
</p></div>
-
+
</div>
</div>
@@ -4145,31 +4146,31 @@
<A NAME="FIELD_REVOLUTIONS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_REVOLUTIONS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of revolutions.
</p></div>
-
+
</div>
</div>
@@ -4177,31 +4178,31 @@
<A NAME="FIELD_RPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_RPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Revolutions per minute or rate per minute.
</p></div>
-
+
</div>
</div>
@@ -4209,31 +4210,31 @@
<A NAME="FIELD_SPEED"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_SPEED
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A speed in meter/sec.
</p></div>
-
+
</div>
</div>
@@ -4241,31 +4242,31 @@
<A NAME="FIELD_STEPS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_STEPS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of steps.
</p></div>
-
+
</div>
</div>
@@ -4273,31 +4274,31 @@
<A NAME="FIELD_WATTS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WATTS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Power in watts.
</p></div>
-
+
</div>
</div>
@@ -4305,31 +4306,31 @@
<A NAME="FIELD_WEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A weight in kilograms.
</p></div>
-
+
</div>
</div>
@@ -4354,14 +4355,14 @@
<A NAME="describeContents()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">describeContents</span>
@@ -4369,15 +4370,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4386,14 +4387,14 @@
<A NAME="equals(java.lang.Object)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
boolean
</span>
<span class="sympad">equals</span>
@@ -4401,15 +4402,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4418,14 +4419,14 @@
<A NAME="getFormat()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">getFormat</span>
@@ -4433,15 +4434,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the format of the field, as one of the format constant values.
</p></div>
@@ -4451,14 +4452,14 @@
<A NAME="getName()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">getName</span>
@@ -4466,15 +4467,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the name of the field.
</p></div>
@@ -4484,14 +4485,14 @@
<A NAME="hashCode()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">hashCode</span>
@@ -4499,15 +4500,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4516,14 +4517,14 @@
<A NAME="toString()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">toString</span>
@@ -4531,15 +4532,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4548,14 +4549,14 @@
<A NAME="writeToParcel(android.os.Parcel, int)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
void
</span>
<span class="sympad">writeToParcel</span>
@@ -4563,15 +4564,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4589,17 +4590,17 @@
<A NAME="navbar_top"></A>
<div id="footer" class="wrap" >
-
+
<div id="copyright">
-
+
Except as noted, this content is licensed under <a
- href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
+ href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
For details and restrictions, see the <a href="/license.html">
Content License</a>.
</div>
<div id="build_info">
-
+
<script src="/timestamp.js" type="text/javascript"></script>
<script>document.write(BUILD_TIMESTAMP)</script>
@@ -4607,7 +4608,7 @@
<div id="footerlinks">
-
+
<p>
<a href="/about/index.html">About Android</a> |
<a href="/legal.html">Legal</a> |
@@ -4620,7 +4621,7 @@
</div><!-- end doc-content -->
-</div> <!-- end body-content -->
+</div> <!-- end body-content -->
diff --git a/docs/html/training/enterprise/app-restrictions.jd b/docs/html/training/enterprise/app-restrictions.jd
index fc5dfcc..dd2c2c0 100644
--- a/docs/html/training/enterprise/app-restrictions.jd
+++ b/docs/html/training/enterprise/app-restrictions.jd
@@ -316,8 +316,18 @@
{@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for
this intent so you can change the app's behavior when the restriction settings
- change. The following code shows how to dynamically register a broadcast
- receiver for this intent:
+ change.</p>
+
+<p class="note">
+ <strong>Note:</strong> The {@link
+ android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
+ ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners
+ that are dynamically registered, <em>not</em> to listeners that are declared
+ in the app manifest.
+</p>
+<p>
+ The following code shows how to dynamically register a broadcast receiver for
+ this intent:
</p>
<pre>IntentFilter restrictionsFilter =
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 649d996..6385706 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -185,7 +185,9 @@
public static final int VERTICAL_TEXT_FLAG = 0x1000;
// we use this when we first create a paint
- static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
+ private static final int HIDDEN_DEFAULT_PAINT_FLAGS =
+ DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
+ private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG;
/**
* Font hinter option that disables font hinting.
@@ -415,9 +417,11 @@
/**
* Create a new paint with default settings.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, sets {@link #ANTI_ALIAS_FLAG}.
*/
public Paint() {
- this(0);
+ this(DEFAULT_PAINT_FLAGS);
}
/**
@@ -428,7 +432,7 @@
*/
public Paint(int flags) {
mNativePaint = native_init();
- setFlags(flags | DEFAULT_PAINT_FLAGS);
+ setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
@@ -452,7 +456,7 @@
/** Restores the paint to its default settings. */
public void reset() {
native_reset(mNativePaint);
- setFlags(DEFAULT_PAINT_FLAGS);
+ setFlags(DEFAULT_PAINT_FLAGS | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
@@ -1870,7 +1874,7 @@
* Convenience overload that takes a char array instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
* @hide
*/
public float getTextRunAdvances(char[] chars, int index, int count,
@@ -1915,7 +1919,7 @@
* Convenience overload that takes a CharSequence instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
* @hide
*/
public float getTextRunAdvances(CharSequence text, int start, int end,
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index a9a8f37..f036b19 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.CheckResult;
import android.os.Parcel;
import android.os.Parcelable;
@@ -389,6 +390,7 @@
* (and this rectangle is then set to that intersection) else
* return false and do not change this rectangle.
*/
+ @CheckResult
public boolean intersect(int left, int top, int right, int bottom) {
if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
if (this.left < left) this.left = left;
@@ -411,6 +413,7 @@
* (and this rectangle is then set to that intersection) else
* return false and do not change this rectangle.
*/
+ @CheckResult
public boolean intersect(Rect r) {
return intersect(r.left, r.top, r.right, r.bottom);
}
@@ -427,6 +430,7 @@
* this rectangle to that intersection. If they do not, return
* false and do not change this rectangle.
*/
+ @CheckResult
public boolean setIntersect(Rect a, Rect b) {
if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
left = Math.max(a.left, b.left);
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e52cb03..2346abe 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,6 +20,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
+import java.util.Collection;
import java.util.Iterator;
import android.annotation.IntDef;
@@ -32,6 +33,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
/**
@@ -113,6 +115,11 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 3;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
private final static String TAG = "android.media.AudioRecord";
/** @hide */
@@ -1149,7 +1156,7 @@
* Sets the listener the AudioRecord notifies when a previously set marker is reached or
* for each periodic record head position update.
* Use this method to receive AudioRecord events in the Handler associated with another
- * thread than the one in which you created the AudioTrack instance.
+ * thread than the one in which you created the AudioRecord instance.
* @param listener
* @param handler the Handler that will receive the event notification messages.
*/
@@ -1190,6 +1197,115 @@
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioRecord changes if they provide
+ * a {#link Handler} object to addOnAudioRecordRoutingListener().
+ */
+ private ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioRecordRoutingListener} to receive notifications of routing changes
+ * on this AudioRecord.
+ */
+ public void addOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioRecordRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioRecord record,
+ final OnAudioRecordRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioRecord was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (record == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioRecordRouting(record);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
/**
* Sets the period at which the listener is called, if set with
* {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
@@ -1206,6 +1322,39 @@
}
+ //--------------------------------------------------------------------------
+ // Explicit Routing
+ //--------------------
+ private AudioDeviceInfo mPreferredDevice = null;
+
+ /**
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
+ * the input to this AudioRecord.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source.
+ * If deviceInfo is null, default routing is restored.
+ * @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
+ * does not correspond to a valid audio input device.
+ */
+ public boolean setPreferredInputDevice(AudioDeviceInfo deviceInfo) {
+ // Do some validation....
+ if (deviceInfo != null && !deviceInfo.isSource()) {
+ return false;
+ }
+
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
+
+ return native_setInputDevice(preferredDeviceId);
+ }
+
+ /**
+ * Returns the selected input specified by {@link #setPreferredInputDevice}. Note that this
+ * is not guarenteed to correspond to the actual device being used for recording.
+ */
+ public AudioDeviceInfo getPreferredInputDevice() {
+ return mPreferredDevice;
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -1336,6 +1485,8 @@
static private native final int native_get_min_buff_size(
int sampleRateInHz, int channelCount, int audioFormat);
+ private native final boolean native_setInputDevice(int deviceId);
+
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e2dc6df..847bdc2 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,6 +23,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
+import java.util.Collection;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -37,6 +38,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.IAppOpsService;
@@ -176,6 +178,12 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
+
private final static String TAG = "android.media.AudioTrack";
@@ -224,7 +232,7 @@
/**
* Handler for events coming from the native code.
*/
- private NativeEventHandlerDelegate mEventHandlerDelegate;
+ private NativePositionEventHandlerDelegate mEventHandlerDelegate;
/**
* Looper associated with the thread that creates the AudioTrack instance.
*/
@@ -1265,7 +1273,7 @@
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
if (listener != null) {
- mEventHandlerDelegate = new NativeEventHandlerDelegate(this, listener, handler);
+ mEventHandlerDelegate = new NativePositionEventHandlerDelegate(this, listener, handler);
} else {
mEventHandlerDelegate = null;
}
@@ -2104,33 +2112,93 @@
private AudioDeviceInfo mPreferredDevice = null;
/**
- * Specifies an audio device (via and {@link AudioDeviceInfo} object) to route
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
* the output from this AudioTrack.
- * @param deviceSpec The {@link AudioDeviceInfo} specifying the physical audio device.
- * If deviceSpec is null, default routing is restored.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink.
+ * If deviceInfo is null, default routing is restored.
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio output device.
*/
- public boolean setPreferredOutputDevice(AudioDeviceInfo deviceSpec) {
+ public boolean setPreferredOutputDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
- if (deviceSpec != null && !deviceSpec.isSink()) {
+ if (deviceInfo != null && !deviceInfo.isSink()) {
return false;
}
- mPreferredDevice = deviceSpec;
- int routingDeviceId = mPreferredDevice != null ? deviceSpec.getId() : 0;
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
- return native_setOutputDevice(routingDeviceId);
+ return native_setOutputDevice(preferredDeviceId);
}
/**
* Returns the selected output specified by {@link #setPreferredOutputDevice}. Note that this
- * is not guarenteed to correspond to the actual device being used for playback.
+ * is not guaranteed to correspond to the actual device being used for playback.
*/
public AudioDeviceInfo getPreferredOutputDevice() {
return mPreferredDevice;
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioTrack changes if they provide
+ * a {#link Handler} object to addOnAudioTrackRoutingListener().
+ */
+ private ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioTrackRoutingListener} to receive notifications of routing changes
+ * on this AudioTrack.
+ */
+ public void addOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioTrackRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -2159,10 +2227,10 @@
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
- private class NativeEventHandlerDelegate {
+ private class NativePositionEventHandlerDelegate {
private final Handler mHandler;
- NativeEventHandlerDelegate(final AudioTrack track,
+ NativePositionEventHandlerDelegate(final AudioTrack track,
final OnPlaybackPositionUpdateListener listener,
Handler handler) {
// find the looper for our new event handler
@@ -2210,6 +2278,55 @@
}
}
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioTrack track,
+ final OnAudioTrackRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioTrack was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (track == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioTrackRouting(track);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
//---------------------------------------------------------
// Java methods called from the native side
@@ -2223,7 +2340,7 @@
return;
}
- NativeEventHandlerDelegate delegate = track.mEventHandlerDelegate;
+ NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
if (handler != null) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b0cd3e4..1f00c7b 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -300,6 +300,14 @@
*/
@BufferFlag
public int flags;
+
+ /** @hide */
+ @NonNull
+ public BufferInfo dup() {
+ BufferInfo copy = new BufferInfo();
+ copy.set(offset, size, presentationTimeUs, flags);
+ return copy;
+ }
};
// The follow flag constants MUST stay in sync with their equivalents
@@ -343,11 +351,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface BufferFlag {}
+ private static class FrameRenderedInfo {
+ public long mPresentationTimeUs;
+ public long mNanoTime;
+ public FrameRenderedInfo(long presentationTimeUs, long nanoTime) {
+ mPresentationTimeUs = presentationTimeUs;
+ mNanoTime = nanoTime;
+ }
+ }
+
private EventHandler mEventHandler;
+ private EventHandler mOnFrameRenderedHandler;
+ private EventHandler mCallbackHandler;
private Callback mCallback;
+ private OnFrameRenderedListener mOnFrameRenderedListener;
+ private Object mListenerLock = new Object();
private static final int EVENT_CALLBACK = 1;
private static final int EVENT_SET_CALLBACK = 2;
+ private static final int EVENT_FRAME_RENDERED = 3;
private static final int CB_INPUT_AVAILABLE = 1;
private static final int CB_OUTPUT_AVAILABLE = 2;
@@ -375,6 +397,15 @@
mCallback = (MediaCodec.Callback) msg.obj;
break;
}
+ case EVENT_FRAME_RENDERED:
+ synchronized (mListenerLock) {
+ FrameRenderedInfo info = (FrameRenderedInfo)msg.obj;
+ if (mOnFrameRenderedListener != null) {
+ mOnFrameRenderedListener.onFrameRendered(
+ mCodec, info.mPresentationTimeUs, info.mNanoTime);
+ }
+ break;
+ }
default:
{
break;
@@ -431,6 +462,8 @@
}
}
+ private boolean mHasSurface = false;
+
/**
* Instantiate a decoder supporting input data of the given mime type.
*
@@ -501,6 +534,9 @@
} else {
mEventHandler = null;
}
+ mCallbackHandler = mEventHandler;
+ mOnFrameRenderedHandler = mEventHandler;
+
mBufferLock = new Object();
native_setup(name, nameIsType, encoder);
@@ -607,9 +643,66 @@
}
}
+ mHasSurface = surface != null;
+
native_configure(keys, values, surface, crypto, flags);
}
+ /**
+ * Dynamically sets the output surface of a codec.
+ * <p>
+ * This can only be used if the codec was configured with an output surface. The
+ * new output surface should have a compatible usage type to the original output surface.
+ * E.g. codecs may not support switching from a SurfaceTexture (GPU readable) output
+ * to ImageReader (software readable) output.
+ * @param surface the output surface to use. It must not be {@code null}.
+ * @throws IllegalStateException if the codec does not support setting the output
+ * surface in the current state.
+ * @throws IllegalArgumentException if the new surface is not of a suitable type for the codec.
+ */
+ public void setSurface(@NonNull Surface surface) {
+ if (!mHasSurface) {
+ throw new IllegalStateException("codec was not configured for an output surface");
+ }
+
+ // TODO implement this
+ throw new IllegalArgumentException("codec does not support this surface");
+ }
+
+ /**
+ * Create a persistent input surface that can be used with codecs that normally have an input
+ * surface, such as video encoders. A persistent input can be reused by subsequent
+ * {@link MediaCodec} or {@link MediaRecorder} instances, but can only be used by at
+ * most one codec or recorder instance concurrently.
+ * <p>
+ * The application is responsible for calling release() on the Surface when done.
+ *
+ * @return an input surface that can be used with {@link #usePersistentInputSurface}.
+ */
+ @NonNull
+ public static Surface createPersistentInputSurface() {
+ // TODO implement this
+ return new PersistentSurface();
+ }
+
+ static class PersistentSurface extends Surface {
+ PersistentSurface() {}
+ };
+
+ /**
+ * Configures the codec (e.g. encoder) to use a persistent input surface in place of input
+ * buffers. This may only be called after {@link #configure} and before {@link #start}, in
+ * lieu of {@link #createInputSurface}.
+ * @param surface a persistent input surface created by {@link #createPersistentInputSurface}
+ * @throws IllegalStateException if not in the Configured state or does not require an input
+ * surface.
+ * @throws IllegalArgumentException if the surface was not created by
+ * {@link #createPersistentInputSurface}.
+ */
+ public void usePersistentInputSurface(@NonNull Surface surface) {
+ throw new IllegalArgumentException("not implemented");
+ }
+
private native final void native_setCallback(@Nullable Callback cb);
private native final void native_configure(
@@ -662,9 +755,14 @@
native_stop();
freeAllTrackedBuffers();
- if (mEventHandler != null) {
- mEventHandler.removeMessages(EVENT_CALLBACK);
- mEventHandler.removeMessages(EVENT_SET_CALLBACK);
+ synchronized (mListenerLock) {
+ if (mCallbackHandler != null) {
+ mCallbackHandler.removeMessages(EVENT_SET_CALLBACK);
+ mCallbackHandler.removeMessages(EVENT_CALLBACK);
+ }
+ if (mOnFrameRenderedHandler != null) {
+ mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED);
+ }
}
}
@@ -1124,6 +1222,9 @@
cacheBuffers(false /* input */);
} else if (res >= 0) {
validateOutputByteBuffer(mCachedOutputBuffers, res, info);
+ if (mHasSurface) {
+ mDequeuedOutputInfos.put(res, info.dup());
+ }
}
}
return res;
@@ -1150,13 +1251,34 @@
* @throws MediaCodec.CodecException upon codec error.
*/
public final void releaseOutputBuffer(int index, boolean render) {
+ BufferInfo info = null;
synchronized(mBufferLock) {
invalidateByteBuffer(mCachedOutputBuffers, index);
mDequeuedOutputBuffers.remove(index);
+ if (mHasSurface) {
+ info = mDequeuedOutputInfos.remove(index);
+ }
}
+ // TODO
+ // until codec and libgui supports callback, assume frame is rendered within 50 ms
+ postRenderedCallback(render, info, 50 /* delayMs */);
releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
}
+ private void postRenderedCallback(boolean render, @Nullable BufferInfo info, long delayMs) {
+ if (render && info != null) {
+ synchronized (mListenerLock) {
+ if (mOnFrameRenderedListener != null) {
+ FrameRenderedInfo obj = new FrameRenderedInfo(
+ info.presentationTimeUs, System.nanoTime() + delayMs * 1000000);
+ Message msg = mOnFrameRenderedHandler.obtainMessage(
+ EVENT_FRAME_RENDERED, obj);
+ mOnFrameRenderedHandler.sendMessageDelayed(msg, delayMs);
+ }
+ }
+ }
+ }
+
/**
* If you are done with a buffer, use this call to update its surface timestamp
* and return it to the codec to render it on the output surface. If you
@@ -1207,10 +1329,20 @@
* @throws MediaCodec.CodecException upon codec error.
*/
public final void releaseOutputBuffer(int index, long renderTimestampNs) {
+ BufferInfo info = null;
synchronized(mBufferLock) {
invalidateByteBuffer(mCachedOutputBuffers, index);
mDequeuedOutputBuffers.remove(index);
+ if (mHasSurface) {
+ info = mDequeuedOutputInfos.remove(index);
+ }
}
+ // TODO
+ // until codec and libgui supports callback, assume frame is rendered at the
+ // render time or 16 ms from now, whichever is later.
+ postRenderedCallback(
+ true /* render */, info,
+ Math.max(renderTimestampNs - System.nanoTime(), 16666666) / 1000000);
releaseOutputBuffer(
index, true /* render */, true /* updatePTS */, renderTimestampNs);
}
@@ -1350,6 +1482,8 @@
private ByteBuffer[] mCachedOutputBuffers;
private final BufferMap mDequeuedInputBuffers = new BufferMap();
private final BufferMap mDequeuedOutputBuffers = new BufferMap();
+ private final Map<Integer, BufferInfo> mDequeuedOutputInfos =
+ new HashMap<Integer, BufferInfo>();
final private Object mBufferLock;
private final void invalidateByteBuffer(
@@ -1715,21 +1849,121 @@
* @param cb The callback that will run. Use {@code null} to clear a previously
* set callback (before {@link #configure configure} is called and run
* in synchronous mode).
+ * @param handler Callbacks will happen on the handler's thread. If {@code null},
+ * callbacks are done on the default thread (the caller's thread or the
+ * main thread.)
*/
- public void setCallback(@Nullable /* MediaCodec. */ Callback cb) {
- if (mEventHandler != null) {
- // set java callback on handler
- Message msg = mEventHandler.obtainMessage(EVENT_SET_CALLBACK, 0, 0, cb);
- mEventHandler.sendMessage(msg);
+ public void setCallback(@Nullable /* MediaCodec. */ Callback cb, @Nullable Handler handler) {
+ if (cb != null) {
+ synchronized (mListenerLock) {
+ EventHandler newHandler = getEventHandlerOn(handler, mCallbackHandler);
+ // NOTE: there are no callbacks on the handler at this time, but check anyways
+ // even if we were to extend this to be callable dynamically, it must
+ // be called when codec is flushed, so no messages are pending.
+ if (newHandler != mCallbackHandler) {
+ mCallbackHandler.removeMessages(EVENT_SET_CALLBACK);
+ mCallbackHandler.removeMessages(EVENT_CALLBACK);
+ mCallbackHandler = newHandler;
+ }
+ }
+ } else if (mCallbackHandler != null) {
+ mCallbackHandler.removeMessages(EVENT_SET_CALLBACK);
+ mCallbackHandler.removeMessages(EVENT_CALLBACK);
+ }
+
+ if (mCallbackHandler != null) {
+ // set java callback on main handler
+ Message msg = mCallbackHandler.obtainMessage(EVENT_SET_CALLBACK, 0, 0, cb);
+ mCallbackHandler.sendMessage(msg);
// set native handler here, don't post to handler because
- // it may cause the callback to be delayed and set in a wrong state,
- // and MediaCodec is already doing it on looper.
+ // it may cause the callback to be delayed and set in a wrong state.
+ // Note that native codec may start sending events to the callback
+ // handler after this returns.
native_setCallback(cb);
}
}
/**
+ * Sets an asynchronous callback for actionable MediaCodec events on the default
+ * looper.
+ * <p>
+ * Same as {@link #setCallback(Callback, Handler)} with handler set to null.
+ * @param cb The callback that will run. Use {@code null} to clear a previously
+ * set callback (before {@link #configure configure} is called and run
+ * in synchronous mode).
+ * @see #setCallback(Callback, Handler)
+ */
+ public void setCallback(@Nullable /* MediaCodec. */ Callback cb) {
+ setCallback(cb, null /* handler */);
+ }
+
+ /**
+ * Listener to be called when an output frame has rendered on the output surface
+ *
+ * @see MediaCodec#setOnFrameRenderedListener
+ */
+ public interface OnFrameRenderedListener {
+
+ /**
+ * Called when an output frame has rendered on the output surface.
+ *
+ * @param codec the MediaCodec instance
+ * @param presentationTimeUs the presentation time (media time) of the frame rendered.
+ * This is usually the same as specified in {@link #queueInputBuffer}; however,
+ * some codecs may alter the media time by applying some time-based transformation,
+ * such as frame rate conversion. In that case, presentation time corresponds
+ * to the actual output frame rendered.
+ * @param nanoTime The system time when the frame was rendered.
+ *
+ * @see System#nanoTime
+ */
+ public void onFrameRendered(
+ @NonNull MediaCodec codec, long presentationTimeUs, long nanoTime);
+ }
+
+ /**
+ * Register a callback to be invoked when an output frame is rendered on the output surface.
+ * <p>
+ * This method can be called in any codec state, but will only have an effect in the
+ * Executing state for codecs that render buffers to the output surface.
+ *
+ * @param listener the callback that will be run
+ * @param handler the callback will be run on the handler's thread. If {@code null},
+ * the callback will be run on the default thread, which is the looper
+ * from which the codec was created, or a new thread if there was none.
+ */
+ public void setOnFrameRenderedListener(
+ @Nullable OnFrameRenderedListener listener, @Nullable Handler handler) {
+ synchronized (mListenerLock) {
+ mOnFrameRenderedListener = listener;
+ if (listener != null) {
+ EventHandler newHandler = getEventHandlerOn(handler, mOnFrameRenderedHandler);
+ if (newHandler != mOnFrameRenderedHandler) {
+ mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED);
+ }
+ mOnFrameRenderedHandler = newHandler;
+ } else if (mOnFrameRenderedHandler != null) {
+ mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED);
+ }
+ }
+ }
+
+ private EventHandler getEventHandlerOn(
+ @Nullable Handler handler, @NonNull EventHandler lastHandler) {
+ if (handler == null) {
+ return mEventHandler;
+ } else {
+ Looper looper = handler.getLooper();
+ if (lastHandler.getLooper() == looper) {
+ return lastHandler;
+ } else {
+ return new EventHandler(this, looper);
+ }
+ }
+ }
+
+ /**
* MediaCodec callback interface. Used to notify the user asynchronously
* of various MediaCodec events.
*/
@@ -1772,9 +2006,17 @@
private void postEventFromNative(
int what, int arg1, int arg2, @Nullable Object obj) {
- if (mEventHandler != null) {
- Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj);
- mEventHandler.sendMessage(msg);
+ synchronized (mListenerLock) {
+ EventHandler handler = mEventHandler;
+ if (what == EVENT_CALLBACK) {
+ handler = mCallbackHandler;
+ } else if (what == EVENT_FRAME_RENDERED) {
+ handler = mOnFrameRenderedHandler;
+ }
+ if (handler != null) {
+ Message msg = handler.obtainMessage(what, arg1, arg2, obj);
+ handler.sendMessage(msg);
+ }
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b497001..ff1b57d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1136,6 +1136,27 @@
}
/**
+ * Returns the range of achievable video frame rates for a video size.
+ * May return {@code null}, if the codec did not publish any measurement
+ * data.
+ * <p>
+ * This is a performance estimate, based on full-speed decoding
+ * and encoding measurements of common video sizes supported by the codec.
+ *
+ * @param width the width of the video
+ * @param height the height of the video
+ *
+ * @throws IllegalArgumentException if the video size is not supported.
+ */
+ public Range<Double> getAchievableFrameRatesFor(int width, int height) {
+ if (!supports(width, height, null)) {
+ throw new IllegalArgumentException("unsupported size");
+ }
+ // TODO: get this data from the codec
+ return null;
+ }
+
+ /**
* Returns whether a given video size ({@code width} and
* {@code height}) and {@code frameRate} combination is supported.
*/
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 210d08f..a33fa59 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -17,6 +17,8 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.ContentResolver;
@@ -44,10 +46,12 @@
import android.media.AudioManager;
import android.media.MediaFormat;
import android.media.MediaTimeProvider;
+import android.media.PlaybackSettings;
import android.media.SubtitleController;
import android.media.SubtitleController.Anchor;
import android.media.SubtitleData;
import android.media.SubtitleTrack.RenderingWidget;
+import android.media.SyncSettings;
import com.android.internal.app.IAppOpsService;
@@ -471,16 +475,21 @@
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setScreenOnWhilePlaying</></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
* <tr><td>setPlaybackRate</p></td>
* <td>any </p></td>
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
+ * <tr><td>setPlaybackSettings</p></td>
+ * <td>any </p></td>
+ * <td>{} </p></td>
+ * <td>This method can be called in any state and calling it does not change
+ * the object state. </p></td></tr>
+ * <tr><td>setScreenOnWhilePlaying</></td>
+ * <td>any </p></td>
+ * <td>{} </p></td>
+ * <td>This method can be called in any state and calling it does not change
+ * the object state. </p></td></tr>
* <tr><td>setVolume </p></td>
* <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
* PlaybackCompleted}</p></td>
@@ -1342,6 +1351,8 @@
public native boolean isPlaying();
/**
+ * Change playback speed of audio by resampling the audio.
+ * <p>
* Specifies resampling as audio mode for variable rate playback, i.e.,
* resample the waveform based on the requested playback rate to get
* a new waveform, and play back the new waveform at the original sampling
@@ -1349,33 +1360,44 @@
* When rate is larger than 1.0, pitch becomes higher.
* When rate is smaller than 1.0, pitch becomes lower.
*/
- public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0;
+ public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
/**
+ * Change playback speed of audio without changing its pitch.
+ * <p>
* Specifies time stretching as audio mode for variable rate playback.
* Time stretching changes the duration of the audio samples without
* affecting its pitch.
- * FIXME: implement time strectching.
- * @hide
+ * <p>
+ * This mode is only supported for a limited range of playback speed factors,
+ * e.g. between 1/2x and 2x.
*/
public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
+ /**
+ * Change playback speed of audio without changing its pitch, and
+ * possibly mute audio if time stretching is not supported for the playback
+ * speed.
+ * <p>
+ * Try to keep audio pitch when changing the playback rate, but allow the
+ * system to determine how to change audio playback if the rate is out
+ * of range.
+ */
+ public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
+
/** @hide */
@IntDef(
value = {
+ PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
+ PLAYBACK_RATE_AUDIO_MODE_STRETCH,
PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
- PLAYBACK_RATE_AUDIO_MODE_STRETCH })
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface PlaybackRateAudioMode {}
/**
* Sets playback rate and audio mode.
*
- * <p> The supported audio modes are:
- * <ul>
- * <li> {@link #PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
- * </ul>
- *
* @param rate the ratio between desired playback rate and normal one.
* @param audioMode audio playback mode. Must be one of the supported
* audio modes.
@@ -1385,14 +1407,68 @@
* @throws IllegalArgumentException if audioMode is not supported.
*/
public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
- if (!isAudioPlaybackModeSupported(audioMode)) {
+ PlaybackSettings settings = new PlaybackSettings();
+ settings.allowDefaults();
+ switch (audioMode) {
+ case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
+ settings.setSpeed(rate).setPitch(1.0f);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
+ settings.setSpeed(rate).setPitch(1.0f)
+ .setAudioFallbackMode(settings.AUDIO_FALLBACK_MODE_FAIL);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
+ settings.setSpeed(rate).setPitch(rate);
+ break;
+ default:
final String msg = "Audio playback mode " + audioMode + " is not supported";
throw new IllegalArgumentException(msg);
}
- _setPlaybackRate(rate);
+ setPlaybackSettings(settings);
}
- private native void _setPlaybackRate(float rate) throws IllegalStateException;
+ /**
+ * Sets playback rate using {@link PlaybackSettings}.
+ *
+ * @param settings the playback settings.
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ * @throws IllegalArgumentException if settings is not supported.
+ */
+ public native void setPlaybackSettings(@NonNull PlaybackSettings settings);
+
+ /**
+ * Gets the playback settings, containing the current playback rate.
+ *
+ * @return the playback settings.
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ */
+ @NonNull
+ public native PlaybackSettings getPlaybackSettings();
+
+ /**
+ * Sets A/V sync mode.
+ *
+ * @param settings the A/V sync settings to apply
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ * @throws IllegalArgumentException if settings are not supported.
+ */
+ public native void setSyncSettings(@NonNull SyncSettings settings);
+
+ /**
+ * Gets the A/V sync mode.
+ *
+ * @return the A/V sync settings
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ */
+ @NonNull
+ public native SyncSettings getSyncSettings();
/**
* Seeks to specified time position.
@@ -1404,6 +1480,39 @@
public native void seekTo(int msec) throws IllegalStateException;
/**
+ * Get current playback position.
+ * <p>
+ * The MediaTimestamp represents how the media time correlates to the system time in
+ * a linear fashion. It contains the media time and system timestamp of an anchor frame
+ * ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
+ * and the speed of the media clock ({@link MediaTimestamp#clockRate}).
+ * <p>
+ * During regular playback, the media time moves fairly constantly (though the
+ * anchor frame may be rebased to a current system time, the linear correlation stays
+ * steady). Therefore, this method does not need to be called often.
+ * <p>
+ * To help users to get current playback position, this method always returns the timestamp of
+ * just-rendered frame, i.e., {@link System#nanoTime} and its corresponding media time. They
+ * can be used as current playback position.
+ *
+ * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
+ * is available, e.g. because the media player has not been initialized.
+ */
+ @Nullable
+ public MediaTimestamp getTimestamp()
+ {
+ try {
+ // TODO: get the timestamp from native side
+ return new MediaTimestamp(
+ getCurrentPosition() * 1000L,
+ System.nanoTime(),
+ isPlaying() ? getPlaybackSettings().getSpeed() : 0.f);
+ } catch (IllegalStateException e) {
+ return null;
+ }
+ }
+
+ /**
* Gets the current playback position.
*
* @return the current position in milliseconds
@@ -3219,14 +3328,6 @@
mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
}
- /*
- * Test whether a given audio playback mode is supported.
- * TODO query supported AudioPlaybackMode from player.
- */
- private boolean isAudioPlaybackModeSupported(int mode) {
- return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
- }
-
/** @hide */
static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
MediaTimeProvider {
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 876aebc..78fd9f0 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -140,6 +140,24 @@
public native Surface getSurface();
/**
+ * Configures the recorder to use a persistent surface when using SURFACE video source.
+ * <p> May only be called after {@link #prepare} in lieu of {@link #getSurface}.
+ * Frames rendered to the Surface before {@link #start} will be discarded.</p>
+
+ * @param surface a persistent input surface created by
+ * {@link MediaCodec#createPersistentInputSurface}
+ * @throws IllegalStateException if it is called before {@link #prepare}, after
+ * {@link #stop}, or is called when VideoSource is not set to SURFACE.
+ * @throws IllegalArgumentException if the surface was not created by
+ * {@link MediaCodec#createPersistentInputSurface}.
+ * @see MediaCodec#createPersistentInputSurface
+ * @see MediaRecorder.VideoSource
+ */
+ public void usePersistentSurface(Surface surface) {
+ throw new IllegalArgumentException("not implemented");
+ }
+
+ /**
* Sets a Surface to show a preview of recorded media (video). Calls this
* before prepare() to make sure that the desirable preview display is
* set. If {@link #setCamera(Camera)} is used and the surface has been
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index 74a2fb2..3b4f8e5 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioTrack;
+import android.media.PlaybackSettings;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -28,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
import java.util.LinkedList;
import java.util.List;
@@ -38,13 +40,13 @@
* <p>MediaSync is generally used like this:
* <pre>
* MediaSync sync = new MediaSync();
- * sync.configureSurface(surface);
+ * sync.setSurface(surface);
* Surface inputSurface = sync.createInputSurface();
* ...
* // MediaCodec videoDecoder = ...;
* videoDecoder.configure(format, inputSurface, ...);
* ...
- * sync.configureAudioTrack(audioTrack);
+ * sync.setAudioTrack(audioTrack);
* sync.setCallback(new MediaSync.Callback() {
* {@literal @Override}
* public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
@@ -94,8 +96,8 @@
*
* </pre>
*
- * The client needs to configure corresponding sink (i.e., Surface and AudioTrack) based on
- * the stream type it will play.
+ * The client needs to configure corresponding sink by setting the Surface and/or AudioTrack
+ * based on the stream type it will play.
* <p>
* For video, the client needs to call {@link #createInputSurface} to obtain a surface on
* which it will render video frames.
@@ -233,29 +235,33 @@
}
/**
- * Configures the output surface for MediaSync.
+ * Sets the output surface for MediaSync.
+ * <p>
+ * Currently, this is only supported in the Initialized state.
*
* @param surface Specify a surface on which to render the video data.
- * @throws IllegalArgumentException if the surface has been released, or is invalid.
+ * @throws IllegalArgumentException if the surface has been released, is invalid,
* or can not be connected.
- * @throws IllegalStateException if not in the Initialized state, or another surface
- * has already been configured.
+ * @throws IllegalStateException if setting the surface is not supported, e.g.
+ * not in the Initialized state, or another surface has already been configured.
*/
- public void configureSurface(@Nullable Surface surface) {
+ public void setSurface(@Nullable Surface surface) {
native_configureSurface(surface);
}
private native final void native_configureSurface(@Nullable Surface surface);
/**
- * Configures the audio track for MediaSync.
+ * Sets the audio track for MediaSync.
+ * <p>
+ * Currently, this is only supported in the Initialized state.
*
* @param audioTrack Specify an AudioTrack through which to render the audio data.
* @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
- * @throws IllegalStateException if not in the Initialized state, or another audio track
- * has already been configured.
+ * @throws IllegalStateException if setting the audio track is not supported, e.g.
+ * not in the Initialized state, or another audio track has already been configured.
*/
- public void configureAudioTrack(@Nullable AudioTrack audioTrack) {
+ public void setAudioTrack(@Nullable AudioTrack audioTrack) {
// AudioTrack has sanity check for configured sample rate.
int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
@@ -271,7 +277,7 @@
/**
* Requests a Surface to use as the input. This may only be called after
- * {@link #configureSurface}.
+ * {@link #setSurface}.
* <p>
* The application is responsible for calling release() on the Surface when
* done.
@@ -282,60 +288,58 @@
public native final Surface createInputSurface();
/**
- * Specifies resampling as audio mode for variable rate playback, i.e.,
- * resample the waveform based on the requested playback rate to get
+ * Resample audio data when changing playback speed.
+ * <p>
+ * Resample the waveform based on the requested playback rate to get
* a new waveform, and play back the new waveform at the original sampling
* frequency.
- * When rate is larger than 1.0, pitch becomes higher.
- * When rate is smaller than 1.0, pitch becomes lower.
+ * <p><ul>
+ * <li>When rate is larger than 1.0, pitch becomes higher.
+ * <li>When rate is smaller than 1.0, pitch becomes lower.
+ * </ul>
*/
- public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0;
+ public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
/**
- * Specifies time stretching as audio mode for variable rate playback.
+ * Time stretch audio when changing playback speed.
+ * <p>
* Time stretching changes the duration of the audio samples without
- * affecting its pitch.
- * FIXME: implement time strectching.
- * @hide
+ * affecting their pitch. This is only supported for a limited range
+ * of playback speeds, e.g. from 1/2x to 2x. If the rate is adjusted
+ * beyond this limit, the rate change will fail.
*/
public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
+ /**
+ * Time stretch audio when changing playback speed, and may mute if
+ * stretching is no longer supported.
+ * <p>
+ * Time stretching changes the duration of the audio samples without
+ * affecting their pitch. This is only supported for a limited range
+ * of playback speeds, e.g. from 1/2x to 2x. When it is no longer
+ * supported, the audio may be muted. Using this mode will not fail
+ * for non-negative playback rates.
+ */
+ public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
+
/** @hide */
@IntDef(
value = {
+ PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
+ PLAYBACK_RATE_AUDIO_MODE_STRETCH,
PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
- PLAYBACK_RATE_AUDIO_MODE_STRETCH })
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface PlaybackRateAudioMode {}
/**
- * Sets playback rate. It does same as {@link #setPlaybackRate(float, int)},
- * except that it always uses {@link #PLAYBACK_RATE_AUDIO_MODE_STRETCH} for audioMode.
- *
- * @param rate the ratio between desired playback rate and normal one. 1.0 means normal
- * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback,
- * while value between 0.0 and 1.0 for slower playback.
- *
- * @throws IllegalStateException if the internal sync engine or the audio track has not
- * been initialized.
- * TODO: unhide when PLAYBACK_RATE_AUDIO_MODE_STRETCH is supported.
- * @hide
- */
- public void setPlaybackRate(float rate) {
- setPlaybackRate(rate, PLAYBACK_RATE_AUDIO_MODE_STRETCH);
- }
-
- /**
* Sets playback rate and audio mode.
*
- * <p> The supported audio modes are:
- * <ul>
- * <li> {@link #PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
- * </ul>
- *
* @param rate the ratio between desired playback rate and normal one. 1.0 means normal
- * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback,
- * while value between 0.0 and 1.0 for slower playback.
+ * playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
+ * while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
+ * does not change as a result of this call. To restore the original rate at any time,
+ * use 1.0.
* @param audioMode audio playback mode. Must be one of the supported
* audio modes.
*
@@ -344,79 +348,175 @@
* @throws IllegalArgumentException if audioMode is not supported.
*/
public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
- if (!isAudioPlaybackModeSupported(audioMode)) {
- final String msg = "Audio playback mode " + audioMode + " is not supported";
- throw new IllegalArgumentException(msg);
- }
-
- int status = AudioTrack.SUCCESS;
- if (mAudioTrack != null) {
- int nativeSampleRateInHz = mAudioTrack.getSampleRate();
- int playbackSampleRate = (int)(rate * nativeSampleRateInHz + 0.5);
- rate = playbackSampleRate / (float)nativeSampleRateInHz;
-
- try {
- if (rate == 0.0) {
- mAudioTrack.pause();
- } else {
- status = mAudioTrack.setPlaybackRate(playbackSampleRate);
- mAudioTrack.play();
- }
- } catch (IllegalStateException e) {
- throw e;
+ PlaybackSettings rateSettings = new PlaybackSettings();
+ rateSettings.allowDefaults();
+ switch (audioMode) {
+ case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
+ rateSettings.setSpeed(rate).setPitch(1.0f);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
+ rateSettings.setSpeed(rate).setPitch(1.0f)
+ .setAudioFallbackMode(rateSettings.AUDIO_FALLBACK_MODE_FAIL);
+ break;
+ case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
+ rateSettings.setSpeed(rate).setPitch(rate);
+ break;
+ default:
+ {
+ final String msg = "Audio playback mode " + audioMode + " is not supported";
+ throw new IllegalArgumentException(msg);
}
}
+ setPlaybackSettings(rateSettings);
+ }
- if (status != AudioTrack.SUCCESS) {
- throw new IllegalArgumentException("Fail to set playback rate in audio track");
- }
+ /**
+ * Sets playback rate using {@link PlaybackSettings}.
+ * <p>
+ * When using MediaSync with {@link AudioTrack}, set playback settings using this
+ * call instead of calling it directly on the track, so that the sync is aware of
+ * the settings change.
+ * <p>
+ * This call also works if there is no audio track.
+ *
+ * @param settings the playback settings to use. {@link PlaybackSettings#getSpeed
+ * Speed} is the ratio between desired playback rate and normal one. 1.0 means
+ * normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback,
+ * while value between 0.0 and 1.0 for slower playback. <b>Note:</b> the normal rate
+ * does not change as a result of this call. To restore the original rate at any time,
+ * use speed of 1.0.
+ *
+ * @throws IllegalStateException if the internal sync engine or the audio track has not
+ * been initialized.
+ * @throws IllegalArgumentException if the settings are not supported.
+ */
+ public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
+ float rate;
+ try {
+ rate = settings.getSpeed();
- synchronized(mAudioLock) {
- mPlaybackRate = rate;
+ // rate is specified
+ if (mAudioTrack != null) {
+ try {
+ if (rate == 0.0) {
+ mAudioTrack.pause();
+ } else {
+ mAudioTrack.setPlaybackSettings(settings);
+ mAudioTrack.play();
+ }
+ } catch (IllegalStateException e) {
+ throw e;
+ }
+ }
+
+ synchronized(mAudioLock) {
+ mPlaybackRate = rate;
+ }
+ if (mPlaybackRate != 0.0 && mAudioThread != null) {
+ postRenderAudio(0);
+ }
+ native_setPlaybackRate(mPlaybackRate);
+ } catch (IllegalStateException e) {
+ // rate is not specified; still, propagate settings to audio track
+ if (mAudioTrack != null) {
+ mAudioTrack.setPlaybackSettings(settings);
+ }
}
- if (mPlaybackRate != 0.0 && mAudioThread != null) {
- postRenderAudio(0);
+ }
+
+ /**
+ * Gets the playback rate using {@link PlaybackSettings}.
+ *
+ * @return the playback rate being used.
+ *
+ * @throws IllegalStateException if the internal sync engine or the audio track has not
+ * been initialized.
+ */
+ @NonNull
+ public PlaybackSettings getPlaybackSettings() {
+ if (mAudioTrack != null) {
+ return mAudioTrack.getPlaybackSettings();
+ } else {
+ PlaybackSettings settings = new PlaybackSettings();
+ settings.allowDefaults();
+ settings.setSpeed(mPlaybackRate);
+ return settings;
}
- native_setPlaybackRate(mPlaybackRate);
}
private native final void native_setPlaybackRate(float rate);
- /*
- * Test whether a given audio playback mode is supported.
- * TODO query supported AudioPlaybackMode from audio track.
+ /**
+ * Sets A/V sync mode.
+ *
+ * @param settings the A/V sync settings to apply
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ * @throws IllegalArgumentException if settings are not supported.
*/
- private boolean isAudioPlaybackModeSupported(int mode) {
- return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ public native void setSyncSettings(@NonNull SyncSettings settings);
+
+ /**
+ * Gets the A/V sync mode.
+ *
+ * @return the A/V sync settings
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ */
+ @NonNull
+ public native SyncSettings getSyncSettings();
+
+ /**
+ * Flushes all buffers from the sync object.
+ * <p>
+ * No callbacks are received for the flushed buffers.
+ *
+ * @throws IllegalStateException if the internal player engine has not been
+ * initialized.
+ */
+ public void flush() {
+ synchronized(mAudioLock) {
+ mAudioBuffers.clear();
+ mCallbackHandler.removeCallbacksAndMessages(null);
+ }
+ // TODO implement this for surface buffers.
}
/**
* Get current playback position.
* <p>
- * The MediaTimestamp represents a clock ticking during media playback. It's represented
- * by an anchor frame ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
- * and clock speed ({@link MediaTimestamp#clockRate}). For continous playback with
- * constant speed, its anchor frame doesn't change that often. Thereafter, it's recommended
- * to not call this method often.
+ * The MediaTimestamp represents how the media time correlates to the system time in
+ * a linear fashion. It contains the media time and system timestamp of an anchor frame
+ * ({@link MediaTimestamp#mediaTimeUs} and {@link MediaTimestamp#nanoTime})
+ * and the speed of the media clock ({@link MediaTimestamp#clockRate}).
+ * <p>
+ * During regular playback, the media time moves fairly constantly (though the
+ * anchor frame may be rebased to a current system time, the linear correlation stays
+ * steady). Therefore, this method does not need to be called often.
* <p>
* To help users to get current playback position, this method always returns the timestamp of
* just-rendered frame, i.e., {@link System#nanoTime} and its corresponding media time. They
* can be used as current playback position.
*
- * @param timestamp a reference to a non-null MediaTimestamp instance allocated
- * and owned by caller.
- * @return true if a timestamp is available, or false if no timestamp is available.
- * If a timestamp if available, the MediaTimestamp instance is filled in with
- * playback rate, together with the current media timestamp and the system nanoTime
- * corresponding to the measured media timestamp.
- * In the case that no timestamp is available, any supplied instance is left unaltered.
+ * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
+ * is available, e.g. because the media sync has not been initialized.
*/
- public boolean getTimestamp(@NonNull MediaTimestamp timestamp)
+ @Nullable
+ public MediaTimestamp getTimestamp()
{
- if (timestamp == null) {
- throw new IllegalArgumentException();
+ try {
+ // TODO: create the timestamp in native
+ MediaTimestamp timestamp = new MediaTimestamp();
+ if (native_getTimestamp(timestamp)) {
+ return timestamp;
+ } else {
+ return null;
+ }
+ } catch (IllegalStateException e) {
+ return null;
}
- return native_getTimestamp(timestamp);
}
private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);
@@ -487,8 +587,9 @@
audioBuffer.mSizeInBytes -= sizeWritten;
}
- // TODO: wait time depends on fullness of audio track.
- postRenderAudio(10);
+ long pendingTimeMs = TimeUnit.MICROSECONDS.toMillis(
+ native_getPlayTimeForPendingAudioFrames());
+ postRenderAudio(pendingTimeMs / 2);
}
}
}, delayMillis);
@@ -497,6 +598,8 @@
private native final void native_updateQueuedAudioData(
int sizeInBytes, long presentationTimeUs);
+ private native final long native_getPlayTimeForPendingAudioFrames();
+
private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
synchronized(mCallbackLock) {
if (mCallbackHandler != null) {
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index 4b7e827..d3d5618 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -17,36 +17,55 @@
package android.media;
/**
- * Structure that groups clock rate of the stream playback, together with the media timestamp
+ * An immutable object that represents the linear correlation between the media time
+ * and the system time. It contains the media clock rate, together with the media timestamp
* of an anchor frame and the system time when that frame was presented or is committed
* to be presented.
- * The "present" means that audio/video produced on device is detectable by an external
+ * <p>
+ * The phrase "present" means that audio/video produced on device is detectable by an external
* observer off device.
* The time is based on the implementation's best effort, using whatever knowledge
* is available to the system, but cannot account for any delay unknown to the implementation.
- * The anchor frame could be any frame, including just-rendered frame, dependent on how
- * it's selected. When the anchor frame is the just-rendered one, the media time stands for
- * current position of the playback.
+ * The anchor frame could be any frame, including a just-rendered frame, or even a theoretical
+ * or in-between frame, based on the source of the MediaTimestamp.
+ * When the anchor frame is a just-rendered one, the media time stands for
+ * current position of the playback or recording.
*
* @see MediaSync#getTimestamp
+ * @see MediaPlayer#getTimestamp
*/
public final class MediaTimestamp
{
/**
- * Media timestamp in microseconds.
+ * Media time in microseconds.
*/
- public long mediaTimeUs;
+ public final long mediaTimeUs;
/**
- * The {@link java.lang.System#nanoTime} corresponding to the media timestamp.
+ * The {@link java.lang.System#nanoTime system time} corresponding to the media time
+ * in nanoseconds.
*/
- public long nanoTime;
+ public final long nanoTime;
/**
- * Media clock rate.
- * It is 1.0 if media clock is in sync with the system clock;
+ * The rate of the media clock in relation to the system time.
+ * It is 1.0 if media clock advances in sync with the system clock;
* greater than 1.0 if media clock is faster than the system clock;
* less than 1.0 if media clock is slower than the system clock.
*/
- public float clockRate;
+ public final float clockRate;
+
+ /** @hide */
+ MediaTimestamp(long mediaUs, long systemNs, float rate) {
+ mediaTimeUs = mediaUs;
+ nanoTime = systemNs;
+ clockRate = rate;
+ }
+
+ /** @hide */
+ MediaTimestamp() {
+ mediaTimeUs = 0;
+ nanoTime = 0;
+ clockRate = 1.0f;
+ }
}
diff --git a/media/java/android/media/OnAudioRecordRoutingListener.java b/media/java/android/media/OnAudioRecordRoutingListener.java
new file mode 100644
index 0000000..8ff41c5
--- /dev/null
+++ b/media/java/android/media/OnAudioRecordRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioRecordRoutingListener {
+ /**
+ * Called when the routing of an AudioRecord changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioRecordRouting(AudioRecord audioRecord);
+}
diff --git a/media/java/android/media/OnAudioTrackRoutingListener.java b/media/java/android/media/OnAudioTrackRoutingListener.java
new file mode 100644
index 0000000..18c72ef
--- /dev/null
+++ b/media/java/android/media/OnAudioTrackRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioTrackRoutingListener {
+ /**
+ * Called when the routing of an AudioTrack changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioTrackRouting(AudioTrack audioTrack);
+}
diff --git a/media/java/android/media/SyncSettings.java b/media/java/android/media/SyncSettings.java
new file mode 100644
index 0000000..9740147
--- /dev/null
+++ b/media/java/android/media/SyncSettings.java
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
+
+/**
+ * Structure for common A/V sync settings.
+ *
+ * Used by {@link MediaSync} {link MediaSync#getSyncSettings()} and
+ * {link MediaSync#setSyncSettings(SyncSettings)}
+ * to control A/V sync behavior.
+ * <p> <strong>audio adjust mode:</strong>
+ * select handling of audio track when changing playback speed due to sync.
+ * <ul>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_DEFAULT}:
+ * System will determine best handling. </li>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_STRETCH}:
+ * Change the speed of audio playback without altering its pitch.</li>
+ * <li> {@link SyncSettings#AUDIO_ADJUST_MODE_RESAMPLE}:
+ * Change the speed of audio playback by resampling the audio.</li>
+ * </ul>
+ * <p> <strong>sync source:</strong> select
+ * clock source for sync.
+ * <ul>
+ * <li> {@link SyncSettings#SYNC_SOURCE_DEFAULT}:
+ * System will determine best selection.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_SYSTEM_CLOCK}:
+ * Use system clock for sync source.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_AUDIO}:
+ * Use audio track for sync source.</li>
+ * <li> {@link SyncSettings#SYNC_SOURCE_VSYNC}:
+ * Syncronize media to vsync.</li>
+ * </ul>
+ * <p> <strong>tolerance:</strong> specifies the amount of allowed playback rate
+ * change to keep media in sync with the sync source. The handling of this depends
+ * on the sync source.
+ * <p> <strong>frameRate:</strong> initial hint for video frame rate. Used when
+ * sync source is vsync.
+ */
+public final class SyncSettings {
+ /** @hide */
+ @IntDef(
+ value = {
+ SYNC_SOURCE_DEFAULT,
+ SYNC_SOURCE_SYSTEM_CLOCK,
+ SYNC_SOURCE_AUDIO,
+ SYNC_SOURCE_VSYNC,
+ }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SyncSource {}
+
+ /**
+ * Use the default sync source (default). If media has video, the sync renders to a
+ * surface that directly renders to a display, and tolerance is non zero (e.g. not
+ * less than 0.001) vsync source is used for clock source. Otherwise, if media has
+ * audio, audio track is used. Finally, if media has no audio, system clock is used.
+ */
+ public static final int SYNC_SOURCE_DEFAULT = 0;
+
+ /**
+ * Use system monotonic clock for sync source.
+ *
+ * @see System#nanoTime
+ */
+ public static final int SYNC_SOURCE_SYSTEM_CLOCK = 1;
+
+ /**
+ * Use audio track for sync source. This requires audio data and an audio track.
+ *
+ * @see AudioTrack#getTimeStamp
+ */
+ public static final int SYNC_SOURCE_AUDIO = 2;
+
+ /**
+ * Use vsync as the sync source. This requires video data and an output surface that
+ * directly renders to the display, e.g. {@link android.view.SurfaceView}
+ * <p>
+ * This mode allows smoother playback experience by adjusting the playback speed
+ * to match the vsync rate, e.g. playing 30fps content on a 59.94Hz display.
+ * When using this mode, the tolerance should be set to greater than 0 (e.g. at least
+ * 1/1000), so that the playback speed can actually be adjusted.
+ * <p>
+ * This mode can also be used to play 25fps content on a 60Hz display using
+ * a 2:3 pulldown (basically playing the content at 24fps), which results on
+ * better playback experience on most devices. In this case the tolerance should be
+ * at least (1/24).
+ *
+ * @see android.view.Choreographer.FrameCallback#doFrame
+ * @see android.view.Display#getAppVsyncOffsetNanos
+ */
+ public static final int SYNC_SOURCE_VSYNC = 3;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ AUDIO_ADJUST_MODE_DEFAULT,
+ AUDIO_ADJUST_MODE_STRETCH,
+ AUDIO_ADJUST_MODE_RESAMPLE,
+ }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioAdjustMode {}
+
+ /**
+ * System will determine best handling of audio for playback rate
+ * adjustments.
+ * <p>
+ * Used by default. This will make audio play faster or slower as required
+ * by the sync source without changing its pitch; however, system may fall
+ * back to some other method (e.g. change the pitch, or mute the audio) if
+ * time stretching is no longer supported for the playback rate.
+ */
+ public static final int AUDIO_ADJUST_MODE_DEFAULT = 0;
+
+ /**
+ * Time stretch audio when playback rate must be adjusted.
+ * <p>
+ * This will make audio play faster or slower as required by the sync source
+ * without changing its pitch, as long as it is supported for the playback
+ * rate.
+ *
+ * @see MediaSync#PLAYBACK_RATE_AUDIO_MODE_STRETCH
+ * @see MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH
+ */
+ public static final int AUDIO_ADJUST_MODE_STRETCH = 1;
+
+ /**
+ * Resample audio when playback rate must be adjusted.
+ * <p>
+ * This will make audio play faster or slower as required by the sync source
+ * by changing its pitch (making it lower to play slower, and higher to play
+ * faster.)
+ *
+ * @see MediaSync#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE
+ * @see MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE
+ */
+ public static final int AUDIO_ADJUST_MODE_RESAMPLE = 2;
+
+ // flags to indicate which settings are actually set
+ private static final int SET_SYNC_SOURCE = 1 << 0;
+ private static final int SET_AUDIO_ADJUST_MODE = 1 << 1;
+ private static final int SET_TOLERANCE = 1 << 2;
+ private static final int SET_FRAME_RATE = 1 << 3;
+ private int mSet = 0;
+
+ // settings
+ private int mAudioAdjustMode = AUDIO_ADJUST_MODE_STRETCH;
+ private int mSyncSource = SYNC_SOURCE_DEFAULT;
+ private float mTolerance = 0.f;
+ private float mFrameRate = 0.f;
+
+ /**
+ * Allows defaults to be returned for properties not set.
+ * Otherwise a {@link java.lang.IllegalArgumentException} exception
+ * is raised when getting those properties
+ * which have defaults but have never been set.
+ * @return this <code>SyncSettings</code> instance.
+ */
+ public SyncSettings allowDefaults() {
+ mSet |= SET_SYNC_SOURCE | SET_AUDIO_ADJUST_MODE | SET_TOLERANCE;
+ return this;
+ }
+
+ /**
+ * Sets the audio adjust mode.
+ * @param audioAdjustMode
+ * @return this <code>SyncSettings</code> instance.
+ */
+ public SyncSettings setAudioAdjustMode(@AudioAdjustMode int audioAdjustMode) {
+ mAudioAdjustMode = audioAdjustMode;
+ mSet |= SET_AUDIO_ADJUST_MODE;
+ return this;
+ }
+
+ /**
+ * Retrieves the audio adjust mode.
+ * @return audio adjust mode
+ * @throws IllegalStateException if the audio adjust mode is not set.
+ */
+ public @AudioAdjustMode int getAudioAdjustMode() {
+ if ((mSet & SET_AUDIO_ADJUST_MODE) == 0) {
+ throw new IllegalStateException("audio adjust mode not set");
+ }
+ return mAudioAdjustMode;
+ }
+
+ /**
+ * Sets the sync source.
+ * @param syncSource
+ * @return this <code>SyncSettings</code> instance.
+ */
+ public SyncSettings setSyncSource(@SyncSource int syncSource) {
+ mSyncSource = syncSource;
+ mSet |= SET_SYNC_SOURCE;
+ return this;
+ }
+
+ /**
+ * Retrieves the sync source.
+ * @return sync source
+ * @throws IllegalStateException if the sync source is not set.
+ */
+ public @SyncSource int getSyncSource() {
+ if ((mSet & SET_SYNC_SOURCE) == 0) {
+ throw new IllegalStateException("sync source not set");
+ }
+ return mSyncSource;
+ }
+
+ /**
+ * Sets the tolerance. The default tolerance is 0.
+ * @param tolerance A non-negative number representing
+ * the maximum deviation of the playback rate from the playback rate
+ * set. ({@code abs(actual_rate - set_rate) / set_rate})
+ * @return this <code>SyncSettings</code> instance.
+ */
+ public SyncSettings setTolerance(float tolerance) {
+ mTolerance = tolerance;
+ mSet |= SET_TOLERANCE;
+ return this;
+ }
+
+ /**
+ * Retrieves the tolerance factor.
+ * @return tolerance factor. A non-negative number representing
+ * the maximum deviation of the playback rate from the playback rate
+ * set. ({@code abs(actual_rate - set_rate) / set_rate})
+ * @throws IllegalStateException if tolerance is not set.
+ */
+ public float getTolerance() {
+ if ((mSet & SET_TOLERANCE) == 0) {
+ throw new IllegalStateException("tolerance not set");
+ }
+ return mTolerance;
+ }
+
+ /**
+ * Sets the video frame rate hint to be used. By default the frame rate is unspecified.
+ * @param frameRate A non-negative number used as an initial hint on
+ * the video frame rate to be used when using vsync as the sync source.
+ * @return this <code>SyncSettings</code> instance.
+ */
+ public SyncSettings setFrameRate(float frameRate) {
+ mFrameRate = frameRate;
+ mSet |= SET_FRAME_RATE;
+ return this;
+ }
+
+ /**
+ * Retrieves the video frame rate hint.
+ * @return frame rate factor. A non-negative number representing
+ * the maximum deviation of the playback rate from the playback rate
+ * set. ({@code abs(actual_rate - set_rate) / set_rate})
+ * @throws IllegalStateException if frame rate is not set.
+ */
+ public float getFrameRate() {
+ if ((mSet & SET_FRAME_RATE) == 0) {
+ throw new IllegalStateException("frame rate not set");
+ }
+ return mFrameRate;
+ }
+
+}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 9bdeb25..278d627 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -883,6 +883,10 @@
/**
* Called when the application sets playback rate and audio mode.
+ * <p>
+ * Once a playback rate is set, the implementation should honor the value until a new tune
+ * request. Pause/resume/seek request does not reset the playback rate previously set.
+ * </p>
*
* @param rate The ratio between desired playback rate and normal one.
* @param audioMode Audio playback mode. Must be one of the supported audio modes:
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index c8464c7..dbb53b4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_AmrInputStream.cpp \
android_media_ImageWriter.cpp \
android_media_ImageReader.cpp \
android_media_MediaCrypto.cpp \
@@ -14,12 +15,12 @@
android_media_MediaMetadataRetriever.cpp \
android_media_MediaMuxer.cpp \
android_media_MediaPlayer.cpp \
+ android_media_MediaProfiles.cpp \
android_media_MediaRecorder.cpp \
android_media_MediaScanner.cpp \
android_media_MediaSync.cpp \
android_media_ResampleInputStream.cpp \
- android_media_MediaProfiles.cpp \
- android_media_AmrInputStream.cpp \
+ android_media_SyncSettings.cpp \
android_media_Utils.cpp \
android_mtp_MtpDatabase.cpp \
android_mtp_MtpDevice.cpp \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c247220..2c61779 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -20,6 +20,7 @@
#include "utils/Log.h"
#include <media/mediaplayer.h>
+#include <media/AudioResamplerPublic.h>
#include <media/IMediaHTTPService.h>
#include <media/MediaPlayerInterface.h>
#include <stdio.h>
@@ -37,6 +38,8 @@
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_media_MediaDataSource.h"
+#include "android_media_PlaybackSettings.h"
+#include "android_media_SyncSettings.h"
#include "android_media_Utils.h"
#include "android_os_Parcel.h"
@@ -66,6 +69,9 @@
};
static fields_t fields;
+static PlaybackSettings::fields_t gPlaybackSettingsFields;
+static SyncSettings::fields_t gSyncSettingsFields;
+
static Mutex sLock;
// ----------------------------------------------------------------------------
@@ -420,15 +426,105 @@
}
static void
-android_media_MediaPlayer_setPlaybackRate(JNIEnv *env, jobject thiz, jfloat rate)
+android_media_MediaPlayer_setPlaybackSettings(JNIEnv *env, jobject thiz, jobject settings)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
- ALOGV("setPlaybackRate: %f", rate);
- process_media_player_call(env, thiz, mp->setPlaybackRate(rate), NULL, NULL);
+
+ PlaybackSettings pbs;
+ pbs.fillFromJobject(env, gPlaybackSettingsFields, settings);
+ ALOGV("setPlaybackSettings: %d:%f %d:%f %d:%u %d:%u",
+ pbs.speedSet, pbs.audioRate.mSpeed,
+ pbs.pitchSet, pbs.audioRate.mPitch,
+ pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode,
+ pbs.audioStretchModeSet, pbs.audioRate.mStretchMode);
+
+ // TODO: pass playback settings to mediaplayer when audiotrack supports it
+ process_media_player_call(env, thiz, mp->setPlaybackRate(pbs.audioRate.mSpeed), NULL, NULL);
+}
+
+static jobject
+android_media_MediaPlayer_getPlaybackSettings(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ PlaybackSettings pbs;
+ AudioPlaybackRate &audioRate = pbs.audioRate;
+
+ audioRate.mSpeed = 1.0f;
+ audioRate.mPitch = 1.0f;
+ audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_DEFAULT;
+ audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_DEFAULT;
+
+ // TODO: get this from mediaplayer when audiotrack supports it
+ // process_media_player_call(
+ // env, thiz, mp->getPlaybackSettings(&audioRate), NULL, NULL);
+ ALOGV("getPlaybackSettings: %f %f %d %d",
+ audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
+
+ pbs.speedSet = true;
+ pbs.pitchSet = true;
+ pbs.audioFallbackModeSet = true;
+ pbs.audioStretchModeSet = true;
+
+ return pbs.asJobject(env, gPlaybackSettingsFields);
+}
+
+static void
+android_media_MediaPlayer_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ SyncSettings scs;
+ scs.fillFromJobject(env, gSyncSettingsFields, settings);
+ ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
+ scs.syncSourceSet, scs.syncSource,
+ scs.audioAdjustModeSet, scs.audioAdjustMode,
+ scs.toleranceSet, scs.tolerance,
+ scs.frameRateSet, scs.frameRate);
+
+ // TODO: pass sync settings to mediaplayer when it supports it
+ // process_media_player_call(env, thiz, mp->setSyncSettings(scs), NULL, NULL);
+}
+
+static jobject
+android_media_MediaPlayer_getSyncSettings(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ SyncSettings scs;
+ scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
+ scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
+ scs.tolerance = 0.f;
+ scs.frameRate = 0.f;
+
+ // TODO: get this from mediaplayer when it supports it
+ // process_media_player_call(
+ // env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+ ALOGV("getSyncSettings: %d %d %f %f",
+ scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+
+ scs.syncSourceSet = true;
+ scs.audioAdjustModeSet = true;
+ scs.toleranceSet = true;
+ scs.frameRateSet = false;
+
+ return scs.asJobject(env, gSyncSettingsFields);
}
static void
@@ -697,6 +793,8 @@
return;
}
+ env->DeleteLocalRef(clazz);
+
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
@@ -710,6 +808,11 @@
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
+
+ env->DeleteLocalRef(clazz);
+
+ gPlaybackSettingsFields.init(env);
+ gSyncSettingsFields.init(env);
}
static void
@@ -898,7 +1001,10 @@
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
- {"_setPlaybackRate", "(F)V", (void *)android_media_MediaPlayer_setPlaybackRate},
+ {"setPlaybackSettings", "(Landroid/media/PlaybackSettings;)V", (void *)android_media_MediaPlayer_setPlaybackSettings},
+ {"getPlaybackSettings", "()Landroid/media/PlaybackSettings;", (void *)android_media_MediaPlayer_getPlaybackSettings},
+ {"setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaPlayer_setSyncSettings},
+ {"getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaPlayer_getSyncSettings},
{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
{"_pause", "()V", (void *)android_media_MediaPlayer_pause},
{"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index e167f83..f192262 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -21,6 +21,7 @@
#include "android_media_MediaSync.h"
#include "android_media_AudioTrack.h"
+#include "android_media_SyncSettings.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
#include "jni.h"
@@ -46,6 +47,7 @@
};
static fields_t gFields;
+static SyncSettings::fields_t gSyncSettingsFields;
////////////////////////////////////////////////////////////////////////////////
@@ -84,6 +86,10 @@
return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
}
+status_t JMediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+ return mSync->getPlayTimeForPendingAudioFrames(outTimeUs);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -266,6 +272,70 @@
return JNI_TRUE;
}
+static jlong android_media_MediaSync_native_getPlayTimeForPendingAudioFrames(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ }
+
+ int64_t playTimeUs = 0;
+ status_t err = sync->getPlayTimeForPendingAudioFrames(&playTimeUs);
+ if (err != NO_ERROR) {
+ throwExceptionAsNecessary(env, err);
+ }
+ return (jlong)playTimeUs;
+}
+
+static void
+android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
+{
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ SyncSettings scs;
+ scs.fillFromJobject(env, gSyncSettingsFields, settings);
+ ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
+ scs.syncSourceSet, scs.syncSource,
+ scs.audioAdjustModeSet, scs.audioAdjustMode,
+ scs.toleranceSet, scs.tolerance,
+ scs.frameRateSet, scs.frameRate);
+
+ // TODO: pass sync settings to mediasync when it supports it
+}
+
+static jobject
+android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz)
+{
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return NULL;
+ }
+
+ SyncSettings scs;
+ scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
+ scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
+ scs.tolerance = 0.f;
+ scs.frameRate = 0.f;
+
+ // TODO: get this from mediaplayer when it supports it
+ // process_media_player_call(
+ // env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
+ ALOGV("getSyncSettings: %d %d %f %f",
+ scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+
+ scs.syncSourceSet = true;
+ scs.audioAdjustModeSet = true;
+ scs.toleranceSet = true;
+ scs.frameRateSet = false;
+
+ return scs.asJobject(env, gSyncSettingsFields);
+}
+
static void android_media_MediaSync_native_init(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(env, env->FindClass("android/media/MediaSync"));
CHECK(clazz.get() != NULL);
@@ -287,6 +357,8 @@
gFields.mediaTimestampClockRateID =
env->GetFieldID(clazz.get(), "clockRate", "F");
CHECK(gFields.mediaTimestampClockRateID != NULL);
+
+ gSyncSettingsFields.init(env);
}
static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
@@ -334,6 +406,10 @@
"(Landroid/media/MediaTimestamp;)Z",
(void *)android_media_MediaSync_native_getTimestamp },
+ { "native_getPlayTimeForPendingAudioFrames",
+ "()J",
+ (void *)android_media_MediaSync_native_getPlayTimeForPendingAudioFrames },
+
{ "native_init", "()V", (void *)android_media_MediaSync_native_init },
{ "native_setup", "()V", (void *)android_media_MediaSync_native_setup },
@@ -342,6 +418,10 @@
{ "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate },
+ { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings},
+
+ { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings},
+
{ "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize },
};
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index 9e5de7e..cf81a72 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -41,6 +41,8 @@
status_t setPlaybackRate(float rate);
+ status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
sp<const MediaClock> getMediaClock();
protected:
diff --git a/media/jni/android_media_PlaybackSettings.h b/media/jni/android_media_PlaybackSettings.h
new file mode 100644
index 0000000..1f4f256
--- /dev/null
+++ b/media/jni/android_media_PlaybackSettings.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_
+#define _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_
+
+#include <media/AudioResamplerPublic.h>
+
+namespace android {
+
+// This entire class is inline as it is used from both core and media
+struct PlaybackSettings {
+ AudioPlaybackRate audioRate;
+ bool speedSet;
+ bool pitchSet;
+ bool audioFallbackModeSet;
+ bool audioStretchModeSet;
+
+ struct fields_t {
+ jclass clazz;
+ jmethodID constructID;
+
+ jfieldID speed;
+ jfieldID pitch;
+ jfieldID audio_fallback_mode;
+ jfieldID audio_stretch_mode;
+ jfieldID set;
+ jint set_speed;
+ jint set_pitch;
+ jint set_audio_fallback_mode;
+ jint set_audio_stretch_mode;
+
+ void init(JNIEnv *env) {
+ jclass lclazz = env->FindClass("android/media/PlaybackSettings");
+ if (lclazz == NULL) {
+ return;
+ }
+
+ clazz = (jclass)env->NewGlobalRef(lclazz);
+ if (clazz == NULL) {
+ return;
+ }
+
+ constructID = env->GetMethodID(clazz, "<init>", "()V");
+
+ speed = env->GetFieldID(clazz, "mSpeed", "F");
+ pitch = env->GetFieldID(clazz, "mPitch", "F");
+ audio_fallback_mode = env->GetFieldID(clazz, "mAudioFallbackMode", "I");
+ audio_stretch_mode = env->GetFieldID(clazz, "mAudioStretchMode", "I");
+ set = env->GetFieldID(clazz, "mSet", "I");
+
+ set_speed =
+ env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SPEED", "I"));
+ set_pitch =
+ env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_PITCH", "I"));
+ set_audio_fallback_mode = env->GetStaticIntField(
+ clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_FALLBACK_MODE", "I"));
+ set_audio_stretch_mode = env->GetStaticIntField(
+ clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_STRETCH_MODE", "I"));
+
+ env->DeleteLocalRef(lclazz);
+ }
+
+ void exit(JNIEnv *env) {
+ env->DeleteGlobalRef(clazz);
+ clazz = NULL;
+ }
+ };
+
+ void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) {
+ audioRate.mSpeed = env->GetFloatField(settings, fields.speed);
+ audioRate.mPitch = env->GetFloatField(settings, fields.pitch);
+ audioRate.mFallbackMode =
+ (AudioTimestretchFallbackMode)env->GetIntField(settings, fields.audio_fallback_mode);
+ audioRate.mStretchMode =
+ (AudioTimestretchStretchMode)env->GetIntField(settings, fields.audio_stretch_mode);
+ int set = env->GetIntField(settings, fields.set);
+
+ speedSet = set & fields.set_speed;
+ pitchSet = set & fields.set_pitch;
+ audioFallbackModeSet = set & fields.set_audio_fallback_mode;
+ audioStretchModeSet = set & fields.set_audio_stretch_mode;
+ }
+
+ jobject asJobject(JNIEnv *env, const fields_t& fields) {
+ jobject settings = env->NewObject(fields.clazz, fields.constructID);
+ if (settings == NULL) {
+ return NULL;
+ }
+ env->SetFloatField(settings, fields.speed, (jfloat)audioRate.mSpeed);
+ env->SetFloatField(settings, fields.pitch, (jfloat)audioRate.mPitch);
+ env->SetIntField(settings, fields.audio_fallback_mode, (jint)audioRate.mFallbackMode);
+ env->SetIntField(settings, fields.audio_stretch_mode, (jint)audioRate.mStretchMode);
+ env->SetIntField(
+ settings, fields.set,
+ (speedSet ? fields.set_speed : 0)
+ | (pitchSet ? fields.set_pitch : 0)
+ | (audioFallbackModeSet ? fields.set_audio_fallback_mode : 0)
+ | (audioStretchModeSet ? fields.set_audio_stretch_mode : 0));
+
+ return settings;
+ }
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_
diff --git a/media/jni/android_media_SyncSettings.cpp b/media/jni/android_media_SyncSettings.cpp
new file mode 100644
index 0000000..2f0605e
--- /dev/null
+++ b/media/jni/android_media_SyncSettings.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#include "android_media_SyncSettings.h"
+
+#include "JNIHelp.h"
+
+namespace android {
+
+void SyncSettings::fields_t::init(JNIEnv *env) {
+ jclass lclazz = env->FindClass("android/media/SyncSettings");
+ if (lclazz == NULL) {
+ return;
+ }
+
+ clazz = (jclass)env->NewGlobalRef(lclazz);
+ if (clazz == NULL) {
+ return;
+ }
+
+ constructID = env->GetMethodID(clazz, "<init>", "()V");
+
+ sync_source = env->GetFieldID(clazz, "mSyncSource", "I");
+ audio_adjust_mode = env->GetFieldID(clazz, "mAudioAdjustMode", "I");
+ tolerance = env->GetFieldID(clazz, "mTolerance", "F");
+ frame_rate = env->GetFieldID(clazz, "mFrameRate", "F");
+ set = env->GetFieldID(clazz, "mSet", "I");
+
+ set_sync_source =
+ env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SYNC_SOURCE", "I"));
+ set_audio_adjust_mode = env->GetStaticIntField(
+ clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_ADJUST_MODE", "I"));
+ set_tolerance =
+ env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_TOLERANCE", "I"));
+ set_frame_rate =
+ env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_FRAME_RATE", "I"));
+
+ env->DeleteLocalRef(lclazz);
+}
+
+void SyncSettings::fields_t::exit(JNIEnv *env) {
+ env->DeleteGlobalRef(clazz);
+ clazz = NULL;
+}
+
+void SyncSettings::fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) {
+ syncSource = env->GetIntField(settings, fields.sync_source);
+ audioAdjustMode = env->GetIntField(settings, fields.audio_adjust_mode);
+ tolerance = env->GetFloatField(settings, fields.tolerance);
+ frameRate = env->GetFloatField(settings, fields.frame_rate);
+ int set = env->GetIntField(settings, fields.set);
+
+ syncSourceSet = set & fields.set_sync_source;
+ audioAdjustModeSet = set & fields.set_audio_adjust_mode;
+ toleranceSet = set & fields.set_tolerance;
+ frameRateSet = set & fields.set_frame_rate;
+}
+
+jobject SyncSettings::asJobject(JNIEnv *env, const fields_t& fields) {
+ jobject settings = env->NewObject(fields.clazz, fields.constructID);
+ if (settings == NULL) {
+ return NULL;
+ }
+ env->SetIntField(settings, fields.sync_source, (jint)syncSource);
+ env->SetIntField(settings, fields.audio_adjust_mode, (jint)audioAdjustMode);
+ env->SetFloatField(settings, fields.tolerance, (jfloat)tolerance);
+ env->SetFloatField(settings, fields.frame_rate, (jfloat)frameRate);
+ env->SetIntField(
+ settings, fields.set,
+ (syncSourceSet ? fields.set_sync_source : 0)
+ | (audioAdjustModeSet ? fields.set_audio_adjust_mode : 0)
+ | (toleranceSet ? fields.set_tolerance : 0)
+ | (frameRateSet ? fields.set_frame_rate : 0));
+
+ return settings;
+}
+
+} // namespace android
diff --git a/media/jni/android_media_SyncSettings.h b/media/jni/android_media_SyncSettings.h
new file mode 100644
index 0000000..586533f
--- /dev/null
+++ b/media/jni/android_media_SyncSettings.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_SYNC_SETTINGS_H_
+#define _ANDROID_MEDIA_SYNC_SETTINGS_H_
+
+#include "jni.h"
+
+namespace android {
+
+struct SyncSettings {
+ // keep this here until it is implemented
+ int syncSource;
+ int audioAdjustMode;
+ float tolerance;
+ float frameRate;
+
+ bool syncSourceSet;
+ bool audioAdjustModeSet;
+ bool toleranceSet;
+ bool frameRateSet;
+
+ struct fields_t {
+ jclass clazz;
+ jmethodID constructID;
+
+ jfieldID sync_source;
+ jfieldID audio_adjust_mode;
+ jfieldID tolerance;
+ jfieldID frame_rate;
+ jfieldID set;
+ jint set_sync_source;
+ jint set_audio_adjust_mode;
+ jint set_tolerance;
+ jint set_frame_rate;
+
+ // initializes fields
+ void init(JNIEnv *env);
+
+ // releases global references held
+ void exit(JNIEnv *env);
+ };
+
+ // fills this from an android.media.SyncSettings object
+ void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings);
+
+ // returns this as a android.media.SyncSettings object
+ jobject asJobject(JNIEnv *env, const fields_t& fields);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_SYNC_SETTINGS_H_
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 2f97809..67d8ab6 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -11,3 +11,5 @@
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index 068c806..4786d28 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -18,11 +18,9 @@
<bool name="show_as_dialog">true</bool>
<item type="dimen" name="dialog_width">85%</item>
- <item type="dimen" name="dialog_height">90%</item>
<dimen name="grid_padding_horiz">24dp</dimen>
<dimen name="grid_padding_vert">16dp</dimen>
<dimen name="grid_item_padding">8dp</dimen>
-
</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index 45ed7ad..0b03a94 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,6 +16,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DialogWhenReallyLarge" parent="@*android:style/Theme.Material.DayNight.Dialog.FixedSize" />
+ <style name="DialogWhenReallyLarge" parent="@*android:style/Theme.Material.DayNight.Dialog">
+ <!-- We do not specify width of window here because the max size of
+ floating window specified by windowFixedWidthis is limited. -->
+ <item name="*android:windowFixedHeightMajor">80%</item>
+ <item name="*android:windowFixedHeightMinor">90%</item>
+ </style>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 062d433..5281087 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -121,22 +121,20 @@
<string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string>
<!-- Toast shown when a file copy is kicked off -->
<plurals name="copy_begin">
- <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
+ <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
+ <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
</plurals>
<!-- Text shown on the copy notification while DocumentsUI performs setup in preparation for copying files [CHAR LIMIT=32] -->
<string name="copy_preparing">Preparing for copy\u2026</string>
<!-- Title of the copy error notification [CHAR LIMIT=48] -->
<plurals name="copy_error_notification_title">
- <item quantity="one">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Couldn\'t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+ <item quantity="other">Couldn\'t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
</plurals>
<!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
<string name="notification_touch_for_details">Touch to view details</string>
<!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] -->
<string name="retry">Retry</string>
- <!-- Title of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_title">Error copying files</string>
<!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_content">Following files are not copied: <xliff:g id="list">%1$s</xliff:g></string>
+ <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 45dc20b..97509f7 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -33,13 +33,6 @@
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowNoTitle">true</item>
- <item name="*android:windowFixedWidthMajor">@null</item>
- <item name="*android:windowFixedWidthMinor">@null</item>
- <item name="*android:windowMinWidthMajor">@null</item>
- <item name="*android:windowMinWidthMinor">@null</item>
- <item name="*android:windowFixedHeightMajor">80%</item>
- <item name="*android:windowFixedHeightMinor">90%</item>
-
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
</style>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index a9f03b6..2e0bece 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -26,6 +26,7 @@
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
@@ -37,12 +38,14 @@
import android.provider.DocumentsContract.Document;
import android.text.format.DateUtils;
import android.util.Log;
+import android.widget.Toast;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import libcore.io.IoUtils;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -70,7 +73,7 @@
private volatile boolean mIsCancelled;
// Parameters of the copy job. Requests to an IntentService are serialized so this code only
// needs to deal with one job at a time.
- private final ArrayList<Uri> mFailedFiles;
+ private final ArrayList<DocumentInfo> mFailedFiles;
private long mBatchSize;
private long mBytesCopied;
private long mStartTime;
@@ -88,7 +91,27 @@
public CopyService() {
super("CopyService");
- mFailedFiles = new ArrayList<Uri>();
+ mFailedFiles = new ArrayList<DocumentInfo>();
+ }
+
+ /**
+ * Starts the service for a copy operation.
+ *
+ * @param context Context for the intent.
+ * @param srcDocs A list of src files to copy.
+ * @param dstStack The copy destination stack.
+ */
+ public static void start(Context context, List<DocumentInfo> srcDocs, DocumentStack dstStack) {
+ final Resources res = context.getResources();
+ final Intent copyIntent = new Intent(context, CopyService.class);
+ copyIntent.putParcelableArrayListExtra(
+ EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
+ copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+
+ Toast.makeText(context,
+ res.getQuantityString(R.plurals.copy_begin, srcDocs.size(), srcDocs.size()),
+ Toast.LENGTH_SHORT).show();
+ context.startService(copyIntent);
}
@Override
@@ -360,7 +383,7 @@
if (dstUri == null) {
// If this is a directory, the entire subdir will not be copied over.
Log.e(TAG, "Error while copying " + srcInfo.displayName);
- mFailedFiles.add(srcInfo.derivedUri);
+ mFailedFiles.add(srcInfo);
return;
}
@@ -444,7 +467,12 @@
} catch (IOException e) {
errorOccurred = true;
Log.e(TAG, "Error while copying " + srcUri.toString(), e);
- mFailedFiles.add(srcUri);
+ try {
+ mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
+ } catch (FileNotFoundException ignore) {
+ Log.w(TAG, "Source file gone: " + srcUri, e);
+ // The source file is gone.
+ }
} finally {
// This also ensures the file descriptors are closed.
IoUtils.closeQuietly(src);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 1a17ee0..1f7b41e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -56,7 +56,11 @@
final Context context = getActivity();
final ContentResolver resolver = context.getContentResolver();
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ // We need to specify android.R.style.Theme_DeviceDefault_Dialog explicitly,
+ // because the application theme 'DialogWhenReallyLarge' has
+ // fixed window size properties for large screen devices.
+ final AlertDialog.Builder builder = new AlertDialog.Builder(
+ context, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
final View view = dialogInflater.inflate(R.layout.dialog_create_dir, null, false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 7cf58cc..a789da8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -82,6 +82,7 @@
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Lists;
@@ -341,9 +342,6 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- final Context context = getActivity();
- final Resources res = context.getResources();
-
// There's only one request code right now. Replace this with a switch statement or
// something more scalable when more codes are added.
if (requestCode != REQUEST_COPY_DESTINATION) {
@@ -355,15 +353,8 @@
return;
}
- final List<DocumentInfo> docs = getDisplayState(this).selectedDocumentsForCopy;
- final Intent copyIntent = new Intent(context, CopyService.class);
- copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs));
- copyIntent.putExtra(CopyService.EXTRA_STACK, data.getParcelableExtra(CopyService.EXTRA_STACK));
-
- Toast.makeText(context,
- res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()),
- Toast.LENGTH_SHORT).show();
- context.startService(copyIntent);
+ CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
+ (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK));
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 1748c9c..00b0f78 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.DialogInterface;
@@ -27,7 +28,9 @@
import android.os.Bundle;
import android.text.Html;
+import com.android.documentsui.CopyService;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -40,9 +43,10 @@
private static final String TAG = "FailureDialogFragment";
private int mFailure;
- private ArrayList<Uri> mFailedSrcList;
+ private ArrayList<DocumentInfo> mFailedSrcList;
- public static void show(FragmentManager fm, int failure, ArrayList<Uri> failedSrcList) {
+ public static void show(FragmentManager fm, int failure,
+ ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack) {
// TODO: Add support for other failures than copy.
if (failure != CopyService.FAILURE_COPY) {
return;
@@ -62,7 +66,11 @@
@Override
public void onClick(DialogInterface dialog, int whichButton) {
- // TODO: Pass mFailure and mFailedSrcList to the parent fragment.
+ if (whichButton == DialogInterface.BUTTON_POSITIVE) {
+ CopyService.start(getActivity(), mFailedSrcList,
+ (DocumentStack) getActivity().getIntent().getParcelableExtra(
+ CopyService.EXTRA_STACK));
+ }
}
@Override
@@ -73,27 +81,17 @@
mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST);
final StringBuilder list = new StringBuilder("<p>");
- for (Uri documentUri : mFailedSrcList) {
- try {
- final DocumentInfo documentInfo = DocumentInfo.fromUri(
- getActivity().getContentResolver(), documentUri);
- list.append(String.format("• %s<br>", documentInfo.displayName));
- }
- catch (FileNotFoundException ignore) {
- // Source file most probably gone.
- }
+ for (DocumentInfo documentInfo : mFailedSrcList) {
+ list.append(String.format("• %s<br>", documentInfo.displayName));
}
list.append("</p>");
final String message = String.format(getString(R.string.copy_failure_alert_content),
list.toString());
return new AlertDialog.Builder(getActivity())
- .setTitle(getString(R.string.copy_failure_alert_title))
.setMessage(Html.fromHtml(message))
- // TODO: Implement retrying the copy operation.
.setPositiveButton(R.string.retry, this)
.setNegativeButton(android.R.string.cancel, this)
- .setIcon(android.R.drawable.ic_dialog_alert)
.create();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index aad42ed..cad277d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -80,15 +80,6 @@
final Context context = this;
- // Strongly define our horizontal dimension; we leave vertical as
- final WindowManager.LayoutParams a = getWindow().getAttributes();
-
- final Point size = new Point();
- getWindowManager().getDefaultDisplay().getSize(size);
- // a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
-
- getWindow().setAttributes(a);
-
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
mState = (icicle != null)
@@ -115,12 +106,15 @@
RootsFragment.show(getFragmentManager(), null);
if (!mState.restored) {
new RestoreStackTask().execute();
+
+ // Show a failure dialog if there was a failed operation.
final Intent intent = getIntent();
+ final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
if (failure != 0) {
- final ArrayList<Uri> failedSrcList = intent.getParcelableArrayListExtra(
- CopyService.EXTRA_SRC_LIST);
- FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList);
+ final ArrayList<DocumentInfo> failedSrcList =
+ intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
+ FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack);
}
} else {
onCurrentDirectoryChanged(ANIM_NONE);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index aff57bf..73a723d 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -45,7 +45,6 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
@@ -55,7 +54,6 @@
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
-import java.util.Objects;
public class ExternalStorageProvider extends DocumentsProvider {
private static final String TAG = "ExternalStorage";
@@ -327,7 +325,7 @@
throw new IllegalArgumentException("Parent document isn't a directory");
}
- final File file = buildUniqueFile(parent, mimeType, displayName);
+ final File file = FileUtils.buildUniqueFile(parent, mimeType, displayName);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
if (!file.mkdir()) {
throw new IllegalStateException("Failed to mkdir " + file);
@@ -345,68 +343,6 @@
return getDocIdForFile(file);
}
- private static File buildFile(File parent, String name, String ext) {
- if (TextUtils.isEmpty(ext)) {
- return new File(parent, name);
- } else {
- return new File(parent, name + "." + ext);
- }
- }
-
- @VisibleForTesting
- public static File buildUniqueFile(File parent, String mimeType, String displayName)
- throws FileNotFoundException {
- String name;
- String ext;
-
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- name = displayName;
- ext = null;
- } else {
- String mimeTypeFromExt;
-
- // Extract requested extension from display name
- final int lastDot = displayName.lastIndexOf('.');
- if (lastDot >= 0) {
- name = displayName.substring(0, lastDot);
- ext = displayName.substring(lastDot + 1);
- mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- ext.toLowerCase());
- } else {
- name = displayName;
- ext = null;
- mimeTypeFromExt = null;
- }
-
- if (mimeTypeFromExt == null) {
- mimeTypeFromExt = "application/octet-stream";
- }
-
- final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- mimeType);
- if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
- // Extension maps back to requested MIME type; allow it
- } else {
- // No match; insist that create file matches requested MIME
- name = displayName;
- ext = extFromMimeType;
- }
- }
-
- File file = buildFile(parent, name, ext);
-
- // If conflicting file, try adding counter suffix
- int n = 0;
- while (file.exists()) {
- if (n++ >= 32) {
- throw new FileNotFoundException("Failed to create unique file");
- }
- file = buildFile(parent, name + " (" + n + ")", ext);
- }
-
- return file;
- }
-
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
diff --git a/packages/ExternalStorageProvider/tests/Android.mk b/packages/ExternalStorageProvider/tests/Android.mk
deleted file mode 100644
index 830731a..0000000
--- a/packages/ExternalStorageProvider/tests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_PACKAGE_NAME := ExternalStorageProviderTests
-LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
deleted file mode 100644
index ffcd499..0000000
--- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.externalstorage.tests">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.externalstorage"
- android:label="Tests for ExternalStorageProvider" />
-
-</manifest>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
deleted file mode 100644
index f980b60..0000000
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.externalstorage;
-
-import static com.android.externalstorage.ExternalStorageProvider.buildUniqueFile;
-
-import android.os.FileUtils;
-import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import java.io.File;
-
-@MediumTest
-public class ExternalStorageProviderTest extends AndroidTestCase {
-
- private File mTarget;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mTarget = getContext().getFilesDir();
- FileUtils.deleteContents(mTarget);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- FileUtils.deleteContents(mTarget);
- }
-
- public void testBuildUniqueFile_normal() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- assertNameEquals("test.jpeg", buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
- assertNameEquals("TEst.JPeg", buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png"));
-
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test.flac"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
- }
-
- public void testBuildUniqueFile_unknown() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, "application/octet-stream", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
- assertNameEquals(".test", buildUniqueFile(mTarget, "application/octet-stream", ".test"));
-
- assertNameEquals("test", buildUniqueFile(mTarget, "lolz/lolz", "test"));
- assertNameEquals("test.lolz", buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
- }
-
- public void testBuildUniqueFile_dir() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
- new File(mTarget, "test").mkdir();
- assertNameEquals("test (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
-
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- new File(mTarget, "test.jpg").mkdir();
- assertNameEquals("test.jpg (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- }
-
- public void testBuildUniqueFile_increment() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test.jpg").createNewFile();
- assertNameEquals("test (1).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test (1).jpg").createNewFile();
- assertNameEquals("test (2).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- }
-
- private static void assertNameEquals(String expected, File actual) {
- assertEquals(expected, actual.getName());
- }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 4fbcc1e..e083c9c 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -18,6 +18,7 @@
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import android.content.Context;
import android.content.Intent;
@@ -141,7 +142,11 @@
plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
- text = concatenate(plmn, spn);
+ if (Objects.equals(plmn, spn)) {
+ text = plmn;
+ } else {
+ text = concatenate(plmn, spn);
+ }
}
displayText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.keyguard_missing_sim_message_short), text);
@@ -293,11 +298,7 @@
final boolean plmnValid = !TextUtils.isEmpty(plmn);
final boolean spnValid = !TextUtils.isEmpty(spn);
if (plmnValid && spnValid) {
- if (plmn.equals(spn)) {
- return plmn;
- } else {
- return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
- }
+ return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
} else if (plmnValid) {
return plmn;
} else if (spnValid) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
new file mode 100644
index 0000000..e0af29d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2011 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.deviceinfo;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.util.Log;
+import android.util.SparseLongArray;
+
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
+import com.google.android.collect.Sets;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility for measuring the disk usage of internal storage or a physical
+ * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
+ * and delivers results to {@link MeasurementReceiver}.
+ */
+public class StorageMeasurement {
+ private static final String TAG = "StorageMeasurement";
+
+ private static final boolean LOCAL_LOGV = true;
+ static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
+
+ public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
+
+ /** Media types to measure on external storage. */
+ private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
+ Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
+ Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
+ Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
+
+ public static class MeasurementDetails {
+ /**
+ * Total apps disk usage.
+ * <p>
+ * When measuring internal storage, this value includes the code size of
+ * all apps (regardless of install status for current user), and
+ * internal disk used by the current user's apps. When the device
+ * emulates external storage, this value also includes emulated storage
+ * used by the current user's apps.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this value includes
+ * usage by all apps on that volume.
+ */
+ public long appsSize;
+
+ /**
+ * Total cache disk usage by apps.
+ */
+ public long cacheSize;
+
+ /**
+ * Total media disk usage, categorized by types such as
+ * {@link Environment#DIRECTORY_MUSIC}.
+ * <p>
+ * When measuring internal storage, this reflects media on emulated
+ * storage for the current user.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this reflects media
+ * on that volume.
+ */
+ public HashMap<String, Long> mediaSize = new HashMap<>();
+
+ /**
+ * Misc external disk usage for the current user, unaccounted in
+ * {@link #mediaSize}.
+ */
+ public long miscSize;
+
+ /**
+ * Total disk usage for users, which is only meaningful for emulated
+ * internal storage. Key is {@link UserHandle}.
+ */
+ public SparseLongArray usersSize = new SparseLongArray();
+ }
+
+ public interface MeasurementReceiver {
+ public void onDetailsChanged(MeasurementDetails details);
+ }
+
+ private WeakReference<MeasurementReceiver> mReceiver;
+
+ private final Context mContext;
+
+ private final VolumeInfo mVolume;
+ private final VolumeInfo mSharedVolume;
+
+ private final MainHandler mMainHandler;
+ private final MeasurementHandler mMeasurementHandler;
+
+ public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
+ mContext = context.getApplicationContext();
+
+ mVolume = volume;
+ mSharedVolume = sharedVolume;
+
+ // Start the thread that will measure the disk usage.
+ final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
+ handlerThread.start();
+
+ mMainHandler = new MainHandler();
+ mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
+ }
+
+ public void setReceiver(MeasurementReceiver receiver) {
+ if (mReceiver == null || mReceiver.get() == null) {
+ mReceiver = new WeakReference<MeasurementReceiver>(receiver);
+ }
+ }
+
+ public void forceMeasure() {
+ invalidate();
+ measure();
+ }
+
+ public void measure() {
+ if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
+ }
+ }
+
+ public void onDestroy() {
+ mReceiver = null;
+ mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
+ }
+
+ private void invalidate() {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
+ }
+
+ private static class StatsObserver extends IPackageStatsObserver.Stub {
+ private final boolean mIsPrivate;
+ private final MeasurementDetails mDetails;
+ private final int mCurrentUser;
+ private final Message mFinished;
+
+ private int mRemaining;
+
+ public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
+ Message finished, int remaining) {
+ mIsPrivate = isPrivate;
+ mDetails = details;
+ mCurrentUser = currentUser;
+ mFinished = finished;
+ mRemaining = remaining;
+ }
+
+ @Override
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ synchronized (mDetails) {
+ if (succeeded) {
+ addStatsLocked(stats);
+ }
+ if (--mRemaining == 0) {
+ mFinished.sendToTarget();
+ }
+ }
+ }
+
+ private void addStatsLocked(PackageStats stats) {
+ if (mIsPrivate) {
+ long codeSize = stats.codeSize;
+ long dataSize = stats.dataSize;
+ long cacheSize = stats.cacheSize;
+ if (Environment.isExternalStorageEmulated()) {
+ // Include emulated storage when measuring internal. OBB is
+ // shared on emulated storage, so treat as code.
+ codeSize += stats.externalCodeSize + stats.externalObbSize;
+ dataSize += stats.externalDataSize + stats.externalMediaSize;
+ cacheSize += stats.externalCacheSize;
+ }
+
+ // Count code and data for current user
+ if (stats.userHandle == mCurrentUser) {
+ mDetails.appsSize += codeSize;
+ mDetails.appsSize += dataSize;
+ }
+
+ // User summary only includes data (code is only counted once
+ // for the current user)
+ addValue(mDetails.usersSize, stats.userHandle, dataSize);
+
+ // Include cache for all users
+ mDetails.cacheSize += cacheSize;
+
+ } else {
+ // Physical storage; only count external sizes
+ mDetails.appsSize += stats.externalCodeSize + stats.externalDataSize
+ + stats.externalMediaSize + stats.externalObbSize;
+ mDetails.cacheSize += stats.externalCacheSize;
+ }
+ }
+ }
+
+ private class MainHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
+ if (receiver != null) {
+ receiver.onDetailsChanged(details);
+ }
+ }
+ }
+
+ private class MeasurementHandler extends Handler {
+ public static final int MSG_MEASURE = 1;
+ public static final int MSG_CONNECTED = 2;
+ public static final int MSG_DISCONNECT = 3;
+ public static final int MSG_COMPLETED = 4;
+ public static final int MSG_INVALIDATE = 5;
+
+ private Object mLock = new Object();
+
+ private IMediaContainerService mDefaultContainer;
+
+ private volatile boolean mBound = false;
+
+ private MeasurementDetails mCached;
+
+ private final ServiceConnection mDefContainerConn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
+ service);
+ mDefaultContainer = imcs;
+ mBound = true;
+ sendMessage(obtainMessage(MSG_CONNECTED, imcs));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+ removeMessages(MSG_CONNECTED);
+ }
+ };
+
+ public MeasurementHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MEASURE: {
+ if (mCached != null) {
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+
+ synchronized (mLock) {
+ if (mBound) {
+ removeMessages(MSG_DISCONNECT);
+ sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
+ } else {
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ mContext.bindServiceAsUser(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+ }
+ break;
+ }
+ case MSG_CONNECTED: {
+ final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
+ measureExactStorage(imcs);
+ break;
+ }
+ case MSG_DISCONNECT: {
+ synchronized (mLock) {
+ if (mBound) {
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+ break;
+ }
+ case MSG_COMPLETED: {
+ mCached = (MeasurementDetails) msg.obj;
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+ case MSG_INVALIDATE: {
+ mCached = null;
+ break;
+ }
+ }
+ }
+ }
+
+ private void measureExactStorage(IMediaContainerService imcs) {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ final PackageManager packageManager = mContext.getPackageManager();
+
+ final List<UserInfo> users = userManager.getUsers();
+ final int currentUser = ActivityManager.getCurrentUser();
+
+ final MeasurementDetails details = new MeasurementDetails();
+ final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
+ details);
+
+ if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
+ final File basePath = mSharedVolume.getPathForUser(currentUser);
+
+ // Measure media types for emulated storage, or for primary physical
+ // external volume
+ for (String type : sMeasureMediaTypes) {
+ final File path = new File(basePath, type);
+ final long size = getDirectorySize(imcs, path);
+ details.mediaSize.put(type, size);
+ }
+
+ // Measure misc files not counted under media
+ details.miscSize = measureMisc(imcs, basePath);
+
+ if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
+ // Measure total emulated storage of all users; internal apps data
+ // will be spliced in later
+ for (UserInfo user : users) {
+ final File userPath = mSharedVolume.getPathForUser(user.id);
+ final long size = getDirectorySize(imcs, userPath);
+ addValue(details.usersSize, user.id, size);
+ }
+ }
+ }
+
+ // Measure all apps hosted on this volume for all users
+ if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
+ final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+
+ final List<ApplicationInfo> volumeApps = new ArrayList<>();
+ for (ApplicationInfo app : apps) {
+ if (Objects.equals(app.volumeUuid, mVolume.getFsUuid())) {
+ volumeApps.add(app);
+ }
+ }
+
+ final int count = users.size() * volumeApps.size();
+ if (count == 0) {
+ finished.sendToTarget();
+ return;
+ }
+
+ final StatsObserver observer = new StatsObserver(
+ true, details, currentUser, finished, count);
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : volumeApps) {
+ packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+ }
+ }
+
+ } else {
+ finished.sendToTarget();
+ return;
+ }
+ }
+
+ private static long getDirectorySize(IMediaContainerService imcs, File path) {
+ try {
+ final long size = imcs.calculateDirectorySize(path.toString());
+ Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
+ return size;
+ } catch (Exception e) {
+ Log.w(TAG, "Could not read memory from default container service for " + path, e);
+ return 0;
+ }
+ }
+
+ private long measureMisc(IMediaContainerService imcs, File dir) {
+ final File[] files = dir.listFiles();
+ if (ArrayUtils.isEmpty(files)) return 0;
+
+ // Get sizes of all top level nodes except the ones already computed
+ long miscSize = 0;
+ for (File file : files) {
+ final String name = file.getName();
+ if (sMeasureMediaTypes.contains(name)) {
+ continue;
+ }
+
+ if (file.isFile()) {
+ miscSize += file.length();
+ } else if (file.isDirectory()) {
+ miscSize += getDirectorySize(imcs, file);
+ }
+ }
+ return miscSize;
+ }
+
+ private static void addValue(SparseLongArray array, int key, long value) {
+ array.put(key, array.get(key) + value);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 0385d1e..8d99a64 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -387,6 +387,7 @@
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write settings, restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -408,10 +409,9 @@
parser.setInput(in, null);
parseStateLocked(parser);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing settings file: "
- + mStatePersistFile , t);
+ + mStatePersistFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
new file mode 100644
index 0000000..ae0a362
--- /dev/null
+++ b/packages/SystemUI/README.md
@@ -0,0 +1,5 @@
+# SystemUI Documentation
+
+---
+
+ * [Demo Mode](/packages/SystemUI/docs/demo_mode.md)
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
new file mode 100644
index 0000000..18ae4cb
--- /dev/null
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -0,0 +1,169 @@
+# Demo Mode for the Android System UI
+*Demo mode for the status bar allows you to force the status bar into a fixed state, useful for taking screenshots with a consistent status bar state, or testing different status icon permutations. Demo mode is available in recent versions of Android.*
+
+## Enabling demo mode
+Demo mode is protected behind a system setting. To enable it for a device, run:
+
+```
+adb shell settings put global sysui_demo_allowed 1
+```
+
+## Protocol
+The protocol is based on broadcast intents, and thus can be driven via the command line (```adb shell am broadcast```) or an app (```Context.sendBroadcast```).
+
+### Broadcast action
+```
+com.android.systemui.demo
+```
+
+### Commands
+Commands and subcommands (below) are sent as string extras in the broadcast
+intent.
+<br/>
+Commands are sent as string extras with key ```command``` (required). Possible values are:
+
+Command | Subcommand | Argument | Description
+--- | --- | --- | ---
+```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
+```exit``` | | | Exits demo mode, bars back to their system-driven state
+```battery``` | | | Control the battery display
+ | ```level``` | | Sets the battery level (0 - 100)
+ | ```plugged``` | | Sets charging state (```true```, ```false```)
+```network``` | | | Control the RSSI display
+ | ```airplane``` | | ```show``` to show icon, any other value to hide
+ | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
+ | ```wifi``` | | ```show``` to show icon, any other value to hide
+ | | ```level``` | Sets wifi level (null or 0-4)
+ | ```mobile``` | | ```show``` to show icon, any other value to hide
+ | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
+ | | ```level``` | Sets mobile signal strength level (null or 0-4)
+ | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide)
+```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
+ | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
+```status``` | | | Control the system status icons
+ | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
+ | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
+ | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
+ | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
+ | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
+ | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
+ | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
+ | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
+ | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
+```notifications``` | | | Control the notification icons
+ | ```visible``` | | ```false``` to hide the notification icons, any other value to show
+```clock``` | | | Control the clock display
+ | ```millis``` | | Sets the time in millis
+ | ```hhmm``` | | Sets the time in hh:mm
+
+## Examples
+Enter demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command enter
+```
+
+
+Exit demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command exit
+```
+
+
+Set the clock to 12:31
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm
+1231
+```
+
+
+Set the wifi level to max
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi
+show -e level 4
+```
+
+
+Show the silent volume icon
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command status -e volume
+silent
+```
+
+
+Empty battery, and not charging (red exclamation point)
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command battery -e level
+0 -e plugged false
+```
+
+
+Hide the notification icons
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command notifications -e
+visible false
+```
+
+
+Exit demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command exit
+```
+
+
+## Example demo controller app in AOSP
+```
+frameworks/base/tests/SystemUIDemoModeController
+```
+
+
+## Example script (for screenshotting purposes)
+```bash
+#!/bin/sh
+CMD=$1
+
+if [[ $ADB == "" ]]; then
+ ADB=adb
+fi
+
+if [[ $CMD != "on" && $CMD != "off" ]]; then
+ echo "Usage: $0 [on|off] [hhmm]" >&2
+ exit
+fi
+
+if [[ "$2" != "" ]]; then
+ HHMM="$2"
+fi
+
+$ADB root || exit
+$ADB wait-for-devices
+$ADB shell settings put global sysui_demo_allowed 1
+
+if [ $CMD == "on" ]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command enter || exit
+ if [[ "$HHMM" != "" ]]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command clock -e
+hhmm ${HHMM}
+ fi
+ $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e
+plugged false
+ $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e
+level 100
+ $ADB shell am broadcast -a com.android.systemui.demo -e command network -e
+wifi show -e level 4
+ $ADB shell am broadcast -a com.android.systemui.demo -e command network -e
+mobile show -e datatype none -e level 4
+ $ADB shell am broadcast -a com.android.systemui.demo -e command notifications
+-e visible false
+elif [ $CMD == "off" ]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command exit
+fi
+```
+
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ccbd0a6..6e59029 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -570,4 +570,7 @@
<!-- Padding to be used on the bottom of the fingerprint icon on Keyguard so it better aligns
with the other icons. -->
<dimen name="fingerprint_icon_additional_padding">12dp</dimen>
+
+ <!-- Minimum margin of the notification panel on the side, when being positioned dynamically -->
+ <dimen name="notification_panel_min_side_margin">48dp</dimen>
</resources>
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 03e5746..7d61099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -187,6 +187,9 @@
private boolean mExpansionIsFromHeadsUp;
private int mBottomBarHeight;
private boolean mExpandingFromHeadsUp;
+ private int mPositionMinSideMargin;
+ private int mLastOrientation = -1;
+
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
public void run() {
@@ -236,6 +239,7 @@
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
mSecureCameraLaunchManager =
new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea);
+ mLastOrientation = getResources().getConfiguration().orientation;
// recompute internal state when qspanel height changes
mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@@ -268,6 +272,8 @@
getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
mQsFalsingThreshold = getResources().getDimensionPixelSize(
R.dimen.qs_falsing_threshold);
+ mPositionMinSideMargin = getResources().getDimensionPixelSize(
+ R.dimen.notification_panel_min_side_margin);
}
public void updateResources() {
@@ -692,6 +698,9 @@
if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
return true;
}
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+ updateVerticalPanelPosition(event.getX());
+ }
super.onTouchEvent(event);
return true;
}
@@ -740,7 +749,7 @@
}
private boolean isInQsArea(float x, float y) {
- return (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) &&
+ return (x >= mScrollView.getX() && x <= mScrollView.getX() + mScrollView.getWidth()) &&
(y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -939,7 +948,7 @@
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
}
-
+ resetVerticalPanelPosition();
updateQsState();
}
@@ -1376,7 +1385,7 @@
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
- boolean onHeader = x >= header.getLeft() && x <= header.getRight()
+ boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y);
@@ -1771,6 +1780,10 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mAfforanceHelper.onConfigurationChanged();
+ if (newConfig.orientation != mLastOrientation) {
+ resetVerticalPanelPosition();
+ }
+ mLastOrientation = newConfig.orientation;
}
@Override
@@ -2185,4 +2198,42 @@
mExpandingFromHeadsUp = true;
}
}
+
+ @Override
+ protected void onClosingFinished() {
+ super.onClosingFinished();
+ resetVerticalPanelPosition();
+ }
+
+ /**
+ * Updates the vertical position of the panel so it is positioned closer to the touch
+ * responsible for opening the panel.
+ *
+ * @param x the x-coordinate the touch event
+ */
+ private void updateVerticalPanelPosition(float x) {
+ if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
+ resetVerticalPanelPosition();
+ return;
+ }
+ float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+ float rightMost = getWidth() - mPositionMinSideMargin
+ - mNotificationStackScroller.getWidth() / 2;
+ if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+ x = getWidth() / 2;
+ }
+ x = Math.min(rightMost, Math.max(leftMost, x));
+ setVerticalPanelTranslation(x -
+ (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth()/2));
+ }
+
+ private void resetVerticalPanelPosition() {
+ setVerticalPanelTranslation(0f);
+ }
+
+ private void setVerticalPanelTranslation(float translation) {
+ mNotificationStackScroller.setTranslationX(translation);
+ mScrollView.setTranslationX(translation);
+ mHeader.setTranslationX(translation);
+ }
}
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 b6dbfce..c854d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3249,6 +3249,10 @@
mKeyguardFadingAway = false;
}
+ public void stopWaitingForKeyguardExit() {
+ mWaitingForKeyguardExit = false;
+ }
+
private void updatePublicMode() {
setLockscreenPublicMode(
mStatusBarKeyguardViewManager.isShowing() && mStatusBarKeyguardViewManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 194a19a..0caf51a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -145,6 +145,7 @@
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
+ mPhoneStatusBar.stopWaitingForKeyguardExit();
mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard();
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 5d02576..0e3867d 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -566,13 +566,22 @@
}
void restorePendingWhileIdleAlarmsLocked() {
+ // Bring pending alarms back into the main list.
final long nowElapsed = SystemClock.elapsedRealtime();
- for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil != null; i --) {
+ for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil == null; i--) {
Alarm a = mPendingWhileIdleAlarms.remove(i);
reAddAlarmLocked(a, nowElapsed, false);
}
+
+ // Reschedule everything.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
+
+ // And send a TIME_TICK right now, since it is important to get the UI updated.
+ try {
+ mTimeTickSender.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
}
static final class InFlight extends Intent {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a31a1a7..5df74c5 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -220,6 +220,35 @@
setString("migrated_biometric_weak", "true", 0);
Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
}
+
+ // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
+ // user was present on the system, so if we're upgrading to M and there is more than one
+ // user we disable the flag to remain consistent.
+ if (getString("migrated_lockscreen_disabled", null, 0) == null) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+
+ final List<UserInfo> users = um.getUsers();
+ final int userCount = users.size();
+ int switchableUsers = 0;
+ for (int i = 0; i < userCount; i++) {
+ if (users.get(i).supportsSwitchTo()) {
+ switchableUsers++;
+ }
+ }
+
+ if (switchableUsers > 1) {
+ for (int i = 0; i < userCount; i++) {
+ int id = users.get(i).id;
+
+ if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
+ setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+ }
+ }
+ }
+
+ setString("migrated_lockscreen_disabled", "true", 0);
+ Slog.i(TAG, "Migrated lockscreen disabled flag");
+ }
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d5ae8e..a48a4d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -437,6 +437,11 @@
*/
SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+ /**
+ * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+ */
+ String mDeviceOwnerName;
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -4831,6 +4836,9 @@
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
+ if (packageName != null && packageName.equals(mDeviceOwnerName)) {
+ throw new SecurityException("Clearing DeviceOwner data is forbidden.");
+ }
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
userId = handleIncomingUser(pid, uid,
@@ -8563,6 +8571,17 @@
}
@Override
+ public void updateDeviceOwner(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("updateDeviceOwner called from non-system process");
+ }
+ synchronized (this) {
+ mDeviceOwnerName = packageName;
+ }
+ }
+
+ @Override
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 905adc0..58665d7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -982,9 +982,18 @@
if (noOutput) {
return;
}
- if (BatteryStatsHelper.checkWifiOnly(mContext)) {
- flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (BatteryStatsHelper.checkWifiOnly(mContext)) {
+ flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
+ }
+ // Fetch data from external sources and update the BatteryStatsImpl object with them.
+ updateExternalStats("dump");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
+
if (reqUid >= 0) {
// By default, if the caller is only interested in a specific package, then
// we only dump the aggregated data since charged.
@@ -995,9 +1004,6 @@
}
}
- // Fetch data from external sources and update the BatteryStatsImpl object with them.
- updateExternalStats("dump");
-
if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
if (isRealCheckin) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c4f410f..5ac027d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -905,10 +905,14 @@
@ServiceThreadOnly
private void updateArcFeatureStatus(int portId, boolean isConnected) {
assertRunOnServiceThread();
+ HdmiDeviceInfo avr = getAvrDeviceInfo();
+ if (avr == null) {
+ return;
+ }
// HEAC 2.4, HEACT 5-15
// Should not activate ARC if +5V status is false.
HdmiPortInfo portInfo = mService.getPortInfo(portId);
- if (portInfo.isArcSupported()) {
+ if (avr.getPortId() == portId && portInfo.isArcSupported()) {
changeArcFeatureEnabled(portId, isConnected);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4c36fa6..a42e4e7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -199,7 +199,8 @@
@Nullable
private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
throws IOException {
- if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) {
+ if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
+ || pkg.applicationInfo.isExternalAsec()) {
return null;
}
File codePath = new File(pkg.codePath);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 24cc909..1c339f5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11067,7 +11067,7 @@
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
boolean replace = false;
- final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
+ int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
@@ -11234,13 +11234,18 @@
return;
}
- // Run dexopt before old package gets removed, to minimize time when app is not available
- int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
- false /* defer */, false /* inclDependencies */);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
- return;
+ // If app directory is not writable, dexopt will be called after the rename
+ if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+ // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
+ scanFlags |= SCAN_NO_DEX;
+ // Run dexopt before old package gets removed, to minimize time when app is unavailable
+ int result = mPackageDexOptimizer
+ .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+ false /* defer */, false /* inclDependencies */);
+ if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
+ res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
+ return;
+ }
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -11250,13 +11255,12 @@
startIntentFilterVerifications(args.user.getIdentifier(), pkg);
- // Call with SCAN_NO_DEX, since dexopt has already been made
if (replace) {
- replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
+ replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES
- | SCAN_NO_DEX, args.user, installerPackageName, volumeUuid, res);
+ installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+ args.user, installerPackageName, volumeUuid, res);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b3aa966..252c16a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4275,6 +4275,8 @@
Slog.wtf(PackageManagerService.TAG,
"Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write runtime permissions,"
+ + " restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -4322,10 +4324,9 @@
parser.setInput(in, null);
parseRuntimePermissionsLPr(parser, userId);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile , t);
+ + permissionsFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4c06cbe..9033c9c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -501,12 +501,15 @@
return a;
}
- private Animation createClipRevealAnimationLocked(int transit, boolean enter,
- int appWidth, int appHeight) {
+ private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
final Animation anim;
if (enter) {
// Reveal will expand and move faster in horizontal direction
+ // Start from upper left of start and move to final position
+ final int appWidth = appFrame.width();
+ final int appHeight = appFrame.height();
+
// Start from size of launch icon, expand to full width/height
Animation clipAnimLR = new ClipRectLRAnimation(
(appWidth - mNextAppTransitionStartWidth) / 2,
@@ -521,9 +524,9 @@
// Start from middle of launch icon area, move to 0, 0
int startMiddleX = mNextAppTransitionStartX +
- (mNextAppTransitionStartWidth - appWidth) / 2;
+ (mNextAppTransitionStartWidth - appWidth) / 2 - appFrame.left;
int startMiddleY = mNextAppTransitionStartY +
- (mNextAppTransitionStartHeight - appHeight) / 2;
+ (mNextAppTransitionStartHeight - appHeight) / 2 - appFrame.top;
TranslateXAnimation translateX = new TranslateXAnimation(
Animation.ABSOLUTE, startMiddleX, Animation.ABSOLUTE, 0);
@@ -940,7 +943,7 @@
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
- boolean isFullScreen, boolean isVoiceInteraction) {
+ Rect appFrame, boolean isFullScreen, boolean isVoiceInteraction) {
Animation a;
if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
@@ -977,9 +980,7 @@
+ " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
+ " transit=" + transit + " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = createClipRevealAnimationLocked(transit, enter,
- containingFrame.right - containingFrame.left,
- containingFrame.bottom - containingFrame.top);
+ a = createClipRevealAnimationLocked(transit, enter, appFrame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f29d524..38fc959 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -624,6 +624,12 @@
static final int WALLPAPER_DRAW_TIMEOUT = 2;
int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+ // Set to the wallpaper window we would like to hide once the transition animations are done.
+ // This is useful in cases where we don't want the wallpaper to be hidden when the close app
+ // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
+ // target and isn't done animating in.
+ WindowState mDeferredHideWallpaper = null;
+
AppWindowToken mFocusedApp = null;
PowerManager mPowerManager;
@@ -1781,17 +1787,24 @@
}
void hideWallpapersLocked(final WindowState winGoingAway) {
- if (mWallpaperTarget != null &&
- (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+ if (mWallpaperTarget != null
+ && (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+ return;
+ }
+ if (mAppTransition.isRunning()) {
+ // Defer hiding the wallpaper when app transition is running until the animations
+ // are done.
+ mDeferredHideWallpaper = winGoingAway;
return;
}
+ final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WindowToken token = mWallpaperTokens.get(i);
for (int j = token.windows.size() - 1; j >= 0; j--) {
final WindowState wallpaper = token.windows.get(j);
final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- if (!winAnimator.mLastHidden) {
+ if (!winAnimator.mLastHidden || wasDeferred) {
winAnimator.hide();
dispatchWallpaperVisibility(wallpaper, false);
final DisplayContent displayContent = wallpaper.getDisplayContent();
@@ -2270,12 +2283,15 @@
}
/**
- * Check wallpaper for visiblity change and notify window if so.
+ * Check wallpaper for visibility change and notify window if so.
* @param wallpaper The wallpaper to test and notify.
* @param visible Current visibility.
*/
void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
- if (wallpaper.mWallpaperVisible != visible) {
+ // Only send notification if the visibility actually changed and we are not trying to hide
+ // the wallpaper when we are deferring hiding of the wallpaper.
+ if (wallpaper.mWallpaperVisible != visible
+ && (mDeferredHideWallpaper == null || visible)) {
wallpaper.mWallpaperVisible = visible;
try {
if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
@@ -3412,6 +3428,7 @@
WindowState win = atoken.findMainWindow();
Rect containingFrame = new Rect(0, 0, width, height);
Rect contentInsets = new Rect();
+ Rect appFrame = new Rect(0, 0, width, height);
boolean isFullScreen = true;
if (win != null) {
if (win.mContainingFrame != null) {
@@ -3420,6 +3437,9 @@
if (win.mContentInsets != null) {
contentInsets.set(win.mContentInsets);
}
+ if (win.mFrame != null) {
+ appFrame.set(win.mFrame);
+ }
isFullScreen =
((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ==
SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ||
@@ -3433,8 +3453,8 @@
enter = false;
}
Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
- mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
- isVoiceInteraction);
+ mCurConfiguration.orientation, containingFrame, contentInsets, appFrame,
+ isFullScreen, isVoiceInteraction);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -9523,6 +9543,12 @@
int changes = 0;
mAppTransition.setIdle();
+
+ if (mDeferredHideWallpaper != null) {
+ hideWallpapersLocked(mDeferredHideWallpaper);
+ mDeferredHideWallpaper = null;
+ }
+
// Restore window app tokens to the ActivityManager views
ArrayList<TaskStack> stacks = getDefaultDisplayContentLocked().getStacks();
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 16d27fa..cd9689e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1635,6 +1635,10 @@
pw.println();
pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
+ if (mWinAnimator.mHasClipRect) {
+ pw.print(" mLastClipRect=");
+ mWinAnimator.mLastClipRect.printShortString(pw);
+ }
pw.println();
}
if (mEnforceSizeCompat) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1a30cba..d6726c1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1139,8 +1139,6 @@
mShownAlpha *= appTransformation.getAlpha();
if (appTransformation.hasClipRect()) {
mClipRect.set(appTransformation.getClipRect());
- // Account for non-fullscreen windows
- mClipRect.offset(frame.left, frame.top);
if (mWin.mHScale > 0) {
mClipRect.left /= mWin.mHScale;
mClipRect.right /= mWin.mHScale;
@@ -1223,7 +1221,7 @@
}
}
- void applyDecorRect(final Rect decorRect) {
+ private void applyDecorRect(final Rect decorRect) {
final WindowState w = mWin;
final int width = w.mFrame.width();
final int height = w.mFrame.height();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dc7fad6..eb9234a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -22,6 +22,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
+import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
import android.app.Activity;
@@ -45,6 +46,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -135,6 +137,7 @@
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -302,7 +305,7 @@
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
// This is the list of component allowed to start lock task mode.
- final List<String> mLockTaskPackages = new ArrayList<>();
+ List<String> mLockTaskPackages = new ArrayList<>();
boolean mStatusBarEnabledState = true;
@@ -1103,6 +1106,7 @@
void loadDeviceOwner() {
synchronized (this) {
mDeviceOwner = DeviceOwner.load();
+ updateDeviceOwnerLocked();
}
}
@@ -1626,36 +1630,54 @@
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
// never normally happen.
- LockPatternUtils utils = new LockPatternUtils(mContext);
- if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
- Slog.w(LOG_TAG, "Active password quality 0x"
- + Integer.toHexString(policy.mActivePasswordQuality)
- + " does not match actual quality 0x"
- + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
- policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- policy.mActivePasswordLength = 0;
- policy.mActivePasswordUpperCase = 0;
- policy.mActivePasswordLowerCase = 0;
- policy.mActivePasswordLetters = 0;
- policy.mActivePasswordNumeric = 0;
- policy.mActivePasswordSymbols = 0;
- policy.mActivePasswordNonLetter = 0;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
+ Slog.w(LOG_TAG, "Active password quality 0x"
+ + Integer.toHexString(policy.mActivePasswordQuality)
+ + " does not match actual quality 0x"
+ + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
+ policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ policy.mActivePasswordLength = 0;
+ policy.mActivePasswordUpperCase = 0;
+ policy.mActivePasswordLowerCase = 0;
+ policy.mActivePasswordLetters = 0;
+ policy.mActivePasswordNumeric = 0;
+ policy.mActivePasswordSymbols = 0;
+ policy.mActivePasswordNonLetter = 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
validatePasswordOwnerLocked(policy);
syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
- updateLockTaskPackagesLocked(policy, userHandle);
+ addDeviceInitializerToLockTaskPackagesLocked(userHandle);
+ updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (!policy.mStatusBarEnabledState) {
setStatusBarEnabledStateInternal(policy.mStatusBarEnabledState, userHandle);
}
}
- private void updateLockTaskPackagesLocked(DevicePolicyData policy, int userId) {
+ private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
IActivityManager am = ActivityManagerNative.getDefault();
long ident = Binder.clearCallingIdentity();
try {
- am.updateLockTaskPackages(userId, policy.mLockTaskPackages.toArray(new String[0]));
+ am.updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateDeviceOwnerLocked() {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ am.updateDeviceOwner(getDeviceOwner());
} catch (RemoteException e) {
// Not gonna happen.
} finally {
@@ -3986,14 +4008,13 @@
if (mDeviceOwner == null) {
// Device owner is not set and does not exist, set it.
mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
} else {
// Device owner is not set but a profile owner exists, update Device owner state.
mDeviceOwner.setDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+ mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
+ return true;
}
}
@@ -4075,6 +4096,7 @@
if (mDeviceOwner != null) {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4103,16 +4125,15 @@
if (mDeviceOwner == null) {
// Device owner state does not exist, create it.
- mDeviceOwner = DeviceOwner.createWithDeviceInitializer(
- initializer, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
+ mDeviceOwner = DeviceOwner.createWithDeviceInitializer(initializer, ownerName);
} else {
// Device owner already exists, update it.
mDeviceOwner.setDeviceInitializer(initializer, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+
+ addDeviceInitializerToLockTaskPackagesLocked(UserHandle.USER_OWNER);
+ mDeviceOwner.writeOwnerFile();
+ return true;
}
}
@@ -5093,20 +5114,21 @@
final IPackageManager ipm = AppGlobals.getPackageManager();
IActivityManager activityManager = ActivityManagerNative.getDefault();
+ final int userHandle = user.getIdentifier();
try {
// Install the profile owner if not present.
- if (!ipm.isPackageAvailable(profileOwnerPkg, user.getIdentifier())) {
- ipm.installExistingPackageAsUser(profileOwnerPkg, user.getIdentifier());
+ if (!ipm.isPackageAvailable(profileOwnerPkg, userHandle)) {
+ ipm.installExistingPackageAsUser(profileOwnerPkg, userHandle);
}
// Start user in background.
- activityManager.startUserInBackground(user.getIdentifier());
+ activityManager.startUserInBackground(userHandle);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
}
- setActiveAdmin(profileOwnerComponent, true, user.getIdentifier(), adminExtras);
- setProfileOwner(profileOwnerComponent, ownerName, user.getIdentifier());
+ setActiveAdmin(profileOwnerComponent, true, userHandle, adminExtras);
+ setProfileOwner(profileOwnerComponent, ownerName, userHandle);
return user;
} finally {
restoreCallingIdentity(id);
@@ -5651,29 +5673,26 @@
* This function can only be called by the device owner.
* @param packages The list of packages allowed to enter lock task mode.
*/
- public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException {
+ public void setLockTaskPackages(ComponentName who, String[] packages)
+ throws SecurityException {
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
int userHandle = Binder.getCallingUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- policy.mLockTaskPackages.clear();
- if (packages != null) {
- for (int j = 0; j < packages.length; j++) {
- String pkg = packages[j];
- if (pkg != null) {
- policy.mLockTaskPackages.add(pkg);
- }
- }
- }
-
- // Store the settings persistently.
- saveSettingsLocked(userHandle);
- updateLockTaskPackagesLocked(policy, userHandle);
+ setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
}
}
+ private void setLockTaskPackagesLocked(int userHandle, List<String> packages) {
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskPackages = packages;
+
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ updateLockTaskPackagesLocked(packages, userHandle);
+ }
+
/**
* This function returns the list of components allowed to start the task lock mode.
*/
@@ -5681,13 +5700,17 @@
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
int userHandle = Binder.getCallingUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- return policy.mLockTaskPackages.toArray(new String[policy.mLockTaskPackages.size()]);
+ final List<String> packages = getLockTaskPackagesLocked(userHandle);
+ return packages.toArray(new String[packages.size()]);
}
}
+ private List<String> getLockTaskPackagesLocked(int userHandle) {
+ final DevicePolicyData policy = getUserData(userHandle);
+ return policy.mLockTaskPackages;
+ }
+
/**
* This function lets the caller know whether the given package is allowed to start the
* lock task mode.
@@ -5951,6 +5974,8 @@
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
synchronized (this) {
+ // The DeviceInitializer was whitelisted but now should be removed.
+ removeDeviceInitializerFromLockTaskPackages(userHandle);
saveSettingsLocked(userHandle);
}
}
@@ -5958,6 +5983,35 @@
}
}
+ private void addDeviceInitializerToLockTaskPackagesLocked(int userHandle) {
+ if (hasUserSetupCompleted(userHandle)) {
+ return;
+ }
+
+ final String deviceInitializerPackage = getDeviceInitializer();
+ if (deviceInitializerPackage == null) {
+ return;
+ }
+
+ final List<String> packages = getLockTaskPackagesLocked(userHandle);
+ if (!packages.contains(deviceInitializerPackage)) {
+ packages.add(deviceInitializerPackage);
+ setLockTaskPackagesLocked(userHandle, packages);
+ }
+ }
+
+ private void removeDeviceInitializerFromLockTaskPackages(int userHandle) {
+ final String deviceInitializerPackage = getDeviceInitializer();
+ if (deviceInitializerPackage == null) {
+ return;
+ }
+
+ List<String> packages = getLockTaskPackagesLocked(userHandle);
+ if (packages.remove(deviceInitializerPackage)) {
+ setLockTaskPackagesLocked(userHandle, packages);
+ }
+ }
+
private class SetupContentObserver extends ContentObserver {
private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
@@ -6088,4 +6142,42 @@
}
return false;
}
+
+ @Override
+ public void notifyPendingSystemUpdate(long updateReceivedTime) {
+ mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE,
+ "Only the system update service can broadcast update information");
+
+ if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
+ Slog.w(LOG_TAG, "Only the system update service in the primary user" +
+ "can broadcast update information.");
+ return;
+ }
+ Intent intent = new Intent(DeviceAdminReceiver.ACTION_NOTIFY_PENDING_SYSTEM_UPDATE);
+ intent.putExtra(DeviceAdminReceiver.EXTRA_SYSTEM_UPDATE_RECEIVED_TIME,
+ updateReceivedTime);
+
+ synchronized (this) {
+ String deviceOwnerPackage = getDeviceOwner();
+ if (deviceOwnerPackage == null) {
+ return;
+ }
+
+ try {
+ ActivityInfo[] receivers = mContext.getPackageManager().getPackageInfo(
+ deviceOwnerPackage, PackageManager.GET_RECEIVERS).receivers;
+ if (receivers != null) {
+ for (int i = 0; i < receivers.length; i++) {
+ if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) {
+ intent.setComponent(new ComponentName(deviceOwnerPackage,
+ receivers[i].name));
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ }
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot find device owner package", e);
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index bbd5e30..2a30384 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -66,9 +66,18 @@
public static final int STATE_DISCONNECTED = 7;
/**
- * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ * The state of an outgoing {@code Call} when waiting on user to select a
+ * {@link PhoneAccount} through which to place the call.
*/
- public static final int STATE_PRE_DIAL_WAIT = 8;
+ public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
+
+ /**
+ * @hide
+ * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
+ */
+ @Deprecated
+ @SystemApi
+ public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
/**
* The initial state of an outgoing {@code Call}.
@@ -929,7 +938,7 @@
mVideoCall = parcelableCall.getVideoCall();
}
- int state = stateFromParcelableCallState(parcelableCall.getState());
+ int state = parcelableCall.getState();
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
@@ -1064,32 +1073,4 @@
callback.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
}
}
-
- private int stateFromParcelableCallState(int parcelableCallState) {
- switch (parcelableCallState) {
- case CallState.NEW:
- return STATE_NEW;
- case CallState.CONNECTING:
- return STATE_CONNECTING;
- case CallState.PRE_DIAL_WAIT:
- return STATE_PRE_DIAL_WAIT;
- case CallState.DIALING:
- return STATE_DIALING;
- case CallState.RINGING:
- return STATE_RINGING;
- case CallState.ACTIVE:
- return STATE_ACTIVE;
- case CallState.ON_HOLD:
- return STATE_HOLDING;
- case CallState.DISCONNECTED:
- return STATE_DISCONNECTED;
- case CallState.ABORTED:
- return STATE_DISCONNECTED;
- case CallState.DISCONNECTING:
- return STATE_DISCONNECTING;
- default:
- Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
- return STATE_NEW;
- }
- }
}
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
deleted file mode 100644
index 5584226..0000000
--- a/telecomm/java/android/telecom/CallState.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecom;
-
-/**
- * Defines call-state constants of the different states in which a call can exist. Although states
- * have the notion of normal transitions, due to the volatile nature of telephony systems, code
- * that uses these states should be resilient to unexpected state changes outside of what is
- * considered traditional.
- */
-public final class CallState {
-
- private CallState() {}
-
- /**
- * Indicates that a call is new and not connected. This is used as the default state internally
- * within Telecom and should not be used between Telecom and call services. Call services are
- * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in
- * this state.
- */
- public static final int NEW = 0;
-
- /**
- * The initial state of an outgoing {@code Call}.
- * Common transitions are to {@link #DIALING} state for a successful call or
- * {@link #DISCONNECTED} if it failed.
- */
- public static final int CONNECTING = 1;
-
- /**
- * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
- * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
- * this is the state where the InCallUI is waiting for the user to select a
- * {@link PhoneAccount} to call from.
- */
- public static final int PRE_DIAL_WAIT = 2;
-
- /**
- * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
- * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
- * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
- * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
- */
- public static final int DIALING = 3;
-
- /**
- * Indicates that a call is incoming and the user still has the option of answering, rejecting,
- * or doing nothing with the call. This state is usually associated with some type of audible
- * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
- * otherwise.
- */
- public static final int RINGING = 4;
-
- /**
- * Indicates that a call is currently connected to another party and a communication channel is
- * open between them. The normal transition to this state is by the user answering a
- * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
- */
- public static final int ACTIVE = 5;
-
- /**
- * Indicates that the call is currently on hold. In this state, the call is not terminated
- * but no communication is allowed until the call is no longer on hold. The typical transition
- * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
- * an action, such as clicking the hold button.
- */
- public static final int ON_HOLD = 6;
-
- /**
- * Indicates that a call is currently disconnected. All states can transition to this state
- * by the call service giving notice that the connection has been severed. When the user
- * explicitly ends a call, it will not transition to this state until the call service confirms
- * the disconnection or communication was lost to the call service currently responsible for
- * this call (e.g., call service crashes).
- */
- public static final int DISCONNECTED = 7;
-
- /**
- * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
- * time of writing) but cancelled before it was successfully connected.
- */
- public static final int ABORTED = 8;
-
- /**
- * Indicates that the call is in the process of being disconnected and will transition next
- * to a {@link #DISCONNECTED} state.
- * <p>
- * This state is not expected to be communicated from the Telephony layer, but will be reported
- * to the InCall UI for calls where disconnection has been initiated by the user but the
- * ConnectionService has confirmed the call as disconnected.
- */
- public static final int DISCONNECTING = 9;
-
- public static String toString(int callState) {
- switch (callState) {
- case NEW:
- return "NEW";
- case CONNECTING:
- return "CONNECTING";
- case PRE_DIAL_WAIT:
- return "PRE_DIAL_WAIT";
- case DIALING:
- return "DIALING";
- case RINGING:
- return "RINGING";
- case ACTIVE:
- return "ACTIVE";
- case ON_HOLD:
- return "ON_HOLD";
- case DISCONNECTED:
- return "DISCONNECTED";
- case ABORTED:
- return "ABORTED";
- case DISCONNECTING:
- return "DISCONNECTING";
- default:
- return "UNKNOWN";
- }
- }
-}
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
new file mode 100644
index 0000000..eef72fb
--- /dev/null
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -0,0 +1,174 @@
+/*
+ * 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 android.telecom;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the default dialer application that will receive incoming calls, and be
+ * allowed to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class DefaultDialerManager {
+ private static final String TAG = "DefaultDialerManager";
+
+ /**
+ * Sets the specified package name as the default dialer application. The caller of this method
+ * needs to have permission to write to secure settings.
+ *
+ * @hide
+ * */
+ public static void setDefaultPhoneApplication(Context context, String packageName) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ final ComponentName foundComponent = getComponentName(componentNames, packageName);
+
+ if (foundComponent != null) {
+ // Update the secure setting.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, foundComponent.getPackageName());
+ }
+ }
+
+ /**
+ * Returns the installed dialer application that will be used to receive incoming calls, and is
+ * allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * @hide
+ * */
+ public static ComponentName getDefaultDialerApplication(Context context) {
+ String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ final List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ if (!TextUtils.isEmpty(defaultPackageName)) {
+ final ComponentName defaultDialer =
+ getComponentName(componentNames, defaultPackageName);
+ if (defaultDialer != null) {
+ return defaultDialer;
+ }
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ ComponentName systemDialer = getTelecomManager(context).getDefaultPhoneApp();
+
+ if (systemDialer == null) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ // Verify that the system dialer has not been disabled.
+ return getComponentName(componentNames, systemDialer.getPackageName());
+ }
+
+ /**
+ * Returns a list of installed and available dialer applications.
+ *
+ * In order to appear in the list, a dialer application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ public static List<ComponentName> getInstalledDialerApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<ComponentName> componentNames = new ArrayList<ComponentName> ();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final ComponentName componentName =
+ new ComponentName(activityInfo.packageName, activityInfo.name);
+ componentNames.add(componentName);
+ }
+
+ // TODO: Filter for apps that don't handle DIAL intent with tel scheme
+ return componentNames;
+ }
+
+ /**
+ * Returns the {@link ComponentName} for the installed dialer application for a given package
+ * name.
+ *
+ * @param context A valid context.
+ * @param packageName to retrieve the {@link ComponentName} for.
+ *
+ * @return The {@link ComponentName} for the installed dialer application corresponding to the
+ * package name, or null if none is found.
+ *
+ * @hide
+ */
+ public static ComponentName getDialerApplicationForPackageName(Context context,
+ String packageName) {
+ return getComponentName(getInstalledDialerApplications(context), packageName);
+ }
+
+ /**
+ * Returns the component from a list of application components that corresponds to the package
+ * name.
+ *
+ * @param componentNames A list of component names
+ * @param packageName The package name to look for
+ * @return The {@link ComponentName} that matches the provided packageName, or null if not
+ * found.
+ */
+ private static ComponentName getComponentName(List<ComponentName> componentNames,
+ String packageName) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(packageName, componentName.getPackageName())) {
+ return componentName;
+ }
+ }
+ return null;
+ }
+
+ private static TelecomManager getTelecomManager(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72172c..fd95327 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -1057,6 +1058,39 @@
}
}
+ /**
+ * Places a new outgoing call to the provided address using the system telecom service with
+ * the specified extras.
+ *
+ * This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
+ * except that the outgoing call will always be sent via the system telecom service. If
+ * method-caller is either the user selected default dialer app or preloaded system dialer
+ * app, then emergency calls will also be allowed.
+ *
+ * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ *
+ * Usage example:
+ * <pre>
+ * Uri uri = Uri.fromParts("tel", "12345", null);
+ * Bundle extras = new Bundle();
+ * extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+ * telecomManager.placeCall(uri, extras);
+ * </pre>
+ *
+ * @param address The address to make the call to.
+ * @param extras Bundle of extras to use with the call.
+ */
+ public void placeCall(Uri address, Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.placeCall(address, extras, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 727fd4bb..45b2482 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -210,4 +210,9 @@
* @see TelecomServiceImpl#addNewUnknownCall
*/
void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+ /**
+ * @see TelecomServiceImpl#placeCall
+ */
+ void placeCall(in Uri handle, in Bundle extras, String callingPackage);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d103fbf..7d1a2fa 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -71,6 +71,11 @@
public static final String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
/**
+ * Control whether users can edit APNs in Settings.
+ */
+ public static final String BOOL_APN_EXPAND = "bool_apn_expand";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -121,6 +126,7 @@
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_PROVISIONED, false);
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_TTY_SUPPORTED, true);
sDefaults.putBoolean(BOOL_SHOW_APN_SETTING_CDMA, false);
+ sDefaults.putBoolean(BOOL_APN_EXPAND, true);
sDefaults.putInt(INT_VOLTE_REPLACEMENT_RAT, 0);
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index d10a7ea..9fea418 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -48,6 +48,13 @@
public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+ // Grouping of RAFs
+ private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
+ private static final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
+ private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
+ private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
+ private static final int WCDMA = HS | RAF_UMTS;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -136,12 +143,6 @@
};
public static int getRafFromNetworkType(int type) {
- final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
- final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
- final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
- final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B;
- final int WCDMA = HS | RAF_UMTS;
-
int raf;
switch (type) {
@@ -158,7 +159,7 @@
raf = GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_CDMA:
- raf = CDMA;
+ raf = CDMA | EVDO;
break;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
raf = RAF_LTE | CDMA | EVDO;
@@ -188,7 +189,71 @@
raf = RAF_UNKNOWN;
break;
}
+
return raf;
}
+
+ /**
+ * if the raf includes ANY bit set for a group
+ * adjust it to contain ALL the bits for that group
+ */
+ private static int getAdjustedRaf(int raf) {
+ raf = ((GSM & raf) > 0) ? (GSM | raf) : raf;
+ raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
+ raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
+ raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+
+ return raf;
+ }
+
+ public static int getNetworkTypeFromRaf(int raf) {
+ int type;
+
+ raf = getAdjustedRaf(raf);
+
+ switch (raf) {
+ case (GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ break;
+ case GSM:
+ type = RILConstants.NETWORK_MODE_GSM_ONLY;
+ break;
+ case WCDMA:
+ type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ break;
+ case (CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_CDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+ break;
+ case (RAF_LTE | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case RAF_LTE:
+ type = RILConstants.NETWORK_MODE_LTE_ONLY;
+ break;
+ case (RAF_LTE | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_WCDMA;
+ break;
+ case CDMA:
+ type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ break;
+ case EVDO:
+ type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ break;
+ case (GSM | WCDMA | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_GLOBAL;
+ break;
+ default:
+ type = RILConstants.PREFERRED_NETWORK_MODE ;
+ break;
+ }
+
+ return type;
+ }
}
diff --git a/tests/VoiceInteraction/res/layout/main.xml b/tests/VoiceInteraction/res/layout/main.xml
index 092d37d..34a7563 100644
--- a/tests/VoiceInteraction/res/layout/main.xml
+++ b/tests/VoiceInteraction/res/layout/main.xml
@@ -34,6 +34,12 @@
android:text="@string/asyncStructure"
/>
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:assistBlocked="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="This won't be included in assist."
+ />
+
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml
index ce5669c..789493a 100644
--- a/tests/VoiceInteraction/res/xml/interaction_service.xml
+++ b/tests/VoiceInteraction/res/xml/interaction_service.xml
@@ -20,4 +20,5 @@
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService"
android:recognitionService="com.android.test.voiceinteraction.MainRecognitionService"
- android:settingsActivity="com.android.test.voiceinteraction.SettingsActivity" />
+ android:settingsActivity="com.android.test.voiceinteraction.SettingsActivity"
+ android:supportsAssistGesture="true" />