merge in oc-release history after reset to master
diff --git a/Android.mk b/Android.mk
index 7748dfc..109f610 100644
--- a/Android.mk
+++ b/Android.mk
@@ -213,7 +213,6 @@
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/ICaptivePortal.aidl \
core/java/android/net/IConnectivityManager.aidl \
- core/java/android/net/IConnectivityMetricsLogger.aidl \
core/java/android/net/IIpConnectivityMetrics.aidl \
core/java/android/net/IEthernetManager.aidl \
core/java/android/net/IEthernetServiceListener.aidl \
@@ -409,6 +408,10 @@
location/java/android/location/INetInitiatedListener.aidl \
location/java/com/android/internal/location/ILocationProvider.aidl \
media/java/android/media/IAudioService.aidl \
+ media/java/android/media/ICas.aidl \
+ media/java/android/media/ICasListener.aidl \
+ media/java/android/media/IDescrambler.aidl \
+ media/java/android/media/IMediaCasService.aidl \
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
media/java/android/media/IMediaHTTPConnection.aidl \
diff --git a/api/current.txt b/api/current.txt
index 04cdf05..d53a4b8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -410,6 +410,7 @@
field public static final int colorControlHighlight = 16843820; // 0x101042c
field public static final int colorControlNormal = 16843817; // 0x1010429
field public static final int colorEdgeEffect = 16843982; // 0x10104ce
+ field public static final int colorError = 16844100; // 0x1010544
field public static final int colorFocusedHighlight = 16843663; // 0x101038f
field public static final int colorForeground = 16842800; // 0x1010030
field public static final int colorForegroundInverse = 16843270; // 0x1010206
@@ -1316,7 +1317,6 @@
field public static final int textCheckMarkInverse = 16842823; // 0x1010047
field public static final int textColor = 16842904; // 0x1010098
field public static final int textColorAlertDialogListItem = 16843526; // 0x1010306
- field public static final int textColorError = 16844100; // 0x1010544
field public static final int textColorHighlight = 16842905; // 0x1010099
field public static final int textColorHighlightInverse = 16843599; // 0x101034f
field public static final int textColorHint = 16842906; // 0x101009a
@@ -2722,10 +2722,10 @@
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
- method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
- method public void onInterrupt();
+ method public abstract void onInterrupt();
method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
method public final boolean performGlobalAction(int);
@@ -4737,6 +4737,7 @@
method public abstract android.app.Fragment getPrimaryNavigationFragment();
method public void invalidateOptionsMenu();
method public abstract boolean isDestroyed();
+ method public abstract boolean isStateSaved();
method public abstract void popBackStack();
method public abstract void popBackStack(java.lang.String, int);
method public abstract void popBackStack(int, int);
@@ -9040,6 +9041,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
@@ -12574,6 +12576,7 @@
method public static android.graphics.ColorSpace.Adaptation valueOf(java.lang.String);
method public static final android.graphics.ColorSpace.Adaptation[] values();
enum_constant public static final android.graphics.ColorSpace.Adaptation BRADFORD;
+ enum_constant public static final android.graphics.ColorSpace.Adaptation CIECAT02;
enum_constant public static final android.graphics.ColorSpace.Adaptation VON_KRIES;
}
@@ -20996,13 +20999,14 @@
field public long nanoTime;
}
- public class AudioTrack implements android.media.AudioRouting {
+ public class AudioTrack implements android.media.AudioRouting android.media.VolumeAutomation {
ctor public deprecated AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public deprecated 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 addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
@@ -21470,8 +21474,42 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
+ public final class MediaCas {
+ ctor public MediaCas(int) throws android.media.UnsupportedCasException;
+ method public void closeSession(byte[]);
+ method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
+ method public static boolean isSystemIdSupported(int);
+ method public byte[] openSession(int);
+ method public byte[] openSession(int, int);
+ method public void processEcm(byte[], byte[], int, int);
+ method public void processEcm(byte[], byte[]);
+ method public void processEmm(byte[], int, int);
+ method public void processEmm(byte[]);
+ method public void provision(java.lang.String);
+ method public void refreshEntitlements(int, byte[]);
+ method public void release();
+ method public void sendEvent(int, int, byte[]);
+ method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
+ method public void setPrivateData(byte[]);
+ method public void setSessionPrivateData(byte[], byte[]);
+ }
+
+ public static abstract interface MediaCas.EventListener {
+ method public abstract void onEvent(android.media.MediaCas, int, int, byte[]);
+ }
+
+ public static class MediaCas.PluginDescriptor {
+ method public java.lang.String getName();
+ method public int getSystemId();
+ }
+
+ public class MediaCasException extends java.lang.Exception {
+ ctor public MediaCasException(java.lang.String);
+ }
+
public final class MediaCodec {
method public void configure(android.media.MediaFormat, android.view.Surface, android.media.MediaCrypto, int);
+ method public void configure(android.media.MediaFormat, android.view.Surface, int, android.media.MediaDescrambler);
method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
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;
@@ -21511,6 +21549,7 @@
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
+ field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
@@ -21667,6 +21706,7 @@
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
+ field public static final java.lang.String FEATURE_PartialFrame = "partial-frame";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -21891,6 +21931,14 @@
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
+ public final class MediaDescrambler {
+ ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
+ method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
+ method public final void release();
+ method public final boolean requiresSecureDecoderComponent(java.lang.String);
+ method public final void setMediaCasSession(byte[]);
+ }
+
public class MediaDescription implements android.os.Parcelable {
method public int describeContents();
method public java.lang.CharSequence getDescription();
@@ -22047,8 +22095,10 @@
method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ method public final void setMediaCas(android.media.MediaCas);
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
+ field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
field public static final int SEEK_TO_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_TO_NEXT_SYNC = 1; // 0x1
@@ -22145,6 +22195,7 @@
field public static final java.lang.String MIMETYPE_AUDIO_OPUS = "audio/opus";
field public static final java.lang.String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
+ field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -22155,6 +22206,7 @@
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
field public static final java.lang.String MIMETYPE_VIDEO_RAW = "video/raw";
+ field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
@@ -22295,7 +22347,7 @@
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
- public class MediaPlayer {
+ public class MediaPlayer implements android.media.VolumeAutomation {
ctor public MediaPlayer();
method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -22307,6 +22359,7 @@
method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri, android.view.SurfaceHolder, android.media.AudioAttributes, int);
method public static android.media.MediaPlayer create(android.content.Context, int);
method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void deselectTrack(int) throws java.lang.IllegalStateException;
method public int getAudioSessionId();
method public android.media.BufferingParams getBufferingParams();
@@ -23126,10 +23179,18 @@
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
+ public final class UnsupportedCasException extends android.media.MediaCasException {
+ ctor public UnsupportedCasException(java.lang.String);
+ }
+
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public abstract interface VolumeAutomation {
+ method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
+ }
+
public abstract class VolumeProvider {
ctor public VolumeProvider(int, int, int);
method public final int getCurrentVolume();
@@ -23143,6 +23204,53 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
+ public final class VolumeShaper implements java.lang.AutoCloseable {
+ method public void apply(android.media.VolumeShaper.Operation);
+ method public void close();
+ method public float getVolume();
+ method public void replace(android.media.VolumeShaper.Configuration, android.media.VolumeShaper.Operation, boolean);
+ }
+
+ public static final class VolumeShaper.Configuration implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getDurationMs();
+ method public int getInterpolatorType();
+ method public static int getMaximumCurvePoints();
+ method public float[] getTimes();
+ method public float[] getVolumes();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Configuration> CREATOR;
+ field public static final android.media.VolumeShaper.Configuration CUBIC_RAMP;
+ field public static final int INTERPOLATOR_TYPE_CUBIC = 2; // 0x2
+ field public static final int INTERPOLATOR_TYPE_CUBIC_MONOTONIC = 3; // 0x3
+ field public static final int INTERPOLATOR_TYPE_LINEAR = 1; // 0x1
+ field public static final int INTERPOLATOR_TYPE_STEP = 0; // 0x0
+ field public static final android.media.VolumeShaper.Configuration LINEAR_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SCURVE_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SINE_RAMP;
+ }
+
+ public static final class VolumeShaper.Configuration.Builder {
+ ctor public VolumeShaper.Configuration.Builder();
+ ctor public VolumeShaper.Configuration.Builder(android.media.VolumeShaper.Configuration);
+ method public android.media.VolumeShaper.Configuration build();
+ method public android.media.VolumeShaper.Configuration.Builder invertVolumes();
+ method public android.media.VolumeShaper.Configuration.Builder reflectTimes();
+ method public android.media.VolumeShaper.Configuration.Builder scaleToEndVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder scaleToStartVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder setCurve(float[], float[]);
+ method public android.media.VolumeShaper.Configuration.Builder setDurationMs(double);
+ method public android.media.VolumeShaper.Configuration.Builder setInterpolatorType(int);
+ }
+
+ public static final class VolumeShaper.Operation implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Operation> CREATOR;
+ field public static final android.media.VolumeShaper.Operation PLAY;
+ field public static final android.media.VolumeShaper.Operation REVERSE;
+ }
+
}
package android.media.audiofx {
@@ -36268,10 +36376,10 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
- method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
public final class SaveCallback {
@@ -36279,6 +36387,22 @@
method public void onSuccess();
}
+ public final class SaveInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int SAVE_UI_TYPE_ADDRESS = 2; // 0x2
+ field public static final int SAVE_UI_TYPE_CREDENTIALS = 1; // 0x1
+ field public static final int SAVE_UI_TYPE_GENERIC = 0; // 0x0
+ field public static final int SAVE_UI_TYPE_PAYMENT = 3; // 0x3
+ }
+
+ public static final class SaveInfo.Builder {
+ ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
+ method public android.service.autofill.SaveInfo build();
+ }
+
}
package android.service.carrier {
@@ -40155,7 +40279,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -40166,7 +40290,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -40179,7 +40303,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -43142,11 +43266,13 @@
field public static final int DRAW_DURATION = 4; // 0x4
field public static final int FIRST_DRAW_FRAME = 9; // 0x9
field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int INTENDED_VSYNC_TIMESTAMP = 10; // 0xa
field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
field public static final int SYNC_DURATION = 5; // 0x5
field public static final int TOTAL_DURATION = 8; // 0x8
field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ field public static final int VSYNC_TIMESTAMP = 11; // 0xb
}
public abstract class FrameStats {
@@ -50934,7 +51060,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
diff --git a/api/removed.txt b/api/removed.txt
index e467811..9baeebc 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -169,10 +169,6 @@
method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getBadgedIconForUser(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getBadgedLabelForUser(java.lang.CharSequence, android.os.UserHandle);
- method public deprecated boolean isUserRunningAndLocked();
- method public deprecated boolean isUserRunningAndLocked(android.os.UserHandle);
- method public deprecated boolean isUserRunningAndUnlocked();
- method public deprecated boolean isUserRunningAndUnlocked(android.os.UserHandle);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 0aa6296..e5de7dc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -523,6 +523,7 @@
field public static final int colorControlHighlight = 16843820; // 0x101042c
field public static final int colorControlNormal = 16843817; // 0x1010429
field public static final int colorEdgeEffect = 16843982; // 0x10104ce
+ field public static final int colorError = 16844100; // 0x1010544
field public static final int colorFocusedHighlight = 16843663; // 0x101038f
field public static final int colorForeground = 16842800; // 0x1010030
field public static final int colorForegroundInverse = 16843270; // 0x1010206
@@ -1433,7 +1434,6 @@
field public static final int textCheckMarkInverse = 16842823; // 0x1010047
field public static final int textColor = 16842904; // 0x1010098
field public static final int textColorAlertDialogListItem = 16843526; // 0x1010306
- field public static final int textColorError = 16844100; // 0x1010544
field public static final int textColorHighlight = 16842905; // 0x1010099
field public static final int textColorHighlightInverse = 16843599; // 0x101034f
field public static final int textColorHint = 16842906; // 0x101009a
@@ -2842,10 +2842,10 @@
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
- method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
- method public void onInterrupt();
+ method public abstract void onInterrupt();
method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
method public final boolean performGlobalAction(int);
@@ -4898,6 +4898,7 @@
method public abstract android.app.Fragment getPrimaryNavigationFragment();
method public void invalidateOptionsMenu();
method public abstract boolean isDestroyed();
+ method public abstract boolean isStateSaved();
method public abstract void popBackStack();
method public abstract void popBackStack(java.lang.String, int);
method public abstract void popBackStack(int, int);
@@ -9514,6 +9515,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
@@ -13210,6 +13212,7 @@
method public static android.graphics.ColorSpace.Adaptation valueOf(java.lang.String);
method public static final android.graphics.ColorSpace.Adaptation[] values();
enum_constant public static final android.graphics.ColorSpace.Adaptation BRADFORD;
+ enum_constant public static final android.graphics.ColorSpace.Adaptation CIECAT02;
enum_constant public static final android.graphics.ColorSpace.Adaptation VON_KRIES;
}
@@ -22684,13 +22687,14 @@
field public long nanoTime;
}
- public class AudioTrack implements android.media.AudioRouting {
+ public class AudioTrack implements android.media.AudioRouting android.media.VolumeAutomation {
ctor public deprecated AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public deprecated 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 addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
@@ -23158,8 +23162,42 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
+ public final class MediaCas {
+ ctor public MediaCas(int) throws android.media.UnsupportedCasException;
+ method public void closeSession(byte[]);
+ method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
+ method public static boolean isSystemIdSupported(int);
+ method public byte[] openSession(int);
+ method public byte[] openSession(int, int);
+ method public void processEcm(byte[], byte[], int, int);
+ method public void processEcm(byte[], byte[]);
+ method public void processEmm(byte[], int, int);
+ method public void processEmm(byte[]);
+ method public void provision(java.lang.String);
+ method public void refreshEntitlements(int, byte[]);
+ method public void release();
+ method public void sendEvent(int, int, byte[]);
+ method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
+ method public void setPrivateData(byte[]);
+ method public void setSessionPrivateData(byte[], byte[]);
+ }
+
+ public static abstract interface MediaCas.EventListener {
+ method public abstract void onEvent(android.media.MediaCas, int, int, byte[]);
+ }
+
+ public static class MediaCas.PluginDescriptor {
+ method public java.lang.String getName();
+ method public int getSystemId();
+ }
+
+ public class MediaCasException extends java.lang.Exception {
+ ctor public MediaCasException(java.lang.String);
+ }
+
public final class MediaCodec {
method public void configure(android.media.MediaFormat, android.view.Surface, android.media.MediaCrypto, int);
+ method public void configure(android.media.MediaFormat, android.view.Surface, int, android.media.MediaDescrambler);
method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
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;
@@ -23199,6 +23237,7 @@
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
+ field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
@@ -23355,6 +23394,7 @@
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
+ field public static final java.lang.String FEATURE_PartialFrame = "partial-frame";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -23579,6 +23619,14 @@
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
+ public final class MediaDescrambler {
+ ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
+ method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
+ method public final void release();
+ method public final boolean requiresSecureDecoderComponent(java.lang.String);
+ method public final void setMediaCasSession(byte[]);
+ }
+
public class MediaDescription implements android.os.Parcelable {
method public int describeContents();
method public java.lang.CharSequence getDescription();
@@ -23735,8 +23783,10 @@
method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ method public final void setMediaCas(android.media.MediaCas);
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
+ field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
field public static final int SEEK_TO_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_TO_NEXT_SYNC = 1; // 0x1
@@ -23833,6 +23883,7 @@
field public static final java.lang.String MIMETYPE_AUDIO_OPUS = "audio/opus";
field public static final java.lang.String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
+ field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -23843,6 +23894,7 @@
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
field public static final java.lang.String MIMETYPE_VIDEO_RAW = "video/raw";
+ field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
@@ -23983,7 +24035,7 @@
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
- public class MediaPlayer {
+ public class MediaPlayer implements android.media.VolumeAutomation {
ctor public MediaPlayer();
method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -23995,6 +24047,7 @@
method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri, android.view.SurfaceHolder, android.media.AudioAttributes, int);
method public static android.media.MediaPlayer create(android.content.Context, int);
method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void deselectTrack(int) throws java.lang.IllegalStateException;
method public int getAudioSessionId();
method public android.media.BufferingParams getBufferingParams();
@@ -24825,10 +24878,18 @@
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
+ public final class UnsupportedCasException extends android.media.MediaCasException {
+ ctor public UnsupportedCasException(java.lang.String);
+ }
+
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public abstract interface VolumeAutomation {
+ method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
+ }
+
public abstract class VolumeProvider {
ctor public VolumeProvider(int, int, int);
method public final int getCurrentVolume();
@@ -24842,6 +24903,53 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
+ public final class VolumeShaper implements java.lang.AutoCloseable {
+ method public void apply(android.media.VolumeShaper.Operation);
+ method public void close();
+ method public float getVolume();
+ method public void replace(android.media.VolumeShaper.Configuration, android.media.VolumeShaper.Operation, boolean);
+ }
+
+ public static final class VolumeShaper.Configuration implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getDurationMs();
+ method public int getInterpolatorType();
+ method public static int getMaximumCurvePoints();
+ method public float[] getTimes();
+ method public float[] getVolumes();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Configuration> CREATOR;
+ field public static final android.media.VolumeShaper.Configuration CUBIC_RAMP;
+ field public static final int INTERPOLATOR_TYPE_CUBIC = 2; // 0x2
+ field public static final int INTERPOLATOR_TYPE_CUBIC_MONOTONIC = 3; // 0x3
+ field public static final int INTERPOLATOR_TYPE_LINEAR = 1; // 0x1
+ field public static final int INTERPOLATOR_TYPE_STEP = 0; // 0x0
+ field public static final android.media.VolumeShaper.Configuration LINEAR_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SCURVE_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SINE_RAMP;
+ }
+
+ public static final class VolumeShaper.Configuration.Builder {
+ ctor public VolumeShaper.Configuration.Builder();
+ ctor public VolumeShaper.Configuration.Builder(android.media.VolumeShaper.Configuration);
+ method public android.media.VolumeShaper.Configuration build();
+ method public android.media.VolumeShaper.Configuration.Builder invertVolumes();
+ method public android.media.VolumeShaper.Configuration.Builder reflectTimes();
+ method public android.media.VolumeShaper.Configuration.Builder scaleToEndVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder scaleToStartVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder setCurve(float[], float[]);
+ method public android.media.VolumeShaper.Configuration.Builder setDurationMs(double);
+ method public android.media.VolumeShaper.Configuration.Builder setInterpolatorType(int);
+ }
+
+ public static final class VolumeShaper.Operation implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Operation> CREATOR;
+ field public static final android.media.VolumeShaper.Operation PLAY;
+ field public static final android.media.VolumeShaper.Operation REVERSE;
+ }
+
}
package android.media.audiofx {
@@ -25823,6 +25931,12 @@
method public static final boolean isChannelUriForTunerInput(android.net.Uri);
method public static final boolean isProgramUri(android.net.Uri);
field public static final java.lang.String AUTHORITY = "android.media.tv";
+ field public static final java.lang.String EXTRA_COLUMN_NAME = "android.media.tv.extra.COLUMN_NAME";
+ field public static final java.lang.String EXTRA_EXISTING_COLUMN_NAMES = "android.media.tv.extra.EXISTING_COLUMN_NAMES";
+ field public static final java.lang.String EXTRA_DATA_TYPE = "android.media.tv.extra.DATA_TYPE";
+ field public static final java.lang.String EXTRA_DEFAULT_VALUE = "android.media.tv.extra.DEFAULT_VALUE";
+ field public static final java.lang.String METHOD_ADD_COLUMN = "add_column";
+ field public static final java.lang.String METHOD_GET_COLUMNS = "get_columns";
}
public static abstract interface TvContract.BaseProgramColumns implements android.media.tv.TvContract.BaseTvColumns {
@@ -26831,44 +26945,6 @@
method public void onTetheringStarted();
}
- public final class ConnectivityMetricsEvent implements android.os.Parcelable {
- ctor public ConnectivityMetricsEvent(long, int, int, android.os.Parcelable);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent> CREATOR;
- field public final int componentTag;
- field public final android.os.Parcelable data;
- field public final int eventTag;
- field public final long timestamp;
- }
-
- public static final class ConnectivityMetricsEvent.Reference implements android.os.Parcelable {
- ctor public ConnectivityMetricsEvent.Reference(long);
- method public int describeContents();
- method public long getValue();
- method public void readFromParcel(android.os.Parcel);
- method public void setValue(long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent.Reference> CREATOR;
- }
-
- public class ConnectivityMetricsLogger {
- ctor public ConnectivityMetricsLogger();
- method public android.net.ConnectivityMetricsEvent[] getEvents(android.net.ConnectivityMetricsEvent.Reference);
- method public void logEvent(long, int, int, android.os.Parcelable);
- method public boolean register(android.app.PendingIntent);
- method public boolean unregister(android.app.PendingIntent);
- field public static final int COMPONENT_TAG_BLUETOOTH = 1; // 0x1
- field public static final int COMPONENT_TAG_CONNECTIVITY = 0; // 0x0
- field public static final int COMPONENT_TAG_TELECOM = 3; // 0x3
- field public static final int COMPONENT_TAG_TELEPHONY = 4; // 0x4
- field public static final int COMPONENT_TAG_WIFI = 2; // 0x2
- field public static final java.lang.String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
- field public static final java.lang.String DATA_KEY_EVENTS_COUNT = "count";
- field public static final int NUMBER_OF_COMPONENTS = 5; // 0x5
- field public static final int TAG_SKIPPED_EVENTS = -1; // 0xffffffff
- }
-
public class Credentials {
ctor public Credentials(int, int, int);
method public int getGid();
@@ -27109,7 +27185,8 @@
}
public abstract class NetworkRecommendationProvider {
- ctor public NetworkRecommendationProvider(android.os.Handler);
+ ctor public deprecated NetworkRecommendationProvider(android.os.Handler);
+ ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor);
method public final android.os.IBinder getBinder();
method public abstract void onRequestRecommendation(android.net.RecommendationRequest, android.net.NetworkRecommendationProvider.ResultCallback);
method public abstract void onRequestScores(android.net.NetworkKey[]);
@@ -27580,175 +27657,6 @@
}
-package android.net.metrics {
-
- public final class ApfProgramEvent implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.ApfProgramEvent> CREATOR;
- field public static final int FLAG_HAS_IPV4_ADDRESS = 1; // 0x1
- field public static final int FLAG_MULTICAST_FILTER_ON = 0; // 0x0
- field public final int currentRas;
- field public final int filteredRas;
- field public final int flags;
- field public final long lifetime;
- field public final int programLength;
- }
-
- public final class ApfStats implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.ApfStats> CREATOR;
- field public final int droppedRas;
- field public final long durationMs;
- field public final int matchingRas;
- field public final int maxProgramSize;
- field public final int parseErrors;
- field public final int programUpdates;
- field public final int receivedRas;
- field public final int zeroLifetimeRas;
- }
-
- public final class DefaultNetworkEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logEvent(int, int[], int, boolean, boolean);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.DefaultNetworkEvent> CREATOR;
- field public final int netId;
- field public final boolean prevIPv4;
- field public final boolean prevIPv6;
- field public final int prevNetId;
- field public final int[] transportTypes;
- }
-
- public final class DhcpClientEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logStateEvent(java.lang.String, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.DhcpClientEvent> CREATOR;
- field public final int durationMs;
- field public final java.lang.String ifName;
- field public final java.lang.String msg;
- }
-
- public final class DhcpErrorEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static int errorCodeWithOption(int, int);
- method public static void logParseError(java.lang.String, int);
- method public static void logReceiveError(java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int BOOTP_TOO_SHORT;
- field public static final int BUFFER_UNDERFLOW;
- field public static final android.os.Parcelable.Creator<android.net.metrics.DhcpErrorEvent> CREATOR;
- field public static final int DHCP_BAD_MAGIC_COOKIE;
- field public static final int DHCP_ERROR = 4; // 0x4
- field public static final int DHCP_INVALID_OPTION_LENGTH;
- field public static final int DHCP_NO_MSG_TYPE;
- field public static final int DHCP_UNKNOWN_MSG_TYPE;
- field public static final int L2_ERROR = 1; // 0x1
- field public static final int L2_TOO_SHORT;
- field public static final int L2_WRONG_ETH_TYPE;
- field public static final int L3_ERROR = 2; // 0x2
- field public static final int L3_INVALID_IP;
- field public static final int L3_NOT_IPV4;
- field public static final int L3_TOO_SHORT;
- field public static final int L4_ERROR = 3; // 0x3
- field public static final int L4_NOT_UDP;
- field public static final int L4_WRONG_PORT;
- field public static final int MISC_ERROR = 5; // 0x5
- field public static final int RECEIVE_ERROR;
- field public final int errorCode;
- field public final java.lang.String ifName;
- }
-
- public final class DnsEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logEvent(int, byte[], byte[], int[]);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.DnsEvent> CREATOR;
- field public final byte[] eventTypes;
- field public final int[] latenciesMs;
- field public final int netId;
- field public final byte[] returnCodes;
- }
-
- public final class IpManagerEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logEvent(int, java.lang.String, long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int COMPLETE_LIFECYCLE = 3; // 0x3
- field public static final android.os.Parcelable.Creator<android.net.metrics.IpManagerEvent> CREATOR;
- field public static final int PROVISIONING_FAIL = 2; // 0x2
- field public static final int PROVISIONING_OK = 1; // 0x1
- field public final long durationMs;
- field public final int eventType;
- field public final java.lang.String ifName;
- }
-
- public final class IpReachabilityEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logNudFailed(java.lang.String);
- method public static void logProbeEvent(java.lang.String, int);
- method public static void logProvisioningLost(java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.IpReachabilityEvent> CREATOR;
- field public static final int NUD_FAILED = 512; // 0x200
- field public static final int PROBE = 256; // 0x100
- field public static final int PROVISIONING_LOST = 768; // 0x300
- field public final int eventType;
- field public final java.lang.String ifName;
- }
-
- public final class NetworkEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logCaptivePortalFound(int, long);
- method public static void logEvent(int, int);
- method public static void logValidated(int, long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.NetworkEvent> CREATOR;
- field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
- field public static final int NETWORK_CONNECTED = 1; // 0x1
- field public static final int NETWORK_DISCONNECTED = 7; // 0x7
- field public static final int NETWORK_LINGER = 5; // 0x5
- field public static final int NETWORK_UNLINGER = 6; // 0x6
- field public static final int NETWORK_VALIDATED = 2; // 0x2
- field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
- field public final long durationMs;
- field public final int eventType;
- field public final int netId;
- }
-
- public final class RaEvent implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.RaEvent> CREATOR;
- field public final long dnsslLifetime;
- field public final long prefixPreferredLifetime;
- field public final long prefixValidLifetime;
- field public final long rdnssLifetime;
- field public final long routeInfoLifetime;
- field public final long routerLifetime;
- }
-
- public final class ValidationProbeEvent implements android.os.Parcelable {
- method public int describeContents();
- method public static void logEvent(int, long, int, int);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.metrics.ValidationProbeEvent> CREATOR;
- field public static final int DNS_FAILURE = 0; // 0x0
- field public static final int DNS_SUCCESS = 1; // 0x1
- field public static final int PROBE_DNS = 0; // 0x0
- field public static final int PROBE_HTTP = 1; // 0x1
- field public static final int PROBE_HTTPS = 2; // 0x2
- field public static final int PROBE_PAC = 3; // 0x3
- field public final long durationMs;
- field public final int netId;
- field public final int probeType;
- field public final int returnCode;
- }
-
-}
-
package android.net.nsd {
public final class NsdManager {
@@ -39388,10 +39296,10 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
- method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
public final class SaveCallback {
@@ -39399,6 +39307,22 @@
method public void onSuccess();
}
+ public final class SaveInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int SAVE_UI_TYPE_ADDRESS = 2; // 0x2
+ field public static final int SAVE_UI_TYPE_CREDENTIALS = 1; // 0x1
+ field public static final int SAVE_UI_TYPE_GENERIC = 0; // 0x0
+ field public static final int SAVE_UI_TYPE_PAYMENT = 3; // 0x3
+ }
+
+ public static final class SaveInfo.Builder {
+ ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
+ method public android.service.autofill.SaveInfo build();
+ }
+
}
package android.service.carrier {
@@ -43699,7 +43623,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -43710,7 +43634,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -43723,7 +43647,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -46687,11 +46611,13 @@
field public static final int DRAW_DURATION = 4; // 0x4
field public static final int FIRST_DRAW_FRAME = 9; // 0x9
field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int INTENDED_VSYNC_TIMESTAMP = 10; // 0xa
field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
field public static final int SYNC_DURATION = 5; // 0x5
field public static final int TOTAL_DURATION = 8; // 0x8
field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ field public static final int VSYNC_TIMESTAMP = 11; // 0xb
}
public abstract class FrameStats {
@@ -54843,7 +54769,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 6773112..4642992 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -163,10 +163,6 @@
method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getBadgedIconForUser(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getBadgedLabelForUser(java.lang.CharSequence, android.os.UserHandle);
- method public deprecated boolean isUserRunningAndLocked();
- method public deprecated boolean isUserRunningAndLocked(android.os.UserHandle);
- method public deprecated boolean isUserRunningAndUnlocked();
- method public deprecated boolean isUserRunningAndUnlocked(android.os.UserHandle);
}
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 22dcb70..cff6ffe 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -410,6 +410,7 @@
field public static final int colorControlHighlight = 16843820; // 0x101042c
field public static final int colorControlNormal = 16843817; // 0x1010429
field public static final int colorEdgeEffect = 16843982; // 0x10104ce
+ field public static final int colorError = 16844100; // 0x1010544
field public static final int colorFocusedHighlight = 16843663; // 0x101038f
field public static final int colorForeground = 16842800; // 0x1010030
field public static final int colorForegroundInverse = 16843270; // 0x1010206
@@ -1316,7 +1317,6 @@
field public static final int textCheckMarkInverse = 16842823; // 0x1010047
field public static final int textColor = 16842904; // 0x1010098
field public static final int textColorAlertDialogListItem = 16843526; // 0x1010306
- field public static final int textColorError = 16844100; // 0x1010544
field public static final int textColorHighlight = 16842905; // 0x1010099
field public static final int textColorHighlightInverse = 16843599; // 0x101034f
field public static final int textColorHint = 16842906; // 0x101009a
@@ -2722,10 +2722,10 @@
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
- method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method protected boolean onGesture(int);
- method public void onInterrupt();
+ method public abstract void onInterrupt();
method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
method public final boolean performGlobalAction(int);
@@ -4747,6 +4747,7 @@
method public abstract android.app.Fragment getPrimaryNavigationFragment();
method public void invalidateOptionsMenu();
method public abstract boolean isDestroyed();
+ method public abstract boolean isStateSaved();
method public abstract void popBackStack();
method public abstract void popBackStack(java.lang.String, int);
method public abstract void popBackStack(int, int);
@@ -9069,6 +9070,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
@@ -12611,6 +12613,7 @@
method public static android.graphics.ColorSpace.Adaptation valueOf(java.lang.String);
method public static final android.graphics.ColorSpace.Adaptation[] values();
enum_constant public static final android.graphics.ColorSpace.Adaptation BRADFORD;
+ enum_constant public static final android.graphics.ColorSpace.Adaptation CIECAT02;
enum_constant public static final android.graphics.ColorSpace.Adaptation VON_KRIES;
}
@@ -21092,13 +21095,14 @@
field public long nanoTime;
}
- public class AudioTrack implements android.media.AudioRouting {
+ public class AudioTrack implements android.media.AudioRouting android.media.VolumeAutomation {
ctor public deprecated AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public deprecated 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 addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
@@ -21566,8 +21570,42 @@
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
+ public final class MediaCas {
+ ctor public MediaCas(int) throws android.media.UnsupportedCasException;
+ method public void closeSession(byte[]);
+ method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
+ method public static boolean isSystemIdSupported(int);
+ method public byte[] openSession(int);
+ method public byte[] openSession(int, int);
+ method public void processEcm(byte[], byte[], int, int);
+ method public void processEcm(byte[], byte[]);
+ method public void processEmm(byte[], int, int);
+ method public void processEmm(byte[]);
+ method public void provision(java.lang.String);
+ method public void refreshEntitlements(int, byte[]);
+ method public void release();
+ method public void sendEvent(int, int, byte[]);
+ method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
+ method public void setPrivateData(byte[]);
+ method public void setSessionPrivateData(byte[], byte[]);
+ }
+
+ public static abstract interface MediaCas.EventListener {
+ method public abstract void onEvent(android.media.MediaCas, int, int, byte[]);
+ }
+
+ public static class MediaCas.PluginDescriptor {
+ method public java.lang.String getName();
+ method public int getSystemId();
+ }
+
+ public class MediaCasException extends java.lang.Exception {
+ ctor public MediaCasException(java.lang.String);
+ }
+
public final class MediaCodec {
method public void configure(android.media.MediaFormat, android.view.Surface, android.media.MediaCrypto, int);
+ method public void configure(android.media.MediaFormat, android.view.Surface, int, android.media.MediaDescrambler);
method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
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;
@@ -21607,6 +21645,7 @@
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
+ field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
@@ -21763,6 +21802,7 @@
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
+ field public static final java.lang.String FEATURE_PartialFrame = "partial-frame";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -21987,6 +22027,14 @@
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
+ public final class MediaDescrambler {
+ ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
+ method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
+ method public final void release();
+ method public final boolean requiresSecureDecoderComponent(java.lang.String);
+ method public final void setMediaCasSession(byte[]);
+ }
+
public class MediaDescription implements android.os.Parcelable {
method public int describeContents();
method public java.lang.CharSequence getDescription();
@@ -22143,8 +22191,10 @@
method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ method public final void setMediaCas(android.media.MediaCas);
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
+ field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
field public static final int SAMPLE_FLAG_SYNC = 1; // 0x1
field public static final int SEEK_TO_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_TO_NEXT_SYNC = 1; // 0x1
@@ -22241,6 +22291,7 @@
field public static final java.lang.String MIMETYPE_AUDIO_OPUS = "audio/opus";
field public static final java.lang.String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
+ field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -22251,6 +22302,7 @@
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
field public static final java.lang.String MIMETYPE_VIDEO_RAW = "video/raw";
+ field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
}
@@ -22391,7 +22443,7 @@
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
- public class MediaPlayer {
+ public class MediaPlayer implements android.media.VolumeAutomation {
ctor public MediaPlayer();
method public void addTimedTextSource(java.lang.String, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void addTimedTextSource(android.content.Context, android.net.Uri, java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
@@ -22403,6 +22455,7 @@
method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri, android.view.SurfaceHolder, android.media.AudioAttributes, int);
method public static android.media.MediaPlayer create(android.content.Context, int);
method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
+ method public android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
method public void deselectTrack(int) throws java.lang.IllegalStateException;
method public int getAudioSessionId();
method public android.media.BufferingParams getBufferingParams();
@@ -23222,10 +23275,18 @@
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
+ public final class UnsupportedCasException extends android.media.MediaCasException {
+ ctor public UnsupportedCasException(java.lang.String);
+ }
+
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}
+ public abstract interface VolumeAutomation {
+ method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
+ }
+
public abstract class VolumeProvider {
ctor public VolumeProvider(int, int, int);
method public final int getCurrentVolume();
@@ -23239,6 +23300,53 @@
field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
}
+ public final class VolumeShaper implements java.lang.AutoCloseable {
+ method public void apply(android.media.VolumeShaper.Operation);
+ method public void close();
+ method public float getVolume();
+ method public void replace(android.media.VolumeShaper.Configuration, android.media.VolumeShaper.Operation, boolean);
+ }
+
+ public static final class VolumeShaper.Configuration implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getDurationMs();
+ method public int getInterpolatorType();
+ method public static int getMaximumCurvePoints();
+ method public float[] getTimes();
+ method public float[] getVolumes();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Configuration> CREATOR;
+ field public static final android.media.VolumeShaper.Configuration CUBIC_RAMP;
+ field public static final int INTERPOLATOR_TYPE_CUBIC = 2; // 0x2
+ field public static final int INTERPOLATOR_TYPE_CUBIC_MONOTONIC = 3; // 0x3
+ field public static final int INTERPOLATOR_TYPE_LINEAR = 1; // 0x1
+ field public static final int INTERPOLATOR_TYPE_STEP = 0; // 0x0
+ field public static final android.media.VolumeShaper.Configuration LINEAR_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SCURVE_RAMP;
+ field public static final android.media.VolumeShaper.Configuration SINE_RAMP;
+ }
+
+ public static final class VolumeShaper.Configuration.Builder {
+ ctor public VolumeShaper.Configuration.Builder();
+ ctor public VolumeShaper.Configuration.Builder(android.media.VolumeShaper.Configuration);
+ method public android.media.VolumeShaper.Configuration build();
+ method public android.media.VolumeShaper.Configuration.Builder invertVolumes();
+ method public android.media.VolumeShaper.Configuration.Builder reflectTimes();
+ method public android.media.VolumeShaper.Configuration.Builder scaleToEndVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder scaleToStartVolume(float);
+ method public android.media.VolumeShaper.Configuration.Builder setCurve(float[], float[]);
+ method public android.media.VolumeShaper.Configuration.Builder setDurationMs(double);
+ method public android.media.VolumeShaper.Configuration.Builder setInterpolatorType(int);
+ }
+
+ public static final class VolumeShaper.Operation implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.VolumeShaper.Operation> CREATOR;
+ field public static final android.media.VolumeShaper.Operation PLAY;
+ field public static final android.media.VolumeShaper.Operation REVERSE;
+ }
+
}
package android.media.audiofx {
@@ -36407,10 +36515,10 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
- method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
public final class SaveCallback {
@@ -36418,6 +36526,22 @@
method public void onSuccess();
}
+ public final class SaveInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int SAVE_UI_TYPE_ADDRESS = 2; // 0x2
+ field public static final int SAVE_UI_TYPE_CREDENTIALS = 1; // 0x1
+ field public static final int SAVE_UI_TYPE_GENERIC = 0; // 0x0
+ field public static final int SAVE_UI_TYPE_PAYMENT = 3; // 0x3
+ }
+
+ public static final class SaveInfo.Builder {
+ ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
+ method public android.service.autofill.SaveInfo build();
+ }
+
}
package android.service.carrier {
@@ -40343,7 +40467,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -40354,7 +40478,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -40367,7 +40491,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -43495,11 +43619,13 @@
field public static final int DRAW_DURATION = 4; // 0x4
field public static final int FIRST_DRAW_FRAME = 9; // 0x9
field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int INTENDED_VSYNC_TIMESTAMP = 10; // 0xa
field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
field public static final int SYNC_DURATION = 5; // 0x5
field public static final int TOTAL_DURATION = 8; // 0x8
field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ field public static final int VSYNC_TIMESTAMP = 11; // 0xb
}
public abstract class FrameStats {
@@ -51313,7 +51439,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
diff --git a/api/test-removed.txt b/api/test-removed.txt
index e467811..9baeebc 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -169,10 +169,6 @@
method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getBadgedIconForUser(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getBadgedLabelForUser(java.lang.CharSequence, android.os.UserHandle);
- method public deprecated boolean isUserRunningAndLocked();
- method public deprecated boolean isUserRunningAndLocked(android.os.UserHandle);
- method public deprecated boolean isUserRunningAndUnlocked();
- method public deprecated boolean isUserRunningAndUnlocked(android.os.UserHandle);
}
}
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
index 50ccb07..eb6da18e 100644
--- a/cmds/idmap/Android.mk
+++ b/cmds/idmap/Android.mk
@@ -15,7 +15,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
+LOCAL_SRC_FILES := idmap.cpp create.cpp inspect.cpp
LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index 3ab1915..d388977 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -13,8 +13,6 @@
idmap --help \n\
idmap --fd target overlay fd \n\
idmap --path target overlay idmap \n\
- idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
- dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
idmap --inspect idmap \n\
\n\
DESCRIPTION \n\
@@ -49,11 +47,6 @@
--path: create idmap for target package 'target' (path to apk) and overlay package \n\
'overlay' (path to apk); write results to 'idmap' (path). \n\
\n\
- --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
- target package 'target-package-name-to-look-for' (package name) present at\n\
- 'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
- idmap file in 'dir-to-hold-idmaps' (path). \n\
-\n\
--inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
debug-friendly format. \n\
\n\
@@ -97,16 +90,6 @@
NOTES \n\
This tool and its expected invocation from installd is modelled on dexopt.";
- bool verify_directory_readable(const char *path)
- {
- return access(path, R_OK | X_OK) == 0;
- }
-
- bool verify_directory_writable(const char *path)
- {
- return access(path, W_OK) == 0;
- }
-
bool verify_file_readable(const char *path)
{
return access(path, R_OK) == 0;
@@ -167,36 +150,6 @@
return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
}
- int maybe_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
- {
- if (!verify_root_or_system()) {
- fprintf(stderr, "error: permission denied: not user root or user system\n");
- return -1;
- }
-
- if (!verify_file_readable(target_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
- return -1;
- }
-
- if (!verify_directory_writable(idmap_dir)) {
- ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
- return -1;
- }
-
- const size_t N = overlay_dirs->size();
- for (size_t i = 0; i < N; i++) {
- const char *dir = overlay_dirs->itemAt(i);
- if (!verify_directory_readable(dir)) {
- ALOGD("error: no read access to %s: %s\n", dir, strerror(errno));
- return -1;
- }
- }
-
- return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs);
- }
-
int maybe_inspect(const char *idmap_path)
{
// anyone (not just root or system) may do --inspect
@@ -235,14 +188,6 @@
return maybe_create_path(argv[2], argv[3], argv[4]);
}
- if (argc >= 6 && !strcmp(argv[1], "--scan")) {
- android::Vector<const char *> v;
- for (int i = 5; i < argc; i++) {
- v.push(argv[i]);
- }
- return maybe_scan(argv[2], argv[3], argv[4], &v);
- }
-
if (argc == 3 && !strcmp(argv[1], "--inspect")) {
return maybe_inspect(argv[2]);
}
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
index 8d4210b..5914de9 100644
--- a/cmds/idmap/idmap.h
+++ b/cmds/idmap/idmap.h
@@ -25,12 +25,6 @@
int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
-// Regarding target_package_name: the idmap_scan implementation should
-// be able to extract this from the manifest in target_apk_path,
-// simplifying the external API.
-int idmap_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs);
-
int idmap_inspect(const char *idmap_path);
#endif // _IDMAP_H_
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
deleted file mode 100644
index ab6adfb..0000000
--- a/cmds/idmap/scan.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-#include <dirent.h>
-#include <inttypes.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-
-#include "idmap.h"
-
-#include <memory>
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/StreamingZipInflater.h>
-#include <androidfw/ZipFileRO.h>
-#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include <utils/SortedVector.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#define NO_OVERLAY_TAG (-1000)
-
-using namespace android;
-
-namespace {
- struct Overlay {
- Overlay() {}
- Overlay(const String8& a, const String8& i, int p) :
- apk_path(a), idmap_path(i), priority(p) {}
-
- bool operator<(Overlay const& rhs) const
- {
- return rhs.priority > priority;
- }
-
- String8 apk_path;
- String8 idmap_path;
- int priority;
- };
-
- bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
- {
- // the file is opened for appending so that it doesn't get truncated
- // before we can guarantee mutual exclusion via the flock
- FILE* fout = fopen(filename, "a");
- if (fout == NULL) {
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
- fclose(fout);
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
- TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
- fclose(fout);
- return false;
- }
-
- for (size_t i = 0; i < overlayVector.size(); ++i) {
- const Overlay& overlay = overlayVector[i];
- fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
- }
-
- TEMP_FAILURE_RETRY(fflush(fout));
- TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
- fclose(fout);
-
- // Make file world readable since Zygote (running as root) will read
- // it when creating the initial AssetManger object
- const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
- if (chmod(filename, mode) == -1) {
- unlink(filename);
- return false;
- }
-
- return true;
- }
-
- String8 flatten_path(const char *path)
- {
- String16 tmp(path);
- tmp.replaceAll('/', '@');
- return String8(tmp);
- }
-
- int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
- {
- const size_t N = parser.getAttributeCount();
- String16 target;
- int priority = -1;
- for (size_t i = 0; i < N; ++i) {
- size_t len;
- String16 key(parser.getAttributeName(i, &len));
- if (key == String16("targetPackage")) {
- const char16_t *p = parser.getAttributeStringValue(i, &len);
- if (p != NULL) {
- target = String16(p, len);
- }
- } else if (key == String16("priority")) {
- Res_value v;
- if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
- priority = v.data;
- if (priority < 0 || priority > 9999) {
- return -1;
- }
- }
- }
- }
- if (target == String16(target_package_name)) {
- return priority;
- }
- return NO_OVERLAY_TAG;
- }
-
- int parse_manifest(const void *data, size_t size, const char *target_package_name)
- {
- ResXMLTree parser;
- parser.setTo(data, size);
- if (parser.getError() != NO_ERROR) {
- ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
- return -1;
- }
-
- ResXMLParser::event_code_t type;
- do {
- type = parser.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- String16 tag(parser.getElementName(&len));
- if (tag == String16("overlay")) {
- return parse_overlay_tag(parser, target_package_name);
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
-
- return NO_OVERLAY_TAG;
- }
-
- int parse_apk(const char *path, const char *target_package_name)
- {
- std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
- if (zip.get() == NULL) {
- ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
- return -1;
- }
- ZipEntryRO entry;
- if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
- ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
- return -1;
- }
- uint32_t uncompLen = 0;
- uint16_t method;
- if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
- ALOGW("%s: failed to read entry info\n", __FUNCTION__);
- return -1;
- }
- if (method != ZipFileRO::kCompressDeflated) {
- ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
- return -1;
- }
- FileMap *dataMap = zip->createEntryFileMap(entry);
- if (dataMap == NULL) {
- ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
- return -1;
- }
- char *buf = new char[uncompLen];
- if (NULL == buf) {
- ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
- delete dataMap;
- return -1;
- }
- StreamingZipInflater inflater(dataMap, uncompLen);
- if (inflater.read(buf, uncompLen) < 0) {
- ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
- delete[] buf;
- delete dataMap;
- return -1;
- }
-
- int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
- delete[] buf;
- delete dataMap;
- return priority;
- }
-}
-
-int idmap_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
-{
- String8 filename = String8(idmap_dir);
- filename.appendPath("overlays.list");
-
- SortedVector<Overlay> overlayVector;
- const size_t N = overlay_dirs->size();
- for (size_t i = 0; i < N; ++i) {
- const char *overlay_dir = overlay_dirs->itemAt(i);
- DIR *dir = opendir(overlay_dir);
- if (dir == NULL) {
- return EXIT_FAILURE;
- }
-
- struct dirent *dirent;
- while ((dirent = readdir(dir)) != NULL) {
- struct stat st;
- char overlay_apk_path[PATH_MAX + 1];
- snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
- if (stat(overlay_apk_path, &st) < 0) {
- continue;
- }
- if (!S_ISREG(st.st_mode)) {
- continue;
- }
-
- int priority = parse_apk(overlay_apk_path, target_package_name);
- if (priority < 0) {
- continue;
- }
-
- String8 idmap_path(idmap_dir);
- idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
- idmap_path.append("@idmap");
-
- if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
- ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
- target_apk_path, overlay_apk_path, idmap_path.string());
- continue;
- }
-
- Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
- overlayVector.add(overlay);
- }
-
- closedir(dir);
- }
-
- if (!writePackagesList(filename.string(), overlayVector)) {
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3884748..b4e119e 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -421,12 +421,12 @@
* this method returns. Services wishing to use the event after this method returns should
* make a copy.
*/
- public void onAccessibilityEvent(AccessibilityEvent event) {}
+ public abstract void onAccessibilityEvent(AccessibilityEvent event);
/**
* Callback for interrupting the accessibility feedback.
*/
- public void onInterrupt() {}
+ public abstract void onInterrupt();
/**
* Dispatches service connection to internal components first, then the
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a820da4..0193c5f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2822,7 +2822,9 @@
return;
}
- if (!mFragments.getFragmentManager().popBackStackImmediate()) {
+ FragmentManager fragmentManager = mFragments.getFragmentManager();
+
+ if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) {
finishAfterTransition();
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c305616..ce9d91f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2773,7 +2773,7 @@
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
- dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
+ dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 045bd0a..3e9b987 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2067,24 +2067,17 @@
@Override
public Display getDisplay() {
- final DisplayAdjustments displayAdjustments = mResources.getDisplayAdjustments();
if (mDisplay == null) {
return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
- displayAdjustments);
+ mResources);
}
- if (!mDisplay.getDisplayAdjustments().equals(displayAdjustments)) {
- mDisplay = mResourcesManager.getAdjustedDisplay(mDisplay.getDisplayId(),
- displayAdjustments);
- }
return mDisplay;
}
@Override
public void updateDisplay(int displayId) {
- final DisplayAdjustments displayAdjustments = mResources.getDisplayAdjustments();
- mDisplay = mResourcesManager.getAdjustedDisplay(displayId,
- displayAdjustments);
+ mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
}
@Override
@@ -2202,7 +2195,7 @@
compatInfo,
classLoader);
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
- context.mResources.getDisplayAdjustments());
+ context.getResources());
return context;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 8ad7810..977931a 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -397,6 +397,19 @@
public void invalidateOptionsMenu() { }
/**
+ * Returns {@code true} if the FragmentManager's state has already been saved
+ * by its host. Any operations that would change saved state should not be performed
+ * if this method returns true. For example, any popBackStack() method, such as
+ * {@link #popBackStackImmediate()} or any FragmentTransaction using
+ * {@link FragmentTransaction#commit()} instead of
+ * {@link FragmentTransaction#commitAllowingStateLoss()} will change
+ * the state and will result in an error.
+ *
+ * @return true if this FragmentManager's state has already been saved by its host
+ */
+ public abstract boolean isStateSaved();
+
+ /**
* Callback interface for listening to fragment state changes that happen
* within a given FragmentManager.
*/
@@ -1787,6 +1800,7 @@
}
}
+ @Override
public boolean isStateSaved() {
return mStateSaved;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 812daf8..64fc44b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2702,11 +2702,11 @@
}
/**
- * Specifies the time at which this notification should be canceled, if it is not already
- * canceled.
+ * Specifies a duration in milliseconds after which this notification should be canceled,
+ * if it is not already canceled.
*/
- public Builder setTimeout(long when) {
- mN.mTimeout = when;
+ public Builder setTimeout(long durationMs) {
+ mN.mTimeout = durationMs;
return this;
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 55f7df3..52ec045 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -104,10 +104,17 @@
new WeakHashMap<>();
/**
- * A cache of DisplayId to DisplayAdjustments.
+ * A cache of DisplayId, DisplayAdjustments to Display.
*/
- private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> mDisplays =
- new ArrayMap<>();
+ private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>>
+ mAdjustedDisplays = new ArrayMap<>();
+
+ /**
+ * A cache of DisplayId, Resources to Display. These display adjustments associated with these
+ * {@link Display}s will change as the resources change.
+ */
+ private final ArrayMap<Pair<Integer, Resources>, WeakReference<Display>> mResourceDisplays =
+ new ArrayMap<>();
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
@@ -201,19 +208,21 @@
/**
* Returns an adjusted {@link Display} object based on the inputs or null if display isn't
- * available.
+ * available. This method is only used within {@link ResourcesManager} to calculate display
+ * metrics based on a set {@link DisplayAdjustments}. All other usages should instead call
+ * {@link ResourcesManager#getAdjustedDisplay(int, Resources)}.
*
* @param displayId display Id.
* @param displayAdjustments display adjustments.
*/
- public Display getAdjustedDisplay(final int displayId,
+ private Display getAdjustedDisplay(final int displayId,
@Nullable DisplayAdjustments displayAdjustments) {
final DisplayAdjustments displayAdjustmentsCopy = (displayAdjustments != null)
? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
final Pair<Integer, DisplayAdjustments> key =
Pair.create(displayId, displayAdjustmentsCopy);
synchronized (this) {
- WeakReference<Display> wd = mDisplays.get(key);
+ WeakReference<Display> wd = mAdjustedDisplays.get(key);
if (wd != null) {
final Display display = wd.get();
if (display != null) {
@@ -227,7 +236,37 @@
}
final Display display = dm.getCompatibleDisplay(displayId, key.second);
if (display != null) {
- mDisplays.put(key, new WeakReference<>(display));
+ mAdjustedDisplays.put(key, new WeakReference<>(display));
+ }
+ return display;
+ }
+ }
+
+ /**
+ * Returns an adjusted {@link Display} object based on the inputs or null if display isn't
+ * available.
+ *
+ * @param displayId display Id.
+ * @param resources The {@link Resources} backing the display adjustments.
+ */
+ public Display getAdjustedDisplay(final int displayId, Resources resources) {
+ final Pair<Integer, Resources> key = Pair.create(displayId, resources);
+ synchronized (this) {
+ WeakReference<Display> wd = mResourceDisplays.get(key);
+ if (wd != null) {
+ final Display display = wd.get();
+ if (display != null) {
+ return display;
+ }
+ }
+ final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+ if (dm == null) {
+ // may be null early in system startup
+ return null;
+ }
+ final Display display = dm.getCompatibleDisplay(displayId, resources);
+ if (display != null) {
+ mResourceDisplays.put(key, new WeakReference<>(display));
}
return display;
}
@@ -316,6 +355,7 @@
final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
final Configuration config = generateConfig(key, dm);
final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
+
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
}
@@ -811,7 +851,9 @@
}
int changes = mResConfiguration.updateFrom(config);
// Things might have changed in display manager, so clear the cached displays.
- mDisplays.clear();
+ mAdjustedDisplays.clear();
+ mResourceDisplays.clear();
+
DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
if (compat != null && (mResCompatibilityInfo == null ||
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9cb3dd6..6585793 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3346,6 +3346,17 @@
public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
/**
+ * Keyguard features that when set on a managed profile that doesn't have its own challenge will
+ * affect the profile's parent user. These can also be set on the managed profile's parent
+ * {@link DevicePolicyManager} instance.
+ *
+ * @hide
+ */
+ public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
+ DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
+ | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+
+ /**
* Called by an application that is administering the device to request that the storage system
* be encrypted.
* <p>
@@ -7697,7 +7708,34 @@
/**
* Called by a device owner to control the network logging feature.
*
- * <p> Network logs contain DNS lookup and connect() library call events.
+ * <p> Network logs contain DNS lookup and connect() library call events. The following library
+ * functions are recorded while network logging is active:
+ * <ul>
+ * <li>{@code getaddrinfo()}</li>
+ * <li>{@code gethostbyname()}</li>
+ * <li>{@code connect()}</li>
+ * </ul>
+ *
+ * <p> Network logging is a low-overhead tool for forensics but it is not guaranteed to use
+ * full system call logging; event reporting is enabled by default for all processes but not
+ * strongly enforced.
+ * Events from applications using alternative implementations of libc, making direct kernel
+ * calls, or deliberately obfuscating traffic may not be recorded.
+ *
+ * <p> Some common network events may not be reported. For example:
+ * <ul>
+ * <li>Applications may hardcode IP addresses to reduce the number of DNS lookups, or use
+ * an alternative system for name resolution, and so avoid calling
+ * {@code getaddrinfo()} or {@code gethostbyname}.</li>
+ * <li>Applications may use datagram sockets for performance reasons, for example
+ * for a game client. Calling {@code connect()} is unnecessary for this kind of
+ * socket, so it will not trigger a network event.</li>
+ * </ul>
+ *
+ * <p> It is possible to directly intercept layer 3 traffic leaving the device using an
+ * always-on VPN service.
+ * See {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)}
+ * and {@link android.net.VpnService} for details.
*
* <p><strong>Note:</strong> The device owner won't be able to retrieve network logs if there
* are unaffiliated secondary users or profiles on the device, regardless of whether the
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 716bbe9..483a7a1 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -722,7 +722,14 @@
}
pwriter.writeString(mClassName);
- out.writeInt(flags);
+
+ int writtenFlags = flags;
+ if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0 && (mSanitized || !sanitizeOnWrite)) {
+ // Remove 'checked' from sanitized auto-fill request.
+ writtenFlags = flags & ~FLAGS_CHECKED;
+ }
+
+ out.writeInt(writtenFlags);
if ((flags&FLAGS_HAS_ID) != 0) {
out.writeInt(mId);
if (mId != 0) {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 4480b41..687d590 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -500,6 +500,7 @@
public ContentResolver(Context context) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
+ mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
}
/** @hide */
@@ -1868,13 +1869,18 @@
/**
* Register an observer class that gets callbacks when data identified by a
* given content URI changes.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+ * notifications must be backed by a valid {@link ContentProvider}.
*
- * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
- * for a whole class of content.
- * @param notifyForDescendants When false, the observer will be notified whenever a
- * change occurs to the exact URI specified by <code>uri</code> or to one of the
- * URI's ancestors in the path hierarchy. When true, the observer will also be notified
- * whenever a change occurs to the URI's descendants in the path hierarchy.
+ * @param uri The URI to watch for changes. This can be a specific row URI,
+ * or a base URI for a whole class of content.
+ * @param notifyForDescendants When false, the observer will be notified
+ * whenever a change occurs to the exact URI specified by
+ * <code>uri</code> or to one of the URI's ancestors in the path
+ * hierarchy. When true, the observer will also be notified
+ * whenever a change occurs to the URI's descendants in the path
+ * hierarchy.
* @param observer The object that receives callbacks when changes occur.
* @see #unregisterContentObserver
*/
@@ -1894,7 +1900,7 @@
ContentObserver observer, @UserIdInt int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendents,
- observer.getContentObserver(), userHandle);
+ observer.getContentObserver(), userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
@@ -1918,16 +1924,22 @@
}
/**
- * Notify registered observers that a row was updated and attempt to sync changes
- * to the network.
- * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
- * By default, CursorAdapter objects will get this notification.
+ * Notify registered observers that a row was updated and attempt to sync
+ * changes to the network.
+ * <p>
+ * To observe events sent through this call, use
+ * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+ * notifications must be backed by a valid {@link ContentProvider}.
*
* @param uri The uri of the content that was changed.
- * @param observer The observer that originated the change, may be <code>null</null>.
- * The observer that originated the change will only receive the notification if it
- * has requested to receive self-change notifications by implementing
- * {@link ContentObserver#deliverSelfNotifications()} to return true.
+ * @param observer The observer that originated the change, may be
+ * <code>null</null>. The observer that originated the change
+ * will only receive the notification if it has requested to
+ * receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return
+ * true.
*/
public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) {
notifyChange(uri, observer, true /* sync to network */);
@@ -1935,17 +1947,25 @@
/**
* Notify registered observers that a row was updated.
- * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
- * By default, CursorAdapter objects will get this notification.
- * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
- * adapter that's registered for the authority of the provided uri. No account will be
- * passed to the sync adapter, so all matching accounts will be synchronized.
+ * <p>
+ * To observe events sent through this call, use
+ * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+ * <p>
+ * If syncToNetwork is true, this will attempt to schedule a local sync
+ * using the sync adapter that's registered for the authority of the
+ * provided uri. No account will be passed to the sync adapter, so all
+ * matching accounts will be synchronized.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+ * notifications must be backed by a valid {@link ContentProvider}.
*
* @param uri The uri of the content that was changed.
- * @param observer The observer that originated the change, may be <code>null</null>.
- * The observer that originated the change will only receive the notification if it
- * has requested to receive self-change notifications by implementing
- * {@link ContentObserver#deliverSelfNotifications()} to return true.
+ * @param observer The observer that originated the change, may be
+ * <code>null</null>. The observer that originated the change
+ * will only receive the notification if it has requested to
+ * receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return
+ * true.
* @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
*/
@@ -1961,17 +1981,25 @@
/**
* Notify registered observers that a row was updated.
- * To register, call {@link #registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver) registerContentObserver()}.
- * By default, CursorAdapter objects will get this notification.
- * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
- * adapter that's registered for the authority of the provided uri. No account will be
- * passed to the sync adapter, so all matching accounts will be synchronized.
+ * <p>
+ * To observe events sent through this call, use
+ * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+ * <p>
+ * If syncToNetwork is true, this will attempt to schedule a local sync
+ * using the sync adapter that's registered for the authority of the
+ * provided uri. No account will be passed to the sync adapter, so all
+ * matching accounts will be synchronized.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+ * notifications must be backed by a valid {@link ContentProvider}.
*
* @param uri The uri of the content that was changed.
- * @param observer The observer that originated the change, may be <code>null</null>.
- * The observer that originated the change will only receive the notification if it
- * has requested to receive self-change notifications by implementing
- * {@link ContentObserver#deliverSelfNotifications()} to return true.
+ * @param observer The observer that originated the change, may be
+ * <code>null</null>. The observer that originated the change
+ * will only receive the notification if it has requested to
+ * receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return
+ * true.
* @param flags Additional flags: {@link #NOTIFY_SYNC_TO_NETWORK}.
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
*/
@@ -1997,7 +2025,7 @@
uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(),
syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
- userHandle);
+ userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
@@ -2013,7 +2041,7 @@
getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(), flags,
- userHandle);
+ userHandle, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
@@ -2932,6 +2960,7 @@
private final Context mContext;
final String mPackageName;
+ final int mTargetSdkVersion;
private static final String TAG = "ContentResolver";
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 3446e03..c500116 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -42,7 +42,7 @@
* USER_CURRENT are properly handled.
*/
void registerContentObserver(in Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle);
+ IContentObserver observer, int userHandle, int targetSdkVersion);
/**
* Notify observers of a particular user's view of the provider.
@@ -53,7 +53,7 @@
*/
void notifyChange(in Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, int flags,
- int userHandle);
+ int userHandle, int targetSdkVersion);
void requestSync(in Account account, String authority, in Bundle extras);
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 28068c5..870db217 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1115,6 +1115,15 @@
public static final String ACTION_SIM_ACTIVATION_REQUEST =
"android.intent.action.SIM_ACTIVATION_REQUEST";
/**
+ * Activity Action: Main entry point for carrier setup apps.
+ * <p>Carrier apps that provide an implementation for this action may be invoked to configure
+ * carrier service and typically require
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges() carrier privileges} to
+ * fulfill their duties.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
+ /**
* Activity Action: Send a message to someone specified by the data.
* <p>Input: {@link #getData} is URI describing the target.
* <p>Output: nothing.
@@ -9260,6 +9269,13 @@
mClipData.prepareToLeaveProcess(leavingPackage, getFlags());
}
+ if (mExtras != null && !mExtras.isParcelled()) {
+ final Object intent = mExtras.get(Intent.EXTRA_INTENT);
+ if (intent instanceof Intent) {
+ ((Intent) intent).prepareToLeaveProcess(leavingPackage);
+ }
+ }
+
if (mAction != null && mData != null && StrictMode.vmFileUriExposureEnabled()
&& leavingPackage) {
switch (mAction) {
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
new file mode 100644
index 0000000..7d7784a
--- /dev/null
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Auxiliary application resolution response.
+ * <p>
+ * Used when resolution occurs, but, the target is not actually on the device.
+ * This happens resolving instant apps that haven't been installed yet or if
+ * the application consists of multiple feature splits and the needed split
+ * hasn't been installed.
+ * @hide
+ */
+public final class AuxiliaryResolveInfo extends IntentFilter {
+ /** Resolved information returned from the external ephemeral resolver */
+ public final EphemeralResolveInfo resolveInfo;
+ /** The resolved package. Copied from {@link #resolveInfo}. */
+ public final String packageName;
+ /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+ public final String splitName;
+ /** Whether or not ephemeral resolution needs the second phase */
+ public final boolean needsPhaseTwo;
+ /** Opaque token to track the ephemeral application resolution */
+ public final String token;
+ /** The version code of the package */
+ public final int versionCode;
+
+ /** Create a response for installing an instant application. */
+ public AuxiliaryResolveInfo(@NonNull EphemeralResolveInfo resolveInfo,
+ @NonNull IntentFilter orig,
+ @Nullable String splitName,
+ @NonNull String token,
+ boolean needsPhase2) {
+ super(orig);
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.splitName = splitName;
+ this.token = token;
+ this.needsPhaseTwo = needsPhase2;
+ this.versionCode = resolveInfo.getVersionCode();
+ }
+
+ /** Create a response for installing a split on demand. */
+ public AuxiliaryResolveInfo(@NonNull String packageName,
+ @Nullable String splitName,
+ int versionCode) {
+ super();
+ this.packageName = packageName;
+ this.splitName = splitName;
+ this.versionCode = versionCode;
+ this.resolveInfo = null;
+ this.token = null;
+ this.needsPhaseTwo = false;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ContainerEncryptionParams.aidl b/core/java/android/content/pm/ContainerEncryptionParams.aidl
deleted file mode 100644
index c13d946..0000000
--- a/core/java/android/content/pm/ContainerEncryptionParams.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2012, 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.content.pm;
-
-parcelable ContainerEncryptionParams;
diff --git a/core/java/android/content/pm/EphemeralRequest.java b/core/java/android/content/pm/EphemeralRequest.java
index 7f2b3ee1..58099c2b 100644
--- a/core/java/android/content/pm/EphemeralRequest.java
+++ b/core/java/android/content/pm/EphemeralRequest.java
@@ -24,24 +24,21 @@
*/
public final class EphemeralRequest {
/** Response from the first phase of ephemeral application resolution */
- public final EphemeralResponse responseObj;
+ public final AuxiliaryResolveInfo responseObj;
/** The original intent that triggered ephemeral application resolution */
public final Intent origIntent;
/** Resolved type of the intent */
public final String resolvedType;
- /** The intent that would launch if there were no ephemeral applications */
- public final Intent launchIntent;
/** The name of the package requesting the ephemeral application */
public final String callingPackage;
/** ID of the user requesting the ephemeral application */
public final int userId;
- public EphemeralRequest(EphemeralResponse responseObj, Intent origIntent,
- String resolvedType, Intent launchIntent, String callingPackage, int userId) {
+ public EphemeralRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
+ String resolvedType, String callingPackage, int userId) {
this.responseObj = responseObj;
this.origIntent = origIntent;
this.resolvedType = resolvedType;
- this.launchIntent = launchIntent;
this.callingPackage = callingPackage;
this.userId = userId;
}
diff --git a/core/java/android/content/pm/EphemeralResponse.java b/core/java/android/content/pm/EphemeralResponse.java
deleted file mode 100644
index 6e569f7..0000000
--- a/core/java/android/content/pm/EphemeralResponse.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IntentFilter;
-
-/**
- * Ephemeral application resolution response.
- * @hide
- */
-public final class EphemeralResponse extends IntentFilter {
- /** Resolved information returned from the external ephemeral resolver */
- public final EphemeralResolveInfo resolveInfo;
- /** The resolved package. Copied from {@link #resolveInfo}. */
- public final String packageName;
- /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
- public final String splitName;
- /** Whether or not ephemeral resolution needs the second phase */
- public final boolean needsPhase2;
- /** Opaque token to track the ephemeral application resolution */
- public final String token;
-
- public EphemeralResponse(@NonNull EphemeralResolveInfo resolveInfo,
- @NonNull IntentFilter orig,
- @Nullable String splitName,
- @NonNull String token,
- boolean needsPhase2) {
- super(orig);
- this.resolveInfo = resolveInfo;
- this.packageName = resolveInfo.getPackageName();
- this.splitName = splitName;
- this.token = token;
- this.needsPhase2 = needsPhase2;
- }
-}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ffb777d..11948dbc 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -22,7 +22,6 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ContainerEncryptionParams;
import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
@@ -38,7 +37,6 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
import android.content.pm.PackageInfo;
-import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
diff --git a/core/java/android/content/pm/IPinItemRequest.aidl b/core/java/android/content/pm/IPinItemRequest.aidl
index efe2835..eddce58 100644
--- a/core/java/android/content/pm/IPinItemRequest.aidl
+++ b/core/java/android/content/pm/IPinItemRequest.aidl
@@ -16,6 +16,8 @@
package android.content.pm;
import android.os.Bundle;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.ShortcutInfo;
/**
* {@hide}
@@ -23,4 +25,6 @@
interface IPinItemRequest {
boolean isValid();
boolean accept(in Bundle options);
+ ShortcutInfo getShortcutInfo();
+ AppWidgetProviderInfo getAppWidgetProviderInfo();
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 0866af2..d76d824 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1291,28 +1291,14 @@
public @interface RequestType {}
private final int mRequestType;
- private final ShortcutInfo mShortcutInfo;
- private final AppWidgetProviderInfo mAppWidgetInfo;
private final IPinItemRequest mInner;
/**
* @hide
*/
- public PinItemRequest(ShortcutInfo shortcutInfo, IPinItemRequest inner) {
- mRequestType = REQUEST_TYPE_SHORTCUT;
- mShortcutInfo = shortcutInfo;
- mAppWidgetInfo = null;
+ public PinItemRequest(IPinItemRequest inner, int type) {
mInner = inner;
- }
-
- /**
- * @hide
- */
- public PinItemRequest(AppWidgetProviderInfo appWidgetInfo, IPinItemRequest inner) {
- mRequestType = REQUEST_TYPE_APPWIDGET;
- mShortcutInfo = null;
- mAppWidgetInfo = appWidgetInfo;
- mInner = inner;
+ mRequestType = type;
}
/**
@@ -1330,7 +1316,11 @@
*/
@Nullable
public ShortcutInfo getShortcutInfo() {
- return mShortcutInfo;
+ try {
+ return mInner.getShortcutInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
/**
@@ -1339,12 +1329,16 @@
*/
@Nullable
public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
- if (mAppWidgetInfo != null) {
- AppWidgetProviderInfo info = mAppWidgetInfo.clone();
+ try {
+ final AppWidgetProviderInfo info = mInner.getAppWidgetProviderInfo();
+ if (info == null) {
+ return null;
+ }
info.updateDimensions(context.getResources().getDisplayMetrics());
return info;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
- return null;
}
/**
@@ -1381,22 +1375,12 @@
final ClassLoader cl = getClass().getClassLoader();
mRequestType = source.readInt();
- mShortcutInfo = mRequestType == REQUEST_TYPE_SHORTCUT ?
- (ShortcutInfo) source.readParcelable(cl) : null;
- mAppWidgetInfo = mRequestType == REQUEST_TYPE_APPWIDGET ?
- (AppWidgetProviderInfo) source.readParcelable(cl) : null;
mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mRequestType);
- if (mRequestType == REQUEST_TYPE_SHORTCUT) {
- dest.writeParcelable(mShortcutInfo, flags);
- }
- if (mRequestType == REQUEST_TYPE_APPWIDGET) {
- dest.writeParcelable(mAppWidgetInfo, flags);
- }
dest.writeStrongBinder(mInner.asBinder());
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5733982..6646402 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -624,8 +624,10 @@
* should be installed as forward locked, i.e. only the app itself should
* have access to its code and non-resource assets.
*
+ * @deprecated new installs into ASEC containers are no longer supported.
* @hide
*/
+ @Deprecated
public static final int INSTALL_FORWARD_LOCK = 0x00000001;
/**
@@ -648,8 +650,11 @@
* Flag parameter for {@link #installPackage} to indicate that this package
* must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
*
+ * @deprecated new installs into ASEC containers are no longer supported;
+ * use adoptable storage instead.
* @hide
*/
+ @Deprecated
public static final int INSTALL_EXTERNAL = 0x00000008;
/**
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 7d59bd6..6272822 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -213,12 +213,11 @@
* @param responseObj The response of the first phase of ephemeral resolution
* @param origIntent The original intent that triggered ephemeral resolution
* @param resolvedType The resolved type of the intent
- * @param launchIntent The intent that would launch if there was no ephemeral application
* @param callingPackage The name of the package requesting the ephemeral application
* @param userId The ID of the user that triggered ephemeral resolution
*/
- public abstract void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
- Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage,
+ public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
+ Intent origIntent, String resolvedType, String callingPackage,
int userId);
/**
@@ -307,4 +306,10 @@
*/
public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
List<String> overlayPackageNames);
+
+ /**
+ * Resolves an intent, allowing instant apps to be resolved.
+ */
+ public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
+ int flags, int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 56f1e0c..f801e45 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2065,9 +2065,6 @@
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
- pkg.mOverlayPriority = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
- -1);
sa.recycle();
if (pkg.mOverlayTarget == null) {
@@ -2075,12 +2072,6 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
- if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
- outError[0] = "<overlay> priority must be between 0 and 9999";
- mParseError =
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
- }
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_KEY_SETS)) {
@@ -5518,7 +5509,6 @@
public String mRequiredAccountType;
public String mOverlayTarget;
- public int mOverlayPriority;
public boolean mTrustedOverlay;
/**
@@ -5995,7 +5985,6 @@
mRestrictedAccountType = dest.readString();
mRequiredAccountType = dest.readString();
mOverlayTarget = dest.readString();
- mOverlayPriority = dest.readInt();
mTrustedOverlay = (dest.readInt() == 1);
mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
@@ -6111,7 +6100,6 @@
dest.writeString(mRestrictedAccountType);
dest.writeString(mRequiredAccountType);
dest.writeString(mOverlayTarget);
- dest.writeInt(mOverlayPriority);
dest.writeInt(mTrustedOverlay ? 1 : 0);
dest.writeArraySet(mSigningKeys);
dest.writeArraySet(mUpgradeKeySets);
@@ -6560,6 +6548,7 @@
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
+ ai.resourceDirs = state.resourceDirs;
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 24f1164..ee56a18 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,8 @@
import com.android.internal.util.ArrayUtils;
+import java.util.Arrays;
+
/**
* Per-user state information about a package.
* @hide
@@ -54,6 +56,8 @@
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
+ public String[] resourceDirs;
+
public PackageUserState() {
installed = true;
hidden = false;
@@ -81,6 +85,8 @@
installReason = o.installReason;
disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
+ resourceDirs =
+ o.resourceDirs == null ? null : Arrays.copyOf(o.resourceDirs, o.resourceDirs.length);
}
/**
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index f8b4570..50f2d53 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -61,11 +61,12 @@
public ProviderInfo providerInfo;
/**
- * The ephemeral application that corresponds to this resolution match. This will
- * only be set in specific circumstances.
+ * An auxiliary response that may modify the resolved information. This is
+ * only set under certain circumstances; such as when resolving instant apps
+ * or components defined in un-installed splits.
* @hide
*/
- public EphemeralResponse ephemeralResponse;
+ public AuxiliaryResolveInfo auxiliaryInfo;
/**
* The IntentFilter that was matched for this ResolveInfo.
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 37e32ff..4cf55ba 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -222,19 +222,21 @@
*/
final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
- final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
- if (block < 0) {
- return false;
- }
+ synchronized (this) {
+ final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
+ if (block < 0) {
+ return false;
+ }
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
- if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mStringBlocks[block].get(outValue.data);
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mStringBlocks[block].get(outValue.data);
+ }
+ return true;
}
- return true;
}
/**
@@ -244,18 +246,20 @@
* @param resId the resource id of the string array
*/
final CharSequence[] getResourceTextArray(@ArrayRes int resId) {
- final int[] rawInfoArray = getArrayStringInfo(resId);
- final int rawInfoArrayLen = rawInfoArray.length;
- final int infoArrayLen = rawInfoArrayLen / 2;
- int block;
- int index;
- final CharSequence[] retArray = new CharSequence[infoArrayLen];
- for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
- block = rawInfoArray[i];
- index = rawInfoArray[i + 1];
- retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ synchronized (this) {
+ final int[] rawInfoArray = getArrayStringInfo(resId);
+ final int rawInfoArrayLen = rawInfoArray.length;
+ final int infoArrayLen = rawInfoArrayLen / 2;
+ int block;
+ int index;
+ final CharSequence[] retArray = new CharSequence[infoArrayLen];
+ for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
+ block = rawInfoArray[i];
+ index = rawInfoArray[i + 1];
+ retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ }
+ return retArray;
}
- return retArray;
}
/**
@@ -320,8 +324,10 @@
}
/*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
- // Cookies map to string blocks starting at 1.
- return mStringBlocks[cookie - 1].get(id);
+ synchronized (this) {
+ // Cookies map to string blocks starting at 1.
+ return mStringBlocks[cookie - 1].get(id);
+ }
}
/**
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 273522a..a46db06 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -508,8 +508,8 @@
}
/**
- * Indicates whether this color state list contains more than one state spec
- * and will change color based on state.
+ * Indicates whether this color state list contains at least one state spec
+ * and the first spec is not empty (e.g. match-all).
*
* @return True if this color state list changes color based on state, false
* otherwise.
@@ -517,7 +517,7 @@
*/
@Override
public boolean isStateful() {
- return mStateSpecs.length > 1;
+ return mStateSpecs.length >= 1 && mStateSpecs[0].length > 0;
}
/**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 33a9f5e..a529c2f 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -339,8 +339,7 @@
private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
Display display = mDisplays.get(displayId);
if (display == null) {
- display = mGlobal.getCompatibleDisplay(displayId,
- mContext.getDisplayAdjustments(displayId));
+ display = mGlobal.getCompatibleDisplay(displayId, mContext.getResources());
if (display != null) {
mDisplays.put(displayId, display);
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 826eb74..341754c 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.hardware.display.DisplayManager.DisplayListener;
import android.media.projection.MediaProjection;
import android.media.projection.IMediaProjection;
@@ -181,6 +182,24 @@
}
/**
+ * Gets information about a logical display.
+ *
+ * The display metrics may be adjusted to provide compatibility
+ * for legacy applications or limited screen areas.
+ *
+ * @param displayId The logical display id.
+ * @param resources Resources providing compatibility info.
+ * @return The display object, or null if there is no display with the given id.
+ */
+ public Display getCompatibleDisplay(int displayId, Resources resources) {
+ DisplayInfo displayInfo = getDisplayInfo(displayId);
+ if (displayInfo == null) {
+ return null;
+ }
+ return new Display(this, displayId, displayInfo, resources);
+ }
+
+ /**
* Gets information about a logical display without applying any compatibility metrics.
*
* @param displayId The logical display id.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e6f3021..1259de6 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2952,15 +2952,6 @@
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- int timeoutMs, int legacyType) {
- requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
- }
-
- /**
- * Helper function to request a network with a particular legacy type.
- * @hide
- */
- private void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs, int legacyType, Handler handler) {
CallbackHandler cbHandler = new CallbackHandler(handler);
NetworkCapabilities nc = request.networkCapabilities;
@@ -3062,7 +3053,7 @@
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
- requestNetwork(request, networkCallback, timeoutMs, legacyType);
+ requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
}
diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl
index a027d7c..1c541dc4 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.aidl
+++ b/core/java/android/net/ConnectivityMetricsEvent.aidl
@@ -16,5 +16,5 @@
package android.net;
+/** {@hide} */
parcelable ConnectivityMetricsEvent;
-parcelable ConnectivityMetricsEvent.Reference;
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index eaaef7f..6fdc739 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -16,12 +16,10 @@
package android.net;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
/** {@hide} */
-@SystemApi
public final class ConnectivityMetricsEvent implements Parcelable {
/** The time when this event was collected, as returned by System.currentTimeMillis(). */
@@ -67,7 +65,6 @@
return 0;
}
- /** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(timestamp);
@@ -80,51 +77,4 @@
return String.format("ConnectivityMetricsEvent(%tT.%tL, %d, %d): %s",
timestamp, timestamp, componentTag, eventTag, data);
}
-
- /** {@hide} */
- @SystemApi
- public final static class Reference implements Parcelable {
-
- private long mValue;
-
- public Reference(long ref) {
- this.mValue = ref;
- }
-
- /** Implement the Parcelable interface */
- public static final Parcelable.Creator<Reference> CREATOR
- = new Parcelable.Creator<Reference> (){
- public Reference createFromParcel(Parcel source) {
- return new Reference(source.readLong());
- }
-
- public Reference[] newArray(int size) {
- return new Reference[size];
- }
- };
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mValue);
- }
-
- public void readFromParcel(Parcel in) {
- mValue = in.readLong();
- }
-
- public long getValue() {
- return mValue;
- }
-
- public void setValue(long val) {
- mValue = val;
- }
- }
}
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
deleted file mode 100644
index 67b6908..0000000
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net;
-
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/** {@hide} */
-@SystemApi
-public class ConnectivityMetricsLogger {
- private static String TAG = "ConnectivityMetricsLogger";
- private static final boolean DBG = true;
-
- public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
-
- // Component Tags
- public static final int COMPONENT_TAG_CONNECTIVITY = 0;
- public static final int COMPONENT_TAG_BLUETOOTH = 1;
- public static final int COMPONENT_TAG_WIFI = 2;
- public static final int COMPONENT_TAG_TELECOM = 3;
- public static final int COMPONENT_TAG_TELEPHONY = 4;
- public static final int NUMBER_OF_COMPONENTS = 5;
-
- // Event Tag
- public static final int TAG_SKIPPED_EVENTS = -1;
-
- public static final String DATA_KEY_EVENTS_COUNT = "count";
-
- public ConnectivityMetricsLogger() {
- }
-
- /**
- * Log a ConnectivityMetricsEvent.
- *
- * This method keeps track of skipped events when MetricsLoggerService throttles input events.
- * It skips logging when MetricsLoggerService is active. When throttling ends, it logs a
- * meta-event containing the number of events dropped. It is not safe to call this method
- * concurrently from different threads.
- *
- * @param timestamp is the epoch timestamp of the event in ms.
- * @param componentTag is the COMPONENT_* constant the event belongs to.
- * @param eventTag is an event type constant whose meaning is specific to the component tag.
- * @param data is a Parcelable instance representing the event.
- */
- public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) {
- }
-
- /**
- * Retrieve events
- *
- * @param reference of the last event previously returned. The function will return
- * events following it.
- * If 0 then all events will be returned.
- * After the function call it will contain reference of the
- * last returned event.
- * @return events
- */
- public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
- return new ConnectivityMetricsEvent[0];
- }
-
- /**
- * Register PendingIntent which will be sent when new events are ready to be retrieved.
- */
- public boolean register(PendingIntent newEventsIntent) {
- return false;
- }
-
- public boolean unregister(PendingIntent newEventsIntent) {
- return false;
- }
-}
diff --git a/core/java/android/net/IConnectivityMetricsLogger.aidl b/core/java/android/net/IConnectivityMetricsLogger.aidl
deleted file mode 100644
index a83a019..0000000
--- a/core/java/android/net/IConnectivityMetricsLogger.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.app.PendingIntent;
-import android.net.ConnectivityMetricsEvent;
-
-/** {@hide} */
-interface IConnectivityMetricsLogger {
-
- /**
- * @return 0 on success
- * <0 if error happened
- * >0 timestamp after which new events will be accepted
- */
- long logEvent(in ConnectivityMetricsEvent event);
- long logEvents(in ConnectivityMetricsEvent[] events);
-
- /**
- * @param reference of the last event previously returned. The function will return
- * events following it.
- * If 0 then all events will be returned.
- * After the function call it will contain reference of the last event.
- */
- ConnectivityMetricsEvent[] getEvents(inout ConnectivityMetricsEvent.Reference reference);
-
- boolean register(in PendingIntent newEventsIntent);
- void unregister(in PendingIntent newEventsIntent);
-}
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index f249daf..362ea9d 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -18,7 +18,7 @@
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
-import android.net.NetworkScorerAppManager;
+import android.net.NetworkScorerAppData;
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
@@ -135,11 +135,11 @@
/**
* Returns metadata about the active scorer or <code>null</code> if there is no active scorer.
*/
- NetworkScorerAppManager.NetworkScorerAppData getActiveScorer();
+ NetworkScorerAppData getActiveScorer();
/**
* Returns the list of available scorer apps. The list will be empty if there are
* no valid scorers.
*/
- List<NetworkScorerAppManager.NetworkScorerAppData> getAllValidScorers();
+ List<NetworkScorerAppData> getAllValidScorers();
}
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index 8395864..271b0a7 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -1,6 +1,8 @@
package android.net;
+import android.Manifest.permission;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -10,8 +12,10 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -32,6 +36,7 @@
/**
* Constructs a new instance.
* @param handler indicates which thread to use when handling requests. Cannot be {@code null}.
+ * @deprecated use {@link #NetworkRecommendationProvider(Context, Executor)}
*/
public NetworkRecommendationProvider(Handler handler) {
if (handler == null) {
@@ -41,6 +46,17 @@
}
/**
+ * Constructs a new instance.
+ * @param context the current context instance. Cannot be {@code null}.
+ * @param executor used to execute the incoming requests. Cannot be {@code null}.
+ */
+ public NetworkRecommendationProvider(Context context, Executor executor) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(executor);
+ mService = new ServiceWrapper(context, executor);
+ }
+
+ /**
* Invoked when a recommendation has been requested.
*
* @param request a {@link RecommendationRequest} instance containing additional
@@ -130,17 +146,28 @@
* A wrapper around INetworkRecommendationProvider that dispatches to the provided Handler.
*/
private final class ServiceWrapper extends INetworkRecommendationProvider.Stub {
+ private final Context mContext;
+ private final Executor mExecutor;
private final Handler mHandler;
ServiceWrapper(Handler handler) {
mHandler = handler;
+ mExecutor = null;
+ mContext = null;
+ }
+
+ ServiceWrapper(Context context, Executor executor) {
+ mContext = context;
+ mExecutor = executor;
+ mHandler = null;
}
@Override
public void requestRecommendation(final RecommendationRequest request,
final IRemoteCallback callback, final int sequence) throws RemoteException {
+ enforceCallingPermission();
if (VERBOSE) Log.v(TAG, "requestRecommendation(seq=" + sequence + ")");
- mHandler.post(new Runnable() {
+ execute(new Runnable() {
@Override
public void run() {
if (VERBOSE) {
@@ -154,8 +181,9 @@
@Override
public void requestScores(final NetworkKey[] networks) throws RemoteException {
+ enforceCallingPermission();
if (networks != null && networks.length > 0) {
- mHandler.post(new Runnable() {
+ execute(new Runnable() {
@Override
public void run() {
onRequestScores(networks);
@@ -163,5 +191,20 @@
});
}
}
+
+ private void execute(Runnable command) {
+ if (mExecutor != null) {
+ mExecutor.execute(command);
+ } else {
+ mHandler.post(command);
+ }
+ }
+
+ private void enforceCallingPermission() {
+ if (mContext != null) {
+ mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES,
+ "Permission denied.");
+ }
+ }
}
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index edfaee4..815d480 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -18,7 +18,6 @@
import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,7 +25,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.content.Context;
-import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteCallback;
diff --git a/core/java/android/net/NetworkScorerAppManager.aidl b/core/java/android/net/NetworkScorerAppData.aidl
similarity index 91%
rename from core/java/android/net/NetworkScorerAppManager.aidl
rename to core/java/android/net/NetworkScorerAppData.aidl
index d968343..ee7f1d1 100644
--- a/core/java/android/net/NetworkScorerAppManager.aidl
+++ b/core/java/android/net/NetworkScorerAppData.aidl
@@ -16,4 +16,4 @@
package android.net;
-parcelable NetworkScorerAppManager.NetworkScorerAppData;
+parcelable NetworkScorerAppData;
diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java
new file mode 100644
index 0000000..fca0a2e
--- /dev/null
+++ b/core/java/android/net/NetworkScorerAppData.java
@@ -0,0 +1,99 @@
+package android.net;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Holds metadata about a discovered network scorer/recommendation application.
+ *
+ * @hide
+ */
+public final class NetworkScorerAppData implements Parcelable {
+ /** UID of the scorer app. */
+ public final int packageUid;
+ private final ComponentName mRecommendationService;
+ /**
+ * The {@link ComponentName} of the Activity to start before enabling the "connect to open
+ * wifi networks automatically" feature.
+ */
+ private final ComponentName mEnableUseOpenWifiActivity;
+
+ public NetworkScorerAppData(int packageUid, ComponentName recommendationServiceComp,
+ ComponentName enableUseOpenWifiActivity) {
+ this.packageUid = packageUid;
+ this.mRecommendationService = recommendationServiceComp;
+ this.mEnableUseOpenWifiActivity = enableUseOpenWifiActivity;
+ }
+
+ protected NetworkScorerAppData(Parcel in) {
+ packageUid = in.readInt();
+ mRecommendationService = ComponentName.readFromParcel(in);
+ mEnableUseOpenWifiActivity = ComponentName.readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(packageUid);
+ ComponentName.writeToParcel(mRecommendationService, dest);
+ ComponentName.writeToParcel(mEnableUseOpenWifiActivity, dest);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<NetworkScorerAppData> CREATOR =
+ new Creator<NetworkScorerAppData>() {
+ @Override
+ public NetworkScorerAppData createFromParcel(Parcel in) {
+ return new NetworkScorerAppData(in);
+ }
+
+ @Override
+ public NetworkScorerAppData[] newArray(int size) {
+ return new NetworkScorerAppData[size];
+ }
+ };
+
+ public String getRecommendationServicePackageName() {
+ return mRecommendationService.getPackageName();
+ }
+
+ public ComponentName getRecommendationServiceComponent() {
+ return mRecommendationService;
+ }
+
+ @Nullable
+ public ComponentName getEnableUseOpenWifiActivity() {
+ return mEnableUseOpenWifiActivity;
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkScorerAppData{" +
+ "packageUid=" + packageUid +
+ ", mRecommendationService=" + mRecommendationService +
+ ", mEnableUseOpenWifiActivity=" + mEnableUseOpenWifiActivity +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NetworkScorerAppData that = (NetworkScorerAppData) o;
+ return packageUid == that.packageUid &&
+ Objects.equals(mRecommendationService, that.mRecommendationService) &&
+ Objects.equals(mEnableUseOpenWifiActivity, that.mEnableUseOpenWifiActivity);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageUid, mRecommendationService, mEnableUseOpenWifiActivity);
+ }
+}
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
deleted file mode 100644
index bbc1c79..0000000
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import android.Manifest.permission;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.content.pm.ServiceInfo;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Internal class for discovering and managing the network scorer/recommendation application.
- *
- * @hide
- */
-public class NetworkScorerAppManager {
- private static final String TAG = "NetworkScorerAppManager";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- private final Context mContext;
-
- public NetworkScorerAppManager(Context context) {
- mContext = context;
- }
-
- /**
- * Holds metadata about a discovered network scorer/recommendation application.
- */
- public static final class NetworkScorerAppData implements Parcelable {
- /** UID of the scorer app. */
- public final int packageUid;
- private final ComponentName mRecommendationService;
- /**
- * The {@link ComponentName} of the Activity to start before enabling the "connect to open
- * wifi networks automatically" feature.
- */
- private final ComponentName mEnableUseOpenWifiActivity;
-
- public NetworkScorerAppData(int packageUid, ComponentName recommendationServiceComp,
- ComponentName enableUseOpenWifiActivity) {
- this.packageUid = packageUid;
- this.mRecommendationService = recommendationServiceComp;
- this.mEnableUseOpenWifiActivity = enableUseOpenWifiActivity;
- }
-
- protected NetworkScorerAppData(Parcel in) {
- packageUid = in.readInt();
- mRecommendationService = ComponentName.readFromParcel(in);
- mEnableUseOpenWifiActivity = ComponentName.readFromParcel(in);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(packageUid);
- ComponentName.writeToParcel(mRecommendationService, dest);
- ComponentName.writeToParcel(mEnableUseOpenWifiActivity, dest);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator<NetworkScorerAppData> CREATOR =
- new Creator<NetworkScorerAppData>() {
- @Override
- public NetworkScorerAppData createFromParcel(Parcel in) {
- return new NetworkScorerAppData(in);
- }
-
- @Override
- public NetworkScorerAppData[] newArray(int size) {
- return new NetworkScorerAppData[size];
- }
- };
-
- public String getRecommendationServicePackageName() {
- return mRecommendationService.getPackageName();
- }
-
- public ComponentName getRecommendationServiceComponent() {
- return mRecommendationService;
- }
-
- @Nullable public ComponentName getEnableUseOpenWifiActivity() {
- return mEnableUseOpenWifiActivity;
- }
-
- @Override
- public String toString() {
- return "NetworkScorerAppData{" +
- "packageUid=" + packageUid +
- ", mRecommendationService=" + mRecommendationService +
- ", mEnableUseOpenWifiActivity=" + mEnableUseOpenWifiActivity +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- NetworkScorerAppData that = (NetworkScorerAppData) o;
- return packageUid == that.packageUid &&
- Objects.equals(mRecommendationService, that.mRecommendationService) &&
- Objects.equals(mEnableUseOpenWifiActivity, that.mEnableUseOpenWifiActivity);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(packageUid, mRecommendationService, mEnableUseOpenWifiActivity);
- }
- }
-
- /**
- * Returns the list of available scorer apps. The list will be empty if there are
- * no valid scorers.
- */
- public List<NetworkScorerAppData> getAllValidScorers() {
- return Collections.emptyList();
- }
-
- /**
- * @return A {@link NetworkScorerAppData} instance containing information about the
- * best configured network recommendation provider installed or {@code null}
- * if none of the configured packages can recommend networks.
- *
- * <p>A network recommendation provider is any application which:
- * <ul>
- * <li>Is listed in the <code>config_networkRecommendationPackageNames</code> config.
- * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
- * <li>Includes a Service for {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS}.
- * </ul>
- */
- public NetworkScorerAppData getNetworkRecommendationProviderData() {
- // Network recommendation apps can only run as the primary user right now.
- // http://b/23422763
- if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
- return null;
- }
-
- final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
- if (potentialPkgs.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "No Network Recommendation Providers specified.");
- }
- return null;
- }
-
- for (int i = 0; i < potentialPkgs.size(); i++) {
- final String potentialPkg = potentialPkgs.get(i);
-
- // Look for the recommendation service class and required receiver.
- final ServiceInfo serviceInfo = findRecommendationService(potentialPkg);
- if (serviceInfo != null) {
- final ComponentName serviceComponentName =
- new ComponentName(potentialPkg, serviceInfo.name);
- final ComponentName useOpenWifiNetworksActivity =
- findUseOpenWifiNetworksActivity(serviceInfo);
- return new NetworkScorerAppData(serviceInfo.applicationInfo.uid,
- serviceComponentName, useOpenWifiNetworksActivity);
- } else {
- if (DEBUG) {
- Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
- }
- }
- }
-
- // None of the configured packages are valid.
- return null;
- }
-
- @Nullable private ComponentName findUseOpenWifiNetworksActivity(ServiceInfo serviceInfo) {
- if (serviceInfo.metaData == null) {
- if (DEBUG) {
- Log.d(TAG, "No metadata found on recommendation service.");
- }
- return null;
- }
- final String useOpenWifiPackage = serviceInfo.metaData
- .getString(NetworkScoreManager.USE_OPEN_WIFI_PACKAGE_META_DATA);
- if (TextUtils.isEmpty(useOpenWifiPackage)) {
- if (DEBUG) {
- Log.d(TAG, "No use_open_wifi_package metadata found.");
- }
- return null;
- }
- final Intent enableUseOpenWifiIntent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE)
- .setPackage(useOpenWifiPackage);
- final ResolveInfo resolveActivityInfo = mContext.getPackageManager()
- .resolveActivity(enableUseOpenWifiIntent, 0 /* flags */);
- if (VERBOSE) {
- Log.d(TAG, "Resolved " + enableUseOpenWifiIntent + " to " + serviceInfo);
- }
-
- if (resolveActivityInfo != null && resolveActivityInfo.activityInfo != null) {
- return resolveActivityInfo.activityInfo.getComponentName();
- }
-
- return null;
- }
-
- /**
- * @return A priority order list of package names that have been granted the
- * permission needed for them to act as a network recommendation provider.
- * The packages in the returned list may not contain the other required
- * network recommendation provider components so additional checks are required
- * before making a package the network recommendation provider.
- */
- public List<String> getPotentialRecommendationProviderPackages() {
- final String[] packageArray = mContext.getResources().getStringArray(
- R.array.config_networkRecommendationPackageNames);
- if (packageArray == null || packageArray.length == 0) {
- if (DEBUG) {
- Log.d(TAG, "No Network Recommendation Providers specified.");
- }
- return Collections.emptyList();
- }
-
- if (VERBOSE) {
- Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
- }
-
- List<String> packages = new ArrayList<>();
- final PackageManager pm = mContext.getPackageManager();
- for (String potentialPkg : packageArray) {
- if (pm.checkPermission(permission.SCORE_NETWORKS, potentialPkg)
- == PackageManager.PERMISSION_GRANTED) {
- packages.add(potentialPkg);
- } else {
- if (DEBUG) {
- Log.d(TAG, potentialPkg + " has not been granted " + permission.SCORE_NETWORKS
- + ", skipping.");
- }
- }
- }
-
- return packages;
- }
-
- @Nullable private ServiceInfo findRecommendationService(String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- final int resolveFlags = PackageManager.GET_META_DATA;
- final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
- serviceIntent.setPackage(packageName);
- final ResolveInfo resolveServiceInfo =
- pm.resolveService(serviceIntent, resolveFlags);
-
- if (VERBOSE) {
- Log.d(TAG, "Resolved " + serviceIntent + " to " + resolveServiceInfo);
- }
-
- if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
- return resolveServiceInfo.serviceInfo;
- }
-
- if (VERBOSE) {
- Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
- }
- return null;
- }
-
- /**
- * Get the application to use for scoring networks.
- *
- * @return the scorer app info or null if scoring is disabled (including if no scorer was ever
- * selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
- * it was disabled or uninstalled).
- */
- @Nullable
- public NetworkScorerAppData getActiveScorer() {
- if (isNetworkRecommendationsDisabled()) {
- // If recommendations are disabled then there can't be an active scorer.
- return null;
- }
-
- // Otherwise return the recommendation provider (which may be null).
- return getNetworkRecommendationProviderData();
- }
-
- /**
- * Set the specified package as the default scorer application.
- *
- * <p>The caller must have permission to write to {@link android.provider.Settings.Global}.
- *
- * @param packageName the packageName of the new scorer to use. If null, scoring will be
- * disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
- * @return true if the scorer was changed, or false if the package is not a valid scorer or
- * a valid network recommendation provider exists.
- * @deprecated Scorers are now selected from a configured list.
- */
- @Deprecated
- public boolean setActiveScorer(String packageName) {
- return false;
- }
-
- private boolean isNetworkRecommendationsDisabled() {
- final ContentResolver cr = mContext.getContentResolver();
- // A value of 1 indicates enabled.
- return Settings.Global.getInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) != 1;
- }
-}
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 2715af0..4c0f418 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -506,6 +506,6 @@
if (certificateDate == null) {
return "";
}
- return DateFormat.getDateFormat(context).format(certificateDate);
+ return DateFormat.getMediumDateFormat(context).format(certificateDate);
}
}
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index 258d8e1..c2795a2a 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -17,7 +17,6 @@
package android.net.metrics;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -36,7 +35,6 @@
* the APF program in place with a new APF program.
* {@hide}
*/
-@SystemApi
public final class ApfProgramEvent implements Parcelable {
// Bitflag constants describing what an Apf program filters.
@@ -55,7 +53,6 @@
public final int programLength; // Length of the APF program in bytes
public final int flags; // Bitfield compound of FLAG_* constants
- /** {@hide} */
public ApfProgramEvent(
long lifetime, int filteredRas, int currentRas, int programLength, @Flags int flags) {
this.lifetime = lifetime;
@@ -105,7 +102,6 @@
}
};
- /** {@hide} */
public static @Flags int flagsFor(boolean hasIPv4, boolean multicastFilterOn) {
int bitfield = 0;
if (hasIPv4) {
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index 8451e53..f8d7fa9 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +23,6 @@
* An event logged for an interface with APF capabilities when its IpManager state machine exits.
* {@hide}
*/
-@SystemApi
public final class ApfStats implements Parcelable {
public final long durationMs; // time interval in milliseconds these stastistics covers
@@ -36,7 +34,6 @@
public final int programUpdates; // number of APF program updates
public final int maxProgramSize; // maximum APF program size advertised by hardware
- /** {@hide} */
public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) {
this.durationMs = durationMs;
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 9f0bad7..28cf42f 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.net.NetworkCapabilities;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,7 +24,6 @@
* An event recorded by ConnectivityService when there is a change in the default network.
* {@hide}
*/
-@SystemApi
public final class DefaultNetworkEvent implements Parcelable {
// The ID of the network that has become the new default or NETID_UNSET if none.
public final int netId;
@@ -38,7 +36,6 @@
public final boolean prevIPv4;
public final boolean prevIPv6;
- /** {@hide} */
public DefaultNetworkEvent(int netId, int[] transportTypes,
int prevNetId, boolean prevIPv4, boolean prevIPv6) {
this.netId = netId;
@@ -106,8 +103,4 @@
return new DefaultNetworkEvent[size];
}
};
-
- public static void logEvent(
- int netId, int[] transports, int prevNetId, boolean hadIPv4, boolean hadIPv6) {
- }
}
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 4a9ff05..7e30ab5 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +23,6 @@
* An event recorded when a DhcpClient state machine transitions to a new state.
* {@hide}
*/
-@SystemApi
public final class DhcpClientEvent implements Parcelable {
// Names for recording DhcpClient pseudo-state transitions.
@@ -37,7 +35,6 @@
public final String msg;
public final int durationMs;
- /** {@hide} */
public DhcpClientEvent(String ifName, String msg, int durationMs) {
this.ifName = ifName;
this.msg = msg;
@@ -77,7 +74,4 @@
return new DhcpClientEvent[size];
}
};
-
- public static void logStateEvent(String ifName, String state) {
- }
}
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
index c3abcf7..f34ffdf 100644
--- a/core/java/android/net/metrics/DhcpErrorEvent.java
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -27,7 +26,6 @@
* Event class used to record error events when parsing DHCP response packets.
* {@hide}
*/
-@SystemApi
public final class DhcpErrorEvent implements Parcelable {
public static final int L2_ERROR = 1;
public static final int L3_ERROR = 2;
@@ -50,12 +48,10 @@
public static final int DHCP_INVALID_OPTION_LENGTH = makeErrorCode(DHCP_ERROR, 3);
public static final int DHCP_NO_MSG_TYPE = makeErrorCode(DHCP_ERROR, 4);
public static final int DHCP_UNKNOWN_MSG_TYPE = makeErrorCode(DHCP_ERROR, 5);
- /** {@hide} */
public static final int DHCP_NO_COOKIE = makeErrorCode(DHCP_ERROR, 6);
public static final int BUFFER_UNDERFLOW = makeErrorCode(MISC_ERROR, 1);
public static final int RECEIVE_ERROR = makeErrorCode(MISC_ERROR, 2);
- /** {@hide} */
public static final int PARSING_ERROR = makeErrorCode(MISC_ERROR, 3);
public final String ifName;
@@ -66,7 +62,6 @@
// byte 3: optional code
public final int errorCode;
- /** {@hide} */
public DhcpErrorEvent(String ifName, int errorCode) {
this.ifName = ifName;
this.errorCode = errorCode;
@@ -99,12 +94,6 @@
}
};
- public static void logParseError(String ifName, int errorCode) {
- }
-
- public static void logReceiveError(String ifName) {
- }
-
public static int errorCodeWithOption(int errorCode, int option) {
return (0xFFFF0000 & errorCode) | (0xFF & option);
}
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index 6176b2c..89ae1c2 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +23,6 @@
* A DNS event recorded by NetdEventListenerService.
* {@hide}
*/
-@SystemApi
final public class DnsEvent implements Parcelable {
public final int netId;
@@ -38,7 +36,6 @@
// queries.
public final int[] latenciesMs;
- /** {@hide} */
public DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
this.netId = netId;
this.eventTypes = eventTypes;
@@ -82,8 +79,4 @@
return new DnsEvent[size];
}
};
-
- public static void logEvent(
- int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
- }
}
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index e0a026e..50dda7c 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -17,7 +17,6 @@
package android.net.metrics;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -32,17 +31,15 @@
* when a network disconnects.
* {@hide}
*/
-@SystemApi
public final class IpManagerEvent implements Parcelable {
- public static final int PROVISIONING_OK = 1;
- public static final int PROVISIONING_FAIL = 2;
- public static final int COMPLETE_LIFECYCLE = 3;
- /** @hide */ public static final int ERROR_STARTING_IPV4 = 4;
- /** @hide */ public static final int ERROR_STARTING_IPV6 = 5;
- /** @hide */ public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6;
+ public static final int PROVISIONING_OK = 1;
+ public static final int PROVISIONING_FAIL = 2;
+ public static final int COMPLETE_LIFECYCLE = 3;
+ public static final int ERROR_STARTING_IPV4 = 4;
+ public static final int ERROR_STARTING_IPV6 = 5;
+ public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6;
- /** {@hide} */
@IntDef(value = {
PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE,
ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR,
@@ -54,7 +51,6 @@
public final @EventType int eventType;
public final long durationMs;
- /** {@hide} */
public IpManagerEvent(String ifName, @EventType int eventType, long duration) {
this.ifName = ifName;
this.eventType = eventType;
@@ -90,9 +86,6 @@
}
};
- public static void logEvent(int eventType, String ifName, long durationMs) {
- }
-
@Override
public String toString() {
return String.format("IpManagerEvent(%s, %s, %dms)",
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index ee09e22..d69e806 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -28,7 +27,6 @@
* a neighbor probe result.
* {@hide}
*/
-@SystemApi
public final class IpReachabilityEvent implements Parcelable {
// Event types.
@@ -38,9 +36,9 @@
public static final int NUD_FAILED = 2 << 8;
/** Neighbor unreachable after a forced probe, IP provisioning is also lost. */
public static final int PROVISIONING_LOST = 3 << 8;
- /** {@hide} Neighbor unreachable notification from kernel. */
+ /** Neighbor unreachable notification from kernel. */
public static final int NUD_FAILED_ORGANIC = 4 << 8;
- /** {@hide} Neighbor unreachable notification from kernel, IP provisioning is also lost. */
+ /** Neighbor unreachable notification from kernel, IP provisioning is also lost. */
public static final int PROVISIONING_LOST_ORGANIC = 5 << 8;
public final String ifName;
@@ -51,7 +49,6 @@
// byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor.
public final int eventType;
- /** {@hide} */
public IpReachabilityEvent(String ifName, int eventType) {
this.ifName = ifName;
this.eventType = eventType;
@@ -84,18 +81,8 @@
}
};
- public static void logProbeEvent(String ifName, int nlErrorCode) {
- }
-
- public static void logNudFailed(String ifName) {
- }
-
- public static void logProvisioningLost(String ifName) {
- }
-
/**
* Returns the NUD failure event type code corresponding to the given conditions.
- * {@hide}
*/
public static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
if (isFromProbe) {
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 0667495..4df3bf0 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -17,7 +17,6 @@
package android.net.metrics;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -30,7 +29,6 @@
/**
* {@hide}
*/
-@SystemApi
public final class NetworkEvent implements Parcelable {
public static final int NETWORK_CONNECTED = 1;
@@ -41,16 +39,11 @@
public static final int NETWORK_UNLINGER = 6;
public static final int NETWORK_DISCONNECTED = 7;
- /** {@hide} */
public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8;
- /** {@hide} */
public static final int NETWORK_REVALIDATION_SUCCESS = 9;
- /** {@hide} */
public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
- /** {@hide} */
public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11;
- /** {@hide} */
@IntDef(value = {
NETWORK_CONNECTED,
NETWORK_VALIDATED,
@@ -71,14 +64,12 @@
public final @EventType int eventType;
public final long durationMs;
- /** {@hide} */
public NetworkEvent(int netId, @EventType int eventType, long durationMs) {
this.netId = netId;
this.eventType = eventType;
this.durationMs = durationMs;
}
- /** {@hide} */
public NetworkEvent(int netId, @EventType int eventType) {
this(netId, eventType, 0);
}
@@ -112,15 +103,6 @@
}
};
- public static void logEvent(int netId, int eventType) {
- }
-
- public static void logValidated(int netId, long durationMs) {
- }
-
- public static void logCaptivePortalFound(int netId, long durationMs) {
- }
-
@Override
public String toString() {
return String.format("NetworkEvent(%d, %s, %dms)",
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index 91bd023..3249f80 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,10 +23,8 @@
* An event logged when the APF packet socket receives an RA packet.
* {@hide}
*/
-@SystemApi
public final class RaEvent implements Parcelable {
- /** {@hide} */
public static final long NO_LIFETIME = -1L;
// Lifetime in seconds of options found in a single RA packet.
@@ -39,7 +36,6 @@
public final long rdnssLifetime;
public final long dnsslLifetime;
- /** {@hide} */
public RaEvent(long routerLifetime, long prefixValidLifetime, long prefixPreferredLifetime,
long routeInfoLifetime, long rdnssLifetime, long dnsslLifetime) {
this.routerLifetime = routerLifetime;
@@ -96,7 +92,6 @@
}
};
- /** {@hide} */
public static class Builder {
long routerLifetime = NO_LIFETIME;
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index a724ec1..70c6e84 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -17,7 +17,6 @@
package android.net.metrics;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -31,14 +30,12 @@
* An event recorded by NetworkMonitor when sending a probe for finding captive portals.
* {@hide}
*/
-@SystemApi
public final class ValidationProbeEvent implements Parcelable {
public static final int PROBE_DNS = 0;
public static final int PROBE_HTTP = 1;
public static final int PROBE_HTTPS = 2;
public static final int PROBE_PAC = 3;
- /** {@hide} */
public static final int PROBE_FALLBACK = 4;
public static final int DNS_FAILURE = 0;
@@ -47,7 +44,6 @@
private static final int FIRST_VALIDATION = 1 << 8;
private static final int REVALIDATION = 2 << 8;
- /** {@hide} */
@IntDef(value = {DNS_FAILURE, DNS_SUCCESS})
@Retention(RetentionPolicy.SOURCE)
public @interface ReturnCode {}
@@ -62,7 +58,6 @@
public final int probeType;
public final @ReturnCode int returnCode;
- /** {@hide} */
public ValidationProbeEvent(
int netId, long durationMs, int probeType, @ReturnCode int returnCode) {
this.netId = netId;
@@ -102,24 +97,18 @@
}
};
- /** @hide */
public static int makeProbeType(int probeType, boolean firstValidation) {
return (probeType & 0xff) | (firstValidation ? FIRST_VALIDATION : REVALIDATION);
}
- /** @hide */
public static String getProbeName(int probeType) {
return Decoder.constants.get(probeType & 0xff, "PROBE_???");
}
- /** @hide */
public static String getValidationStage(int probeType) {
return Decoder.constants.get(probeType & 0xff00, "UNKNOWN");
}
- public static void logEvent(int netId, long durationMs, int probeType, int returnCode) {
- }
-
@Override
public String toString() {
return String.format("ValidationProbeEvent(%d, %s:%d %s, %dms)", netId,
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index d299672..63d3e7a 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -18,8 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.LooperProto;
import android.util.Log;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
/**
* Class used to run a message loop for a thread. Threads by default do
@@ -289,6 +291,16 @@
mQueue.dump(pw, prefix + " ");
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long looperToken = proto.start(fieldId);
+ proto.write(LooperProto.THREAD_NAME, mThread.getName());
+ proto.write(LooperProto.THREAD_ID, mThread.getId());
+ proto.write(LooperProto.IDENTITY_HASH_CODE, System.identityHashCode(this));
+ mQueue.writeToProto(proto, LooperProto.QUEUE);
+ proto.end(looperToken);
+ }
+
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 8c75847..d066db1 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,13 +16,15 @@
package android.os;
+import android.os.MessageProto;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
/**
- *
+ *
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
- * extra object field that allow you to not do allocations in many cases.
+ * extra object field that allow you to not do allocations in many cases.
*
* <p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
@@ -31,7 +33,7 @@
*/
public final class Message implements Parcelable {
/**
- * User-defined message code so that the recipient can identify
+ * User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
@@ -43,7 +45,7 @@
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
- public int arg1;
+ public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
@@ -58,7 +60,7 @@
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
- *
+ *
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
@@ -97,13 +99,13 @@
/*package*/ int flags;
/*package*/ long when;
-
+
/*package*/ Bundle data;
-
+
/*package*/ Handler target;
-
+
/*package*/ Runnable callback;
-
+
// sometimes we store linked lists of these things
/*package*/ Message next;
@@ -216,9 +218,9 @@
}
/**
- * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+ * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, and <em>arg2</em> members.
- *
+ *
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
@@ -236,9 +238,9 @@
}
/**
- * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+ * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
- *
+ *
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
@@ -246,7 +248,7 @@
* @param obj The <em>obj</em> value to set.
* @return A Message object from the global pool.
*/
- public static Message obtain(Handler h, int what,
+ public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
@@ -339,7 +341,7 @@
public long getWhen() {
return when;
}
-
+
public void setTarget(Handler target) {
this.target = target;
}
@@ -367,8 +369,8 @@
public Runnable getCallback() {
return callback;
}
-
- /**
+
+ /**
* Obtains a Bundle of arbitrary data associated with this
* event, lazily creating it if necessary. Set this value by calling
* {@link #setData(Bundle)}. Note that when transferring data across
@@ -383,11 +385,11 @@
if (data == null) {
data = new Bundle();
}
-
+
return data;
}
- /**
+ /**
* Like getData(), but does not lazily create the Bundle. A null
* is returned if the Bundle does not already exist. See
* {@link #getData} for further information on this.
@@ -401,7 +403,7 @@
/**
* Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
* as a lower cost way to send a few simple integer values, if you can.
- * @see #getData()
+ * @see #getData()
* @see #peekData()
*/
public void setData(Bundle data) {
@@ -520,6 +522,37 @@
return b.toString();
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long messageToken = proto.start(fieldId);
+ proto.write(MessageProto.WHEN, when);
+
+ if (target != null) {
+ if (callback != null) {
+ proto.write(MessageProto.CALLBACK, callback.getClass().getName());
+ } else {
+ proto.write(MessageProto.WHAT, what);
+ }
+
+ if (arg1 != 0) {
+ proto.write(MessageProto.ARG1, arg1);
+ }
+
+ if (arg2 != 0) {
+ proto.write(MessageProto.ARG2, arg2);
+ }
+
+ if (obj != null) {
+ proto.write(MessageProto.OBJ, obj.toString());
+ }
+
+ proto.write(MessageProto.TARGET, target.getClass().getName());
+ } else {
+ proto.write(MessageProto.BARRIER, arg1);
+ }
+
+ proto.end(messageToken);
+ }
+
public static final Parcelable.Creator<Message> CREATOR
= new Parcelable.Creator<Message>() {
public Message createFromParcel(Parcel source) {
@@ -527,12 +560,12 @@
msg.readFromParcel(source);
return msg;
}
-
+
public Message[] newArray(int size) {
return new Message[size];
}
};
-
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 4f2e968..2a8c52e 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -18,9 +18,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.MessageQueueProto;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -31,7 +33,7 @@
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
- *
+ *
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@@ -770,6 +772,18 @@
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long messageQueueToken = proto.start(fieldId);
+ synchronized (this) {
+ for (Message msg = mMessages; msg != null; msg = msg.next) {
+ msg.writeToProto(proto, MessageQueueProto.MESSAGES);
+ }
+ proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
+ proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
+ }
+ proto.end(messageQueueToken);
+ }
+
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 21c70f9..13a495e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1097,40 +1097,6 @@
}
}
- /** @removed */
- @Deprecated
- public boolean isUserRunningAndLocked() {
- return isUserRunningAndLocked(Process.myUserHandle());
- }
-
- /** @removed */
- @Deprecated
- public boolean isUserRunningAndLocked(UserHandle user) {
- try {
- return ActivityManager.getService().isUserRunning(
- user.getIdentifier(), ActivityManager.FLAG_AND_LOCKED);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /** @removed */
- @Deprecated
- public boolean isUserRunningAndUnlocked() {
- return isUserRunningAndUnlocked(Process.myUserHandle());
- }
-
- /** @removed */
- @Deprecated
- public boolean isUserRunningAndUnlocked(UserHandle user) {
- try {
- return ActivityManager.getService().isUserRunning(
- user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
/**
* Return whether the calling user is running in an "unlocked" state.
* <p>
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index f8da87a..ecec448 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1,6 +1,8 @@
package android.os;
+import android.os.WorkSourceProto;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.util.Arrays;
@@ -296,7 +298,7 @@
break;
}
if (mUids[i] == uid) {
- int diff = mNames[i].compareTo(name);
+ int diff = mNames[i].compareTo(name);
if (diff > 0) {
break;
}
@@ -692,6 +694,20 @@
return result.toString();
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long workSourceToken = proto.start(fieldId);
+ for (int i = 0; i < mNum; i++) {
+ final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
+ proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
+ if (mNames != null) {
+ proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
+ }
+ proto.end(contentProto);
+ }
+ proto.end(workSourceToken);
+ }
+
public static final Parcelable.Creator<WorkSource> CREATOR
= new Parcelable.Creator<WorkSource>() {
public WorkSource createFromParcel(Parcel in) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 40de928..b55a349 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6905,6 +6905,12 @@
public static final String PACKAGE_VERIFIER_STATE = "package_verifier_state";
/**
+ * Specifies additional package name for broadcasting the CMAS messages.
+ * @hide
+ */
+ public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 91c668e..a241d1f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -23,7 +23,6 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.ArraySet;
import android.view.autofill.AutoFillId;
import android.view.autofill.AutoFillManager;
import android.widget.RemoteViews;
@@ -69,18 +68,19 @@
*
* <p>If the user does not have any data associated with this {@link android.app.Activity} but
* the service wants to offer the user the option to save the data that was entered, then the
- * service could populate the response with {@code savableIds} instead of {@link Dataset}s:
+ * service could populate the response with a {@link SaveInfo} instead of {@link Dataset}s:
*
* <pre class="prettyprint">
* new FillResponse.Builder()
- * .addSavableFields(id1, id2)
+ * .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_CREDENTIALS)
+ * .addSavableFields(id1, id2))
* .build();
* </pre>
*
* <p>Similarly, there might be cases where the user data on the service is enough to populate some
* fields but not all, and the service would still be interested on saving the other fields. In this
- * scenario, the service could populate the response with both {@link Dataset}s and {@code
- * savableIds}:
+ * scenario, the service could populate the response with both {@link Dataset}s and
+ * {@link SaveInfo}:
*
* <pre class="prettyprint">
* new FillResponse.Builder()
@@ -90,7 +90,8 @@
* .setTextFieldValue(id3, "742 Evergreen Terrace") // street
* .setTextFieldValue(id4, "Springfield") // city
* .build())
- * .addSavableFields(id5, id6) // state and zipcode
+ * .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_ADDRESS)
+ * .addSavableFields(id5, id6)) // state and zipcode
* .build();
*
* </pre>
@@ -140,9 +141,11 @@
* </pre>
*
* <p>The service could require user authentication at the {@link FillResponse} or the
- * {@link Dataset} level, prior to auto-filling an activity - see {@link FillResponse.Builder
- * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}.
- * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
+ * {@link Dataset} level, prior to auto-filling an activity - see
+ * {@link FillResponse.Builder#setAuthentication(IntentSender, RemoteViews)} and
+ * {@link Dataset.Builder#setAuthentication(IntentSender)}.
+ *
+ * <p>It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
* which would allow you to provide a dataset presentation views with labels and if the user
* chooses one of them challenge the user to authenticate. For example, if the user has a
* home and a work address the Home and Work labels could be stored unencrypted as they don't
@@ -158,14 +161,45 @@
public final class FillResponse implements Parcelable {
private final ArrayList<Dataset> mDatasets;
- private final ArraySet<AutoFillId> mSavableIds;
+ private final SaveInfo mSaveInfo;
private final Bundle mExtras;
private final RemoteViews mPresentation;
private final IntentSender mAuthentication;
private FillResponse(@NonNull Builder builder) {
mDatasets = builder.mDatasets;
- mSavableIds = builder.mSavableIds;
+
+ if (false) {
+ // TODO(b/33197203, 35727295): this is how mSaveInfo will be set once we don't support
+ // FillResponse.setSavableIds()
+ mSaveInfo = builder.mSaveInfo;
+ if (mSaveInfo != null) {
+ mSaveInfo.addSavableIds(mDatasets);
+ if (mSaveInfo.getSavableIds() == null) {
+ throw new IllegalArgumentException(
+ "need to provide at least one savable id on SaveInfo");
+ }
+ }
+ } else {
+ // Temporary workaround to support FillResponse.setSavableIds()
+ SaveInfo saveInfo = builder.mSaveInfoBuilder != null ? builder.mSaveInfoBuilder.build()
+ : builder.mSaveInfo;
+
+ // Handle the the case where service didn't call setSavableIds() because it would
+ // contain just the ids from the datasets.
+ if (saveInfo == null && mDatasets != null) {
+ saveInfo = new SaveInfo.Builder(SaveInfo.SAVE_UI_TYPE_GENERIC).build();
+ }
+ if (saveInfo != null) {
+ saveInfo.addSavableIds(mDatasets);
+ if (saveInfo.getSavableIds() == null) {
+ throw new IllegalArgumentException(
+ "need to provide at least one savable id on SaveInfo");
+ }
+ }
+ mSaveInfo = saveInfo;
+ }
+
mExtras = builder.mExtras;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
@@ -182,8 +216,8 @@
}
/** @hide */
- public @Nullable ArraySet<AutoFillId> getSavableIds() {
- return mSavableIds;
+ public @Nullable SaveInfo getSaveInfo() {
+ return mSaveInfo;
}
/** @hide */
@@ -202,7 +236,10 @@
*/
public static final class Builder {
private ArrayList<Dataset> mDatasets;
- private ArraySet<AutoFillId> mSavableIds;
+ // TODO(b/33197203, 35727295): temporary builder use by deprecated addSavableIds() method,
+ // should be removed once that method is gone
+ private SaveInfo.Builder mSaveInfoBuilder;
+ private SaveInfo mSaveInfo;
private Bundle mExtras;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
@@ -276,41 +313,37 @@
if (!mDatasets.add(dataset)) {
return this;
}
- if (dataset.getFieldIds() != null) {
- final int fieldCount = dataset.getFieldIds().size();
- for (int i = 0; i < fieldCount; i++) {
- final AutoFillId id = dataset.getFieldIds().get(i);
- if (mSavableIds == null) {
- mSavableIds = new ArraySet<>();
- }
- mSavableIds.add(id);
- }
+ return this;
+ }
+
+ /** @hide */
+ // TODO(b/33197203, 35727295): remove when not used by clients
+ public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
+ throwIfDestroyed();
+ if (mSaveInfo != null) {
+ throw new IllegalStateException("setSaveInfo() already called");
}
+ if (mSaveInfoBuilder == null) {
+ mSaveInfoBuilder = new SaveInfo.Builder(SaveInfo.SAVE_UI_TYPE_GENERIC);
+ }
+ mSaveInfoBuilder.addSavableIds(ids);
+
return this;
}
/**
- * Adds ids of additional fields that the service would be interested to save (through
- * {@link AutoFillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, SaveCallback)})
- * but were not indirectly set through {@link #addDataset(Dataset)}.
+ * Sets the {@link SaveInfo} associated with this response.
*
- * @param ids The savable ids.
+ * <p>See {@link FillResponse} for more info.
+ *
* @return This builder.
- *
- * @see FillResponse
*/
- public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
+ public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
throwIfDestroyed();
- if (ids == null) {
- return this;
+ if (mSaveInfoBuilder != null) {
+ throw new IllegalStateException("addSavableFields() already called");
}
- for (AutoFillId id : ids) {
- if (mSavableIds == null) {
- mSavableIds = new ArraySet<>();
- }
- mSavableIds.add(id);
- }
+ mSaveInfo = saveInfo;
return this;
}
@@ -340,9 +373,11 @@
*/
public FillResponse build() {
throwIfDestroyed();
- if (mAuthentication == null && mDatasets == null && mSavableIds == null) {
- throw new IllegalArgumentException("need to provide at least one"
- + " data set or savable ids or an authentication with a presentation");
+
+ if (mAuthentication == null && mDatasets == null && mSaveInfoBuilder == null
+ && mSaveInfo == null) {
+ throw new IllegalArgumentException("need to provide at least one DataSet or a "
+ + "SaveInfo or an authentication with a presentation");
}
mDestroyed = true;
return new FillResponse(this);
@@ -361,9 +396,10 @@
@Override
public String toString() {
if (!DEBUG) return super.toString();
+
return new StringBuilder(
"FillResponse: [datasets=").append(mDatasets)
- .append(", savableIds=").append(mSavableIds)
+ .append(", saveInfo=").append(mSaveInfo)
.append(", hasExtras=").append(mExtras != null)
.append(", hasPresentation=").append(mPresentation != null)
.append(", hasAuthentication=").append(mAuthentication != null)
@@ -382,7 +418,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedArrayList(mDatasets, flags);
- parcel.writeTypedArraySet(mSavableIds, flags);
+ parcel.writeParcelable(mSaveInfo, flags);
parcel.writeParcelable(mExtras, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
@@ -401,11 +437,7 @@
for (int i = 0; i < datasetCount; i++) {
builder.addDataset(datasets.get(i));
}
- final ArraySet<AutoFillId> fillIds = parcel.readTypedArraySet(null);
- final int fillIdCount = (fillIds != null) ? fillIds.size() : 0;
- for (int i = 0; i < fillIdCount; i++) {
- builder.addSavableFields(fillIds.valueAt(i));
- }
+ builder.setSaveInfo(parcel.readParcelable(null));
builder.setExtras(parcel.readParcelable(null));
builder.setAuthentication(parcel.readParcelable(null),
parcel.readParcelable(null));
diff --git a/core/java/android/net/NetworkScorerAppManager.aidl b/core/java/android/service/autofill/SaveInfo.aidl
similarity index 88%
copy from core/java/android/net/NetworkScorerAppManager.aidl
copy to core/java/android/service/autofill/SaveInfo.aidl
index d968343..8cda608 100644
--- a/core/java/android/net/NetworkScorerAppManager.aidl
+++ b/core/java/android/service/autofill/SaveInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.service.autofill;
-parcelable NetworkScorerAppManager.NetworkScorerAppData;
+parcelable SaveInfo;
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
new file mode 100644
index 0000000..148bd24
--- /dev/null
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 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.service.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.view.autofill.AutoFillId;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Information used to indicate that a service is interested on saving the user-inputed data for
+ * future use.
+ *
+ * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}.
+ *
+ * <p>A {@link SaveInfo} must define the type it represents, and contain at least one
+ * {@code savableId}. A {@code savableId} is the {@link AutoFillId} of a view the service is
+ * interested to save in a {@code onSaveRequest()}; the ids of all {@link Dataset} present in the
+ * {@link FillResponse} associated with this {@link SaveInfo} are already marked as savable,
+ * but additional ids can be added through {@link Builder#addSavableIds(AutoFillId...)}.
+ *
+ * <p>See {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+ * SaveCallback)} and {@link FillResponse} for more info.
+ */
+public final class SaveInfo implements Parcelable {
+
+ /**
+ * Type used on when the service can save the contents of an activity, but cannot describe what
+ * the content is for.
+ */
+ public static final int SAVE_UI_TYPE_GENERIC = 0;
+
+ /**
+ * Type used when the {@link FillResponse} represents user credentials (such as username and
+ * password).
+ */
+ public static final int SAVE_UI_TYPE_CREDENTIALS = 1;
+
+ /**
+ * Type used on when the {@link FillResponse} represents a physical address (such as street,
+ * city, state, etc).
+ */
+ public static final int SAVE_UI_TYPE_ADDRESS = 2;
+
+ /**
+ * Type used when the {@link FillResponse} represents a payment (such as credit card number
+ * and expiration date).
+ */
+ public static final int SAVE_UI_TYPE_PAYMENT = 3;
+
+ private final @SaveUiType int mType;
+ private ArraySet<AutoFillId> mSavableIds;
+
+ /** @hide */
+ @IntDef({
+ SAVE_UI_TYPE_GENERIC,
+ SAVE_UI_TYPE_CREDENTIALS,
+ SAVE_UI_TYPE_ADDRESS,
+ SAVE_UI_TYPE_PAYMENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SaveUiType {
+ }
+
+ private SaveInfo(Builder builder) {
+ mType = builder.mType;
+ mSavableIds = builder.mSavableIds;
+ }
+
+ /** @hide */
+ public @Nullable ArraySet<AutoFillId> getSavableIds() {
+ return mSavableIds;
+ }
+
+ /** @hide */
+ public void addSavableIds(@Nullable ArrayList<Dataset> datasets) {
+ if (datasets != null) {
+ for (Dataset dataset : datasets) {
+ final ArrayList<AutoFillId> ids = dataset.getFieldIds();
+ if (ids != null) {
+ final int fieldCount = ids.size();
+ for (int i = 0; i < fieldCount; i++) {
+ final AutoFillId id = ids.get(i);
+ if (mSavableIds == null) {
+ mSavableIds = new ArraySet<>();
+ }
+ mSavableIds.add(id);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * A builder for {@link SaveInfo} objects.
+ */
+ public static final class Builder {
+
+ private final @SaveUiType int mType;
+ private ArraySet<AutoFillId> mSavableIds;
+ private boolean mDestroyed;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param type the type of information the associated {@link FillResponse} represents. Must
+ * be {@link SaveInfo#SAVE_UI_TYPE_GENERIC}, {@link SaveInfo#SAVE_UI_TYPE_CREDENTIALS},
+ * {@link SaveInfo#SAVE_UI_TYPE_ADDRESS}, or {@link SaveInfo#SAVE_UI_TYPE_PAYMENT};
+ * otherwise it will assume {@link SaveInfo#SAVE_UI_TYPE_GENERIC}.
+ */
+ public Builder(@SaveUiType int type) {
+ switch (type) {
+ case SAVE_UI_TYPE_CREDENTIALS:
+ case SAVE_UI_TYPE_ADDRESS:
+ case SAVE_UI_TYPE_PAYMENT:
+ mType = type;
+ break;
+ default:
+ mType = SAVE_UI_TYPE_GENERIC;
+ }
+ }
+
+ /**
+ * Adds ids of additional views the service would be interested to save, but were not
+ * indirectly set through {@link FillResponse.Builder#addDataset(Dataset)}.
+ *
+ * @param ids The savable ids.
+ * @return This builder.
+ *
+ * @see FillResponse
+ */
+ public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
+ throwIfDestroyed();
+
+ if (ids == null) {
+ return this;
+ }
+ for (AutoFillId id : ids) {
+ if (mSavableIds == null) {
+ mSavableIds = new ArraySet<>();
+ }
+ mSavableIds.add(id);
+ }
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SaveInfo} instance.
+ */
+ public SaveInfo build() {
+ throwIfDestroyed();
+ mDestroyed = true;
+ return new SaveInfo(this);
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Already called #build()");
+ }
+ }
+
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return new StringBuilder("SaveInfo: [type=").append(mType)
+ .append(", savableIds=").append(mSavableIds)
+ .append("]").toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeTypedArraySet(mSavableIds, flags);
+ }
+
+ public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() {
+ @Override
+ public SaveInfo createFromParcel(Parcel parcel) {
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final Builder builder = new Builder(parcel.readInt());
+ final ArraySet<AutoFillId> savableIds = parcel.readTypedArraySet(null);
+ final int savableIdsCount = (savableIds != null) ? savableIds.size() : 0;
+ for (int i = 0; i < savableIdsCount; i++) {
+ builder.addSavableIds(savableIds.valueAt(i));
+ }
+
+ return builder.build();
+ }
+
+ @Override
+ public SaveInfo[] newArray(int size) {
+ return new SaveInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 5f7ff67..8a83b7a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -547,20 +547,20 @@
* Inform the notification manager about snoozing a specific notification.
* <p>
* Use this if your listener has a user interface that allows the user to snooze a notification
- * until a given time. It should be called after the user snoozes a single notification using
+ * for a time. It should be called after the user snoozes a single notification using
* your UI; upon being informed, the notification manager will actually remove the notification
* and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
* snoozing period expires, you will get a
* {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
* notification.
* @param key The key of the notification to snooze
- * @param snoozeUntil A time in the future, in milliseconds.
+ * @param durationMs A duration to snooze the notification for, in milliseconds.
*/
- public final void snoozeNotification(String key, long snoozeUntil) {
+ public final void snoozeNotification(String key, long durationMs) {
if (!isBound()) return;
try {
getNotificationInterface().snoozeNotificationUntilFromListener(
- mWrapper, key, snoozeUntil);
+ mWrapper, key, durationMs);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 356804e..80ec03e 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -189,7 +189,9 @@
// TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
String[] availableLanguages = {
"as",
+ "bg",
"bn",
+ "cu",
"cy",
"da",
"de-1901", "de-1996", "de-CH-1901",
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 83b6a52..7ec7ba7 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.content.res.CompatibilityInfo;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -71,7 +72,8 @@
private final String mAddress;
private final int mOwnerUid;
private final String mOwnerPackageName;
- private final DisplayAdjustments mDisplayAdjustments;
+ private final Resources mResources;
+ private DisplayAdjustments mDisplayAdjustments;
private DisplayInfo mDisplayInfo; // never null
private boolean mIsValid;
@@ -355,19 +357,39 @@
/**
* Internal method to create a display.
+ * The display created with this method will have a static {@link DisplayAdjustments} applied.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
* or {@link android.hardware.display.DisplayManager#getDisplay}
* to get a display object.
*
* @hide
*/
- public Display(DisplayManagerGlobal global,
- int displayId, DisplayInfo displayInfo /*not null*/,
+ public Display(DisplayManagerGlobal global, int displayId, /*@NotNull*/ DisplayInfo displayInfo,
DisplayAdjustments daj) {
+ this(global, displayId, displayInfo, daj, null /*res*/);
+ }
+
+ /**
+ * Internal method to create a display.
+ * The display created with this method will be adjusted based on the adjustments in the
+ * supplied {@link Resources}.
+ *
+ * @hide
+ */
+ public Display(DisplayManagerGlobal global, int displayId, /*@NotNull*/ DisplayInfo displayInfo,
+ Resources res) {
+ this(global, displayId, displayInfo, null /*daj*/, res);
+ }
+
+ private Display(DisplayManagerGlobal global, int displayId,
+ /*@NotNull*/ DisplayInfo displayInfo, DisplayAdjustments daj, Resources res) {
mGlobal = global;
mDisplayId = displayId;
mDisplayInfo = displayInfo;
- mDisplayAdjustments = new DisplayAdjustments(daj);
+ mResources = res;
+ mDisplayAdjustments = mResources != null
+ ? new DisplayAdjustments(mResources.getConfiguration())
+ : daj != null ? new DisplayAdjustments(daj) : null;
mIsValid = true;
// Cache properties that cannot change as long as the display is valid.
@@ -512,6 +534,13 @@
* @hide
*/
public DisplayAdjustments getDisplayAdjustments() {
+ if (mResources != null) {
+ final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustements)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
+ }
+ }
+
return mDisplayAdjustments;
}
@@ -562,7 +591,7 @@
public void getSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
+ mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
outSize.x = mTempMetrics.widthPixels;
outSize.y = mTempMetrics.heightPixels;
}
@@ -577,7 +606,7 @@
public void getRectSize(Rect outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
+ mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
outSize.set(0, 0, mTempMetrics.widthPixels, mTempMetrics.heightPixels);
}
}
@@ -908,7 +937,7 @@
public void getMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(outMetrics, mDisplayAdjustments);
+ mDisplayInfo.getAppMetrics(outMetrics, getDisplayAdjustments());
}
}
@@ -1017,7 +1046,7 @@
long now = SystemClock.uptimeMillis();
if (now > mLastCachedAppSizeUpdate + CACHED_APP_SIZE_DURATION_MILLIS) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
+ mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
mCachedAppWidthCompat = mTempMetrics.widthPixels;
mCachedAppHeightCompat = mTempMetrics.heightPixels;
mLastCachedAppSizeUpdate = now;
@@ -1029,7 +1058,7 @@
public String toString() {
synchronized (this) {
updateDisplayInfoLocked();
- mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
+ mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
return "Display id " + mDisplayId + ": " + mDisplayInfo
+ ", " + mTempMetrics + ", isValid=" + mIsValid;
}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 7fde8a6..ad06141 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -117,7 +117,7 @@
public View findNextKeyboardNavigationCluster(
@NonNull View root,
@Nullable View currentCluster,
- int direction) {
+ @View.FocusDirection int direction) {
View next = null;
final ArrayList<View> clusters = mTempList;
@@ -206,7 +206,7 @@
View root,
View currentCluster,
List<View> clusters,
- int direction) {
+ @View.FocusDirection int direction) {
final int count = clusters.size();
switch (direction) {
@@ -732,27 +732,17 @@
getRect(first, mFirstRect);
getRect(second, mSecondRect);
- if (mFirstRect.top < mSecondRect.top) {
- return -1;
- } else if (mFirstRect.top > mSecondRect.top) {
- return 1;
- } else if (mFirstRect.left < mSecondRect.left) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.left > mSecondRect.left) {
- return mIsLayoutRtl ? -1 : 1;
- } else if (mFirstRect.bottom < mSecondRect.bottom) {
- return -1;
- } else if (mFirstRect.bottom > mSecondRect.bottom) {
- return 1;
- } else if (mFirstRect.right < mSecondRect.right) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.right > mSecondRect.right) {
- return mIsLayoutRtl ? -1 : 1;
+ boolean overlapsVertically = (mFirstRect.top < mSecondRect.top
+ && mFirstRect.bottom > mSecondRect.top)
+ || (mFirstRect.top > mSecondRect.top
+ && mFirstRect.top < mSecondRect.bottom);
+ boolean alignedVertically = (mFirstRect.left > mSecondRect.left)
+ == (mFirstRect.right < mSecondRect.right);
+ if (overlapsVertically && !alignedVertically) {
+ int rtl = mIsLayoutRtl ? -1 : 1;
+ return rtl * (mFirstRect.left - mSecondRect.left);
} else {
- // The view are distinct but completely coincident so we consider
- // them equal for our purposes. Since the sort is stable, this
- // means that the views will retain their layout order relative to one another.
- return 0;
+ return mFirstRect.top - mSecondRect.top;
}
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 92f0e8f..358a2d1 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -132,6 +132,26 @@
*/
public static final int FIRST_DRAW_FRAME = 9;
+ /**
+ * Metric identifier for the timestamp of the intended vsync for this frame.
+ * <p>
+ * The intended start point for the frame. If this value is different from
+ * {@link #VSYNC_TIMESTAMP}, there was work occurring on the UI thread that
+ * prevented it from responding to the vsync signal in a timely fashion.
+ * </p>
+ */
+ public static final int INTENDED_VSYNC_TIMESTAMP = 10;
+
+ /**
+ * Metric identifier for the timestamp of the actual vsync for this frame.
+ * <p>
+ * The time value that was used in all the vsync listeners and drawing for
+ * the frame (Choreographer frame callbacks, animations,
+ * {@link View#getDrawingTime()}, etc…)
+ * </p>
+ */
+ public static final int VSYNC_TIMESTAMP = 11;
+
private static final int FRAME_INFO_FLAG_FIRST_DRAW = 1 << 0;
/**
@@ -151,6 +171,8 @@
SWAP_BUFFERS_DURATION,
TOTAL_DURATION,
FIRST_DRAW_FRAME,
+ INTENDED_VSYNC_TIMESTAMP,
+ VSYNC_TIMESTAMP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Metric {}
@@ -261,7 +283,7 @@
* @return the value of the metric or -1 if it is not available.
*/
public long getMetric(@Metric int id) {
- if (id < UNKNOWN_DELAY_DURATION || id > FIRST_DRAW_FRAME) {
+ if (id < UNKNOWN_DELAY_DURATION || id > VSYNC_TIMESTAMP) {
return -1;
}
@@ -271,6 +293,10 @@
if (id == FIRST_DRAW_FRAME) {
return (mTimingData[Index.FLAGS] & FRAME_INFO_FLAG_FIRST_DRAW) != 0 ? 1 : 0;
+ } else if (id == INTENDED_VSYNC_TIMESTAMP) {
+ return mTimingData[Index.INTENDED_VSYNC];
+ } else if (id == VSYNC_TIMESTAMP) {
+ return mTimingData[Index.VSYNC];
}
int durationsIdx = 2 * id;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf8da17..b718696 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -508,9 +508,9 @@
nativeSetAlpha(mNativeObject, alpha);
}
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
checkNotReleased();
- nativeSetMatrix(mNativeObject, dsdx, dtdx, dsdy, dtdy);
+ nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy);
}
public void setWindowCrop(Rect crop) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 66c394f..a880842 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9433,7 +9433,8 @@
* @return The nearest keyboard navigation cluster in the specified direction, or null if none
* can be found
*/
- public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
+ public View keyboardNavigationClusterSearch(View currentCluster,
+ @FocusDirection int direction) {
if (isKeyboardNavigationCluster()) {
currentCluster = this;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c81e938..b79f22f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -72,6 +72,7 @@
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
+import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.accessibility.AccessibilityEvent;
@@ -1047,8 +1048,8 @@
// Get new instance of display based on current display adjustments. It may be updated later
// if moving between the displays also involved a configuration change.
- final DisplayAdjustments displayAdjustments = mView.getResources().getDisplayAdjustments();
- mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId, displayAdjustments);
+ mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(displayId,
+ mView.getResources());
mAttachInfo.mDisplayState = mDisplay.getState();
// Internal state updated, now notify the view hierarchy.
mView.dispatchMovedToDisplay(mDisplay);
@@ -3384,7 +3385,7 @@
if (force || mLastConfiguration.diff(config) != 0) {
// Update the display with new DisplayAdjustments.
mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
- mDisplay.getDisplayId(), localResources.getDisplayAdjustments());
+ mDisplay.getDisplayId(), localResources);
final int lastLayoutDirection = mLastConfiguration.getLayoutDirection();
final int currentLayoutDirection = config.getLayoutDirection();
@@ -5975,7 +5976,8 @@
* {@inheritDoc}
*/
@Override
- public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
+ public View keyboardNavigationClusterSearch(View currentCluster,
+ @FocusDirection int direction) {
checkThread();
return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
mView, currentCluster, direction);
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index baba389..b44bbbe 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -17,12 +17,14 @@
package android.view.autofill;
import static android.view.autofill.Helper.DEBUG;
+import static android.view.autofill.Helper.VERBOSE;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -131,7 +133,7 @@
if (!mHasSession) {
if (gainFocus) {
// Starts new session.
- startSession(id, bounds, value);
+ startSession(id, view.getWindowToken(), bounds, value);
}
} else {
// Update focus on existing session.
@@ -159,7 +161,7 @@
if (!mHasSession) {
if (gainFocus) {
// Starts new session.
- startSession(id, bounds, null);
+ startSession(id, parent.getWindowToken(), bounds, null);
}
} else {
// Update focus on existing session.
@@ -251,13 +253,14 @@
return new AutoFillId(parent.getAccessibilityViewId(), childId);
}
- private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) {
+ private void startSession(AutoFillId id, IBinder windowToken,
+ Rect bounds, AutoFillValue value) {
if (DEBUG) {
- Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
+ Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
}
try {
- mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(),
- id, bounds, value, mContext.getUserId());
+ mService.startSession(mContext.getActivityToken(), windowToken,
+ mServiceClient.asBinder(), id, bounds, value, mContext.getUserId());
AutoFillClient client = getClient();
if (client != null) {
client.resetableStateAvailable();
@@ -270,7 +273,7 @@
private void finishSession() {
if (DEBUG) {
- Log.v(TAG, "finishSession()");
+ Log.d(TAG, "finishSession()");
}
mHasSession = false;
try {
@@ -282,9 +285,12 @@
private void updateSession(AutoFillId id, Rect bounds, AutoFillValue value, int flags) {
if (DEBUG) {
- Log.v(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ if (VERBOSE || (flags & FLAG_FOCUS_LOST) != 0) {
+ Log.d(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags);
+ }
}
+
try {
mService.updateSession(mContext.getActivityToken(), id, bounds, value, flags,
mContext.getUserId());
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 0433a8f..d054e97 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -30,7 +30,7 @@
*/
interface IAutoFillManager {
boolean addClient(in IAutoFillManagerClient client, int userId);
- oneway void startSession(in IBinder activityToken, in IBinder appCallback,
+ oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
in AutoFillValue value, int flags, int userId);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index f2c2af5..887c59a 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -32,6 +32,7 @@
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
+import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutoFillManager;
@@ -68,6 +69,10 @@
private OnCheckedChangeListener mOnCheckedChangeListener;
private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
+ // Indicates whether the toggle state was set from resources or dynamically, so it can be used
+ // to sanitize auto-fill requests.
+ private boolean mCheckedFromResource = false;
+
private static final int[] CHECKED_STATE_SET = {
R.attr.state_checked
};
@@ -109,6 +114,7 @@
final boolean checked = a.getBoolean(
com.android.internal.R.styleable.CompoundButton_checked, false);
setChecked(checked);
+ mCheckedFromResource = true;
a.recycle();
@@ -148,6 +154,7 @@
@Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
+ mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
notifyViewAccessibilityStateChangedIfNeeded(
@@ -569,6 +576,13 @@
// TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
@Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+
+ structure.setSanitized(mCheckedFromResource);
+ }
+
+ @Override
public void autoFill(AutoFillValue value) {
if (!isEnabled()) return;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dd3b054..ade03e1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2183,6 +2183,11 @@
}
void onTouchUpEvent(MotionEvent event) {
+ if (getSelectionActionModeHelper().resetOriginalSelection(
+ getTextView().getOffsetForPosition(event.getX(), event.getY()))) {
+ return;
+ }
+
boolean selectAllGotFocus = mSelectAllOnFocus && mTextView.didTouchFocusSelect();
hideCursorAndSpanControllers();
stopTextActionMode();
@@ -3916,7 +3921,7 @@
@Override
public void onDestroyActionMode(ActionMode mode) {
// Clear mTextActionMode not to recursively destroy action mode by clearing selection.
- getSelectionActionModeHelper().cancelAsyncTask();
+ getSelectionActionModeHelper().onDestroyActionMode();
mTextActionMode = null;
Callback customCallback = getCustomCallback();
if (customCallback != null) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 49253eb..bba3a11 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -24,6 +24,7 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStructure;
import android.view.autofill.AutoFillManager;
import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
@@ -66,6 +67,10 @@
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
+ // Indicates whether the child was set from resources or dynamically, so it can be used
+ // to sanitize auto-fill requests.
+ private int mInitialCheckedId = View.NO_ID;
+
/**
* {@inheritDoc}
*/
@@ -89,8 +94,8 @@
int value = attributes.getResourceId(R.styleable.RadioGroup_checkedButton, View.NO_ID);
if (value != View.NO_ID) {
mCheckedId = value;
+ mInitialCheckedId = value;
}
-
final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL);
setOrientation(index);
@@ -411,6 +416,12 @@
// TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
@Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+ structure.setSanitized(mCheckedId == mInitialCheckedId);
+ }
+
+ @Override
public void autoFill(AutoFillValue value) {
if (!isEnabled()) return;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 770d9ee..6790532 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -54,6 +54,8 @@
private TextClassificationResult mTextClassificationResult;
private AsyncTask mTextClassificationAsyncTask;
+ private final SelectionInfo mSelectionInfo = new SelectionInfo();
+
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
final TextView textView = mEditor.getTextView();
@@ -94,12 +96,12 @@
}
}
- public void cancelAsyncTask() {
- if (mTextClassificationAsyncTask != null) {
- mTextClassificationAsyncTask.cancel(true);
- mTextClassificationAsyncTask = null;
+ public boolean resetOriginalSelection(int textIndex) {
+ if (mSelectionInfo.resetOriginalSelection(textIndex, mEditor.getTextView().getText())) {
+ invalidateActionModeAsync();
+ return true;
}
- mTextClassificationResult = null;
+ return false;
}
@Nullable
@@ -107,12 +109,28 @@
return mTextClassificationResult;
}
+ public void onDestroyActionMode() {
+ mSelectionInfo.onSelectionDestroyed();
+ cancelAsyncTask();
+ }
+
+ private void cancelAsyncTask() {
+ if (mTextClassificationAsyncTask != null) {
+ mTextClassificationAsyncTask.cancel(true);
+ mTextClassificationAsyncTask = null;
+ }
+ mTextClassificationResult = null;
+ }
+
private boolean isNoOpTextClassifier() {
return mEditor.getTextView().getTextClassifier() == TextClassifier.NO_OP;
}
private void startActionMode(@Nullable SelectionResult result) {
- final CharSequence text = mEditor.getTextView().getText();
+ final TextView textView = mEditor.getTextView();
+ final CharSequence text = textView.getText();
+ mSelectionInfo.setOriginalSelection(
+ textView.getSelectionStart(), textView.getSelectionEnd());
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextClassificationResult = result.mResult;
@@ -124,6 +142,9 @@
if (controller != null) {
controller.show();
}
+ if (result != null) {
+ mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd);
+ }
}
mEditor.setRestartActionModeOnNextRefresh(false);
mTextClassificationAsyncTask = null;
@@ -135,6 +156,8 @@
if (actionMode != null) {
actionMode.invalidate();
}
+ final TextView textView = mEditor.getTextView();
+ mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd());
mTextClassificationAsyncTask = null;
}
@@ -145,6 +168,56 @@
}
/**
+ * Holds information about the selection and uses it to decide on whether or not to update
+ * the selection when resetOriginalSelection is called.
+ * The expected UX here is to allow the user to re-snap the selection back to the original word
+ * that was selected with one tap on that word.
+ */
+ private static final class SelectionInfo {
+
+ private int mOriginalStart;
+ private int mOriginalEnd;
+ private int mSelectionStart;
+ private int mSelectionEnd;
+
+ private boolean mResetOriginal;
+
+ public void setOriginalSelection(int selectionStart, int selectionEnd) {
+ mOriginalStart = selectionStart;
+ mOriginalEnd = selectionEnd;
+ mResetOriginal = false;
+ }
+
+ public void onSelectionStarted(int selectionStart, int selectionEnd) {
+ // Set the reset flag to true if the selection changed.
+ mSelectionStart = selectionStart;
+ mSelectionEnd = selectionEnd;
+ mResetOriginal = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ }
+
+ public void onSelectionUpdated(int selectionStart, int selectionEnd) {
+ // If the selection did not change, maintain the reset state. Otherwise, disable reset.
+ mResetOriginal &= selectionStart == mSelectionStart && selectionEnd == mSelectionEnd;
+ }
+
+ public void onSelectionDestroyed() {
+ mResetOriginal = false;
+ }
+
+ public boolean resetOriginalSelection(int textIndex, CharSequence text) {
+ if (mResetOriginal
+ && textIndex >= mOriginalStart && textIndex <= mOriginalEnd
+ && text instanceof Spannable) {
+ Selection.setSelection((Spannable) text, mOriginalStart, mOriginalEnd);
+ // Only allow a reset once.
+ mResetOriginal = false;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
* AsyncTask for running a query on a background thread and returning the result on the
* UiThread. The AsyncTask times out after a specified time, returning a null result if the
* query has not yet returned.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bf8de38..9078e61 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -727,7 +727,7 @@
private boolean mHasPresetAutoSizeValues = false;
// Indicates whether the text was set from resources or dynamically, so it can be used to
- // sanitize auto-fill request.
+ // sanitize auto-fill requests.
private boolean mTextFromResource = false;
/**
@@ -8208,7 +8208,7 @@
// If we have a fixed width, we can just swap in a new text layout
// if the text height stays the same or if the view height is fixed.
- if (((mLayoutParams.width != LayoutParams.WRAP_CONTENT && mLayoutParams.width != 0)
+ if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
|| (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
&& (mHint == null || mHintLayout != null)
&& (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
new file mode 100644
index 0000000..3ac5a72
--- /dev/null
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import android.annotation.CallSuper;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.FileObserver;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsProvider;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
+ * files.
+ */
+public abstract class FileSystemProvider extends DocumentsProvider {
+
+ private static final String TAG = "FileSystemProvider";
+
+ private static final boolean LOG_INOTIFY = false;
+
+ private String[] mDefaultProjection;
+
+ @GuardedBy("mObservers")
+ private final ArrayMap<File, DirectoryObserver> mObservers = new ArrayMap<>();
+
+ private Handler mHandler;
+
+ protected abstract File getFileForDocId(String docId, boolean visible)
+ throws FileNotFoundException;
+
+ protected abstract String getDocIdForFile(File file) throws FileNotFoundException;
+
+ protected abstract Uri buildNotificationUri(String docId);
+
+ @Override
+ public boolean onCreate() {
+ throw new UnsupportedOperationException(
+ "Subclass should override this and call onCreate(defaultDocumentProjection)");
+ }
+
+ @CallSuper
+ protected void onCreate(String[] defaultProjection) {
+ mHandler = new Handler();
+ mDefaultProjection = defaultProjection;
+ }
+
+ @Override
+ public boolean isChildDocument(String parentDocId, String docId) {
+ try {
+ final File parent = getFileForDocId(parentDocId).getCanonicalFile();
+ final File doc = getFileForDocId(docId).getCanonicalFile();
+ return FileUtils.contains(parent, doc);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Failed to determine if " + docId + " is child of " + parentDocId + ": " + e);
+ }
+ }
+
+ protected final List<String> findDocumentPath(File parent, File doc)
+ throws FileNotFoundException {
+
+ if (!doc.exists()) {
+ throw new FileNotFoundException(doc + " is not found.");
+ }
+
+ if (!FileUtils.contains(parent, doc)) {
+ throw new FileNotFoundException(doc + " is not found under " + parent);
+ }
+
+ LinkedList<String> path = new LinkedList<>();
+ while (doc != null && FileUtils.contains(parent, doc)) {
+ path.addFirst(getDocIdForFile(doc));
+
+ doc = doc.getParentFile();
+ }
+
+ return path;
+ }
+
+ @Override
+ public String createDocument(String docId, String mimeType, String displayName)
+ throws FileNotFoundException {
+ displayName = FileUtils.buildValidFatFilename(displayName);
+
+ final File parent = getFileForDocId(docId);
+ if (!parent.isDirectory()) {
+ throw new IllegalArgumentException("Parent document isn't a directory");
+ }
+
+ 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);
+ }
+ } else {
+ try {
+ if (!file.createNewFile()) {
+ throw new IllegalStateException("Failed to touch " + file);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to touch " + file + ": " + e);
+ }
+ }
+
+ return getDocIdForFile(file);
+ }
+
+ @Override
+ public String renameDocument(String docId, String displayName) throws FileNotFoundException {
+ // Since this provider treats renames as generating a completely new
+ // docId, we're okay with letting the MIME type change.
+ displayName = FileUtils.buildValidFatFilename(displayName);
+
+ final File before = getFileForDocId(docId);
+ final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
+ final File visibleFileBefore = getFileForDocId(docId, true);
+ if (!before.renameTo(after)) {
+ throw new IllegalStateException("Failed to rename to " + after);
+ }
+ removeFromMediaStore(visibleFileBefore);
+
+ final String afterDocId = getDocIdForFile(after);
+ scanFile(getFileForDocId(afterDocId, true));
+
+ if (!TextUtils.equals(docId, afterDocId)) {
+ return afterDocId;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteDocument(String docId) throws FileNotFoundException {
+ final File file = getFileForDocId(docId);
+ final File visibleFile = getFileForDocId(docId, true);
+
+ final boolean isDirectory = file.isDirectory();
+ if (isDirectory) {
+ FileUtils.deleteContents(file);
+ }
+ if (!file.delete()) {
+ throw new IllegalStateException("Failed to delete " + file);
+ }
+
+ removeFromMediaStore(visibleFile);
+ }
+
+ @Override
+ public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+ String targetParentDocumentId)
+ throws FileNotFoundException {
+ final File before = getFileForDocId(sourceDocumentId);
+ final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
+ final File visibleFileBefore = getFileForDocId(sourceDocumentId, true);
+
+ if (after.exists()) {
+ throw new IllegalStateException("Already exists " + after);
+ }
+ if (!before.renameTo(after)) {
+ throw new IllegalStateException("Failed to move to " + after);
+ }
+
+ // Notify media store to update its content
+ removeFromMediaStore(visibleFileBefore);
+ final String docId = getDocIdForFile(after);
+ scanFile(getFileForDocId(docId, true));
+
+ return docId;
+ }
+
+ private void removeFromMediaStore(File visibleFile) throws FileNotFoundException {
+ if (visibleFile != null) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri externalUri = MediaStore.Files.getContentUri("external");
+
+ // Remove media store entries for any files inside this directory, using
+ // path prefix match. Logic borrowed from MtpDatabase.
+ if (visibleFile.isDirectory()) {
+ final String path = visibleFile.getAbsolutePath() + "/";
+ resolver.delete(externalUri,
+ "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
+ new String[] { path + "%", Integer.toString(path.length()), path });
+ }
+
+ // Remove media store entry for this exact file.
+ final String path = visibleFile.getAbsolutePath();
+ resolver.delete(externalUri,
+ "_data LIKE ?1 AND lower(_data)=lower(?2)",
+ new String[] { path, path });
+ }
+ }
+
+ @Override
+ public Cursor queryDocument(String documentId, String[] projection)
+ throws FileNotFoundException {
+ final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
+ includeFile(result, documentId, null);
+ return result;
+ }
+
+ @Override
+ public Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+
+ final File parent = getFileForDocId(parentDocumentId);
+ final MatrixCursor result = new DirectoryCursor(
+ resolveProjection(projection), parentDocumentId, parent);
+ for (File file : parent.listFiles()) {
+ includeFile(result, null, file);
+ }
+ return result;
+ }
+
+ /**
+ * Searches documents under the given folder.
+ *
+ * To avoid runtime explosion only returns the at most 23 items.
+ *
+ * @param folder the root folder where recursive search begins
+ * @param query the search condition used to match file names
+ * @param projection projection of the returned cursor
+ * @param exclusion absolute file paths to exclude from result
+ * @return cursor containing search result
+ * @throws FileNotFoundException when root folder doesn't exist or search fails
+ */
+ protected final Cursor querySearchDocuments(
+ File folder, String query, String[] projection, Set<String> exclusion)
+ throws FileNotFoundException {
+
+ query = query.toLowerCase();
+ final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
+ final LinkedList<File> pending = new LinkedList<>();
+ pending.add(folder);
+ while (!pending.isEmpty() && result.getCount() < 24) {
+ final File file = pending.removeFirst();
+ if (file.isDirectory()) {
+ for (File child : file.listFiles()) {
+ pending.add(child);
+ }
+ }
+ if (file.getName().toLowerCase().contains(query)
+ && !exclusion.contains(file.getAbsolutePath())) {
+ includeFile(result, null, file);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String getDocumentType(String documentId) throws FileNotFoundException {
+ final File file = getFileForDocId(documentId);
+ return getTypeForFile(file);
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(
+ String documentId, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ final File file = getFileForDocId(documentId);
+ final File visibleFile = getFileForDocId(documentId, true);
+
+ final int pfdMode = ParcelFileDescriptor.parseMode(mode);
+ if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
+ return ParcelFileDescriptor.open(file, pfdMode);
+ } else {
+ try {
+ // When finished writing, kick off media scanner
+ return ParcelFileDescriptor.open(
+ file, pfdMode, mHandler, (IOException e) -> scanFile(visibleFile));
+ } catch (IOException e) {
+ throw new FileNotFoundException("Failed to open for writing: " + e);
+ }
+ }
+ }
+
+ private void scanFile(File visibleFile) {
+ final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(visibleFile));
+ getContext().sendBroadcast(intent);
+ }
+
+ @Override
+ public AssetFileDescriptor openDocumentThumbnail(
+ String documentId, Point sizeHint, CancellationSignal signal)
+ throws FileNotFoundException {
+ final File file = getFileForDocId(documentId);
+ return DocumentsContract.openImageThumbnail(file);
+ }
+
+ protected RowBuilder includeFile(MatrixCursor result, String docId, File file)
+ throws FileNotFoundException {
+ if (docId == null) {
+ docId = getDocIdForFile(file);
+ } else {
+ file = getFileForDocId(docId);
+ }
+
+ int flags = 0;
+
+ if (file.canWrite()) {
+ if (file.isDirectory()) {
+ flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+ flags |= Document.FLAG_SUPPORTS_DELETE;
+ flags |= Document.FLAG_SUPPORTS_RENAME;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
+ } else {
+ flags |= Document.FLAG_SUPPORTS_WRITE;
+ flags |= Document.FLAG_SUPPORTS_DELETE;
+ flags |= Document.FLAG_SUPPORTS_RENAME;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
+ }
+ }
+
+ final String mimeType = getTypeForFile(file);
+ final String displayName = file.getName();
+ if (mimeType.startsWith("image/")) {
+ flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
+ }
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, displayName);
+ row.add(Document.COLUMN_SIZE, file.length());
+ row.add(Document.COLUMN_MIME_TYPE, mimeType);
+ row.add(Document.COLUMN_FLAGS, flags);
+
+ // Only publish dates reasonably after epoch
+ long lastModified = file.lastModified();
+ if (lastModified > 31536000000L) {
+ row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
+ }
+
+ // Return the row builder just in case any subclass want to add more stuff to it.
+ return row;
+ }
+
+ private static String getTypeForFile(File file) {
+ if (file.isDirectory()) {
+ return Document.MIME_TYPE_DIR;
+ } else {
+ return getTypeForName(file.getName());
+ }
+ }
+
+ private static String getTypeForName(String name) {
+ final int lastDot = name.lastIndexOf('.');
+ if (lastDot >= 0) {
+ final String extension = name.substring(lastDot + 1).toLowerCase();
+ final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ if (mime != null) {
+ return mime;
+ }
+ }
+
+ return "application/octet-stream";
+ }
+
+ protected final File getFileForDocId(String docId) throws FileNotFoundException {
+ return getFileForDocId(docId, false);
+ }
+
+ private String[] resolveProjection(String[] projection) {
+ return projection == null ? mDefaultProjection : projection;
+ }
+
+ private void startObserving(File file, Uri notifyUri) {
+ synchronized (mObservers) {
+ DirectoryObserver observer = mObservers.get(file);
+ if (observer == null) {
+ observer = new DirectoryObserver(
+ file, getContext().getContentResolver(), notifyUri);
+ observer.startWatching();
+ mObservers.put(file, observer);
+ }
+ observer.mRefCount++;
+
+ if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
+ }
+ }
+
+ private void stopObserving(File file) {
+ synchronized (mObservers) {
+ DirectoryObserver observer = mObservers.get(file);
+ if (observer == null) return;
+
+ observer.mRefCount--;
+ if (observer.mRefCount == 0) {
+ mObservers.remove(file);
+ observer.stopWatching();
+ }
+
+ if (LOG_INOTIFY) Log.d(TAG, "after stop: " + observer);
+ }
+ }
+
+ private static class DirectoryObserver extends FileObserver {
+ private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO
+ | CREATE | DELETE | DELETE_SELF | MOVE_SELF;
+
+ private final File mFile;
+ private final ContentResolver mResolver;
+ private final Uri mNotifyUri;
+
+ private int mRefCount = 0;
+
+ public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
+ super(file.getAbsolutePath(), NOTIFY_EVENTS);
+ mFile = file;
+ mResolver = resolver;
+ mNotifyUri = notifyUri;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ if ((event & NOTIFY_EVENTS) != 0) {
+ if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
+ mResolver.notifyChange(mNotifyUri, null, false);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}";
+ }
+ }
+
+ private class DirectoryCursor extends MatrixCursor {
+ private final File mFile;
+
+ public DirectoryCursor(String[] columnNames, String docId, File file) {
+ super(columnNames);
+
+ final Uri notifyUri = buildNotificationUri(docId);
+ setNotificationUri(getContext().getContentResolver(), notifyUri);
+
+ mFile = file;
+ startObserving(mFile, notifyUri);
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ stopObserving(mFile);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/Predicate.java b/core/java/com/android/internal/util/Predicate.java
index bc6d6b3..1b5eaff 100644
--- a/core/java/com/android/internal/util/Predicate.java
+++ b/core/java/com/android/internal/util/Predicate.java
@@ -25,7 +25,10 @@
* <p/>
* Implementors of Predicate which may cause side effects upon evaluation are
* strongly encouraged to state this fact clearly in their API documentation.
+ *
+ * @deprecated Use {@code java.util.function.Predicate} instead.
*/
+@Deprecated
public interface Predicate<T> {
boolean apply(T t);
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index be10608df..d67cef3 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -23,6 +23,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -1495,7 +1497,7 @@
}
/**
- * @return number of log records
+ * @return the number of log records currently readable
*/
public final int getLogRecSize() {
// mSmHandler can be null if the state machine has quit.
@@ -1505,6 +1507,17 @@
}
/**
+ * @return the number of log records we can store
+ */
+ @VisibleForTesting
+ public final int getLogRecMaxSize() {
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return 0;
+ return smh.mLogRecords.mMaxSize;
+ }
+
+ /**
* @return the total number of records processed
*/
public final int getLogRecCount() {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 555263d..28291ae 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -496,6 +496,7 @@
} finally {
args.recycle();
}
+ return;
}
case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
SomeArgs args = (SomeArgs)msg.obj;
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 43005e6..919cf99 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -115,7 +115,6 @@
setEnabled(itemData.isEnabled());
setSubMenuArrowVisible(itemData.hasSubMenu());
setContentDescription(itemData.getContentDescription());
- setTooltipText(itemData.getTooltipText());
}
public void setForceShowIcon(boolean forceShow) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 817b186..79b0cd1 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1195,6 +1195,8 @@
}
setButtonTagAndClickListener(menuItemButton, menuItem);
+ // Set tooltips for main panel items, but not overflow items (b/35726766).
+ menuItemButton.setTooltipText(menuItem.getTooltipText());
mMainPanel.addView(menuItemButton);
final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
params.width = menuItemButtonWidth + extraPadding / 2;
@@ -1635,7 +1637,6 @@
buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
}
}
- menuItemButton.setTooltipText(menuItem.getTooltipText());
final CharSequence contentDescription = menuItem.getContentDescription();
if (TextUtils.isEmpty(contentDescription)) {
menuItemButton.setContentDescription(menuItem.getTitle());
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index ae2e0ac..cc1c65e 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -278,7 +278,8 @@
public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LockPatternView);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LockPatternView,
+ R.attr.lockPatternStyle, R.style.Widget_LockPatternView);
final String aspect = a.getString(R.styleable.LockPatternView_aspect);
@@ -298,12 +299,9 @@
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
- mRegularColor = context.getColor(R.color.lock_pattern_view_regular_color);
- mErrorColor = context.getColor(R.color.lock_pattern_view_error_color);
- mSuccessColor = context.getColor(R.color.lock_pattern_view_success_color);
- mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, mRegularColor);
- mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, mErrorColor);
- mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, mSuccessColor);
+ mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, 0);
+ mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
+ mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
mPathPaint.setColor(pathColor);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 3d012bf..67f9f8f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -59,6 +59,7 @@
private static final int ALLOW_LIBS = 0x02;
private static final int ALLOW_PERMISSIONS = 0x04;
private static final int ALLOW_APP_CONFIGS = 0x08;
+ private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
private static final int ALLOW_ALL = ~0;
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
@@ -225,6 +226,13 @@
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
+ // Allow Vendor to customize system configs around libs, features, permissions and apps
+ int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
+ ALLOW_APP_CONFIGS;
+ readPermissions(Environment.buildPath(
+ Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
+ readPermissions(Environment.buildPath(
+ Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
// Allow ODM to customize system configs around libs, features and apps
int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
readPermissions(Environment.buildPath(
@@ -313,6 +321,7 @@
boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
+ boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -553,7 +562,7 @@
associatedPkgs.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
- } else if ("privapp-permissions".equals(name) && allowAppConfigs) {
+ } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
readPrivAppPermissions(parser);
} else {
XmlUtils.skipCurrentTag(parser);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0c07192..cb60c62 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -220,7 +220,6 @@
external/skia/src/effects \
external/skia/src/image \
external/skia/src/images \
- external/skia/src/utils \
external/sqlite/dist \
external/sqlite/android \
external/tremor/Tremor \
@@ -284,11 +283,16 @@
libhidltransport \
libhwbinder \
libvintf \
+ libnativewindow \
LOCAL_SHARED_LIBRARIES += \
libhwui \
libdl \
+# our headers include libnativewindow's public headers
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ libnativewindow \
+
# we need to access the private Bionic header
# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
LOCAL_C_INCLUDES += bionic/libc/private
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 30d6337..417ef8a0 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -8,7 +8,6 @@
#include "SkBRDAllocator.h"
#include "SkFrontBufferedStream.h"
#include "SkMath.h"
-#include "SkOpts.h"
#include "SkPixelRef.h"
#include "SkStream.h"
#include "SkUtils.h"
@@ -229,45 +228,6 @@
needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
}
-static inline SkAlphaType computeDecodeAlphaType(SkColorType colorType, SkAlphaType alphaType) {
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- // Skia premultiplies linearly. Until the framework enables linear blending,
- // it expects a legacy premultiply.
- if (kPremul_SkAlphaType == alphaType && kRGBA_F16_SkColorType != colorType) {
- return kUnpremul_SkAlphaType;
- }
-#endif
-
- return alphaType;
-}
-
-static inline void premultiplyIfNecessary(SkBitmap* bitmap, SkPMColor* colorPtr, int* colorCount,
- SkAlphaType alphaType, bool requireUnpremultiplied) {
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- if (kUnpremul_SkAlphaType != alphaType || requireUnpremultiplied) {
- return;
- }
-
- switch (bitmap->colorType()) {
- case kN32_SkColorType:
- for (int y = 0; y < bitmap->height(); y++) {
- SkOpts::RGBA_to_rgbA(bitmap->getAddr32(0, y), bitmap->getAddr32(0, y),
- bitmap->width());
- }
-
- return;
- case kIndex_8_SkColorType:
- SkOpts::RGBA_to_rgbA(colorPtr, colorPtr, *colorCount);
- return;
- default:
- // kRGBA_F16 will be premultiplied by the codec if necessary.
- // kGray_8 (alias kAlpha_8) and k565 are opaque.
- LOG_ALWAYS_FATAL("Should be unreachable - no need for legacy premultiply.");
- return;
- }
-#endif
-}
-
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
// This function takes ownership of the input stream. Since the SkAndroidCodec
// will take ownership of the stream, we don't necessarily need to take ownership
@@ -450,15 +410,13 @@
}
SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
- SkAlphaType decodeAlphaType = computeDecodeAlphaType(decodeColorType, alphaType);
const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
- decodeColorType, decodeAlphaType, codec->computeOutputColorSpace(decodeColorType));
-
- SkImageInfo bitmapInfo = decodeInfo.makeAlphaType(alphaType);
+ decodeColorType, alphaType, codec->computeOutputColorSpace(decodeColorType));
// For wide gamut images, we will leave the color space on the SkBitmap. Otherwise,
// use the default.
+ SkImageInfo bitmapInfo = decodeInfo;
sk_sp<SkColorSpace> srgb =
SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
SkColorSpace::kSRGB_Gamut,
@@ -502,8 +460,6 @@
default:
return nullObjectReturn("codec->getAndroidPixels() failed.");
}
- premultiplyIfNecessary(&decodingBitmap, colorPtr, colorCount, decodeAlphaType,
- requireUnpremultiplied);
jbyteArray ninePatchChunk = NULL;
if (peeker.mPatch != NULL) {
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index 5f0664b..b26b6e1 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -26,15 +26,17 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <private/android/AHardwareBufferHelpers.h>
#include <binder/Parcel.h>
+
+#include <ui/GraphicBuffer.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
-#include <hardware/gralloc1.h>
-#include <ui/GraphicBuffer.h>
-
#include <private/gui/ComposerService.h>
+#include <hardware/gralloc1.h>
+
#include "core_jni_helpers.h"
using namespace android;
@@ -64,15 +66,6 @@
sp<GraphicBuffer> buffer;
};
-
-// ----------------------------------------------------------------------------
-// Helper functions
-// ----------------------------------------------------------------------------
-
-static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
- return (mask & bitsToCheck) == bitsToCheck;
-}
-
// ----------------------------------------------------------------------------
// HardwareBuffer lifecycle
// ----------------------------------------------------------------------------
@@ -121,8 +114,7 @@
delete wrapper;
}
-static jlong android_hardware_HardwareBuffer_getNativeFinalizer(JNIEnv* env,
- jobject clazz) {
+static jlong android_hardware_HardwareBuffer_getNativeFinalizer(JNIEnv* env, jobject clazz) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyWrapper));
}
@@ -203,7 +195,8 @@
if (env->IsInstanceOf(hardwareBufferObj, gHardwareBufferClassInfo.clazz)) {
GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(
env->GetLongField(hardwareBufferObj, gHardwareBufferClassInfo.mNativeObject));
- return reinterpret_cast<AHardwareBuffer*>(buffer);
+ return AHardwareBuffer_from_GraphicBuffer(buffer);
+
} else {
return nullptr;
}
@@ -211,7 +204,7 @@
jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
JNIEnv* env, AHardwareBuffer* hardwareBuffer) {
- GraphicBuffer* buffer = reinterpret_cast<GraphicBuffer*>(hardwareBuffer);
+ GraphicBuffer* buffer = AHardwareBuffer_to_GraphicBuffer(hardwareBuffer);
GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
jobject hardwareBufferObj = env->NewObject(gHardwareBufferClassInfo.clazz,
gHardwareBufferClassInfo.ctor, reinterpret_cast<jlong>(wrapper));
@@ -228,103 +221,22 @@
}
uint32_t android_hardware_HardwareBuffer_convertFromPixelFormat(uint32_t format) {
- switch (format) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
- case HAL_PIXEL_FORMAT_RGBX_8888:
- return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
- case HAL_PIXEL_FORMAT_RGB_565:
- return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
- case HAL_PIXEL_FORMAT_RGB_888:
- return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
- case HAL_PIXEL_FORMAT_RGBA_FP16:
- return AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT;
- case HAL_PIXEL_FORMAT_RGBA_1010102:
- return AHARDWAREBUFFER_FORMAT_A2R10G10B10_UNORM_PACK32;
- case HAL_PIXEL_FORMAT_BLOB:
- return AHARDWAREBUFFER_FORMAT_BLOB;
- default:
- ALOGE("Unknown pixel format %u", format);
- return 0;
- }
+ return AHardwareBuffer_convertFromPixelFormat(format);
}
uint32_t android_hardware_HardwareBuffer_convertToPixelFormat(uint32_t format) {
- switch (format) {
- case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
- return HAL_PIXEL_FORMAT_RGBA_8888;
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
- return HAL_PIXEL_FORMAT_RGBX_8888;
- case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
- return HAL_PIXEL_FORMAT_RGB_565;
- case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
- return HAL_PIXEL_FORMAT_RGB_888;
- case AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT:
- return HAL_PIXEL_FORMAT_RGBA_FP16;
- case AHARDWAREBUFFER_FORMAT_A2R10G10B10_UNORM_PACK32:
- return HAL_PIXEL_FORMAT_RGBA_1010102;
- case AHARDWAREBUFFER_FORMAT_BLOB:
- return HAL_PIXEL_FORMAT_BLOB;
- default:
- ALOGE("Unknown AHardwareBuffer format %u", format);
- return 0;
- }
+ return AHardwareBuffer_convertToPixelFormat(format);
}
void android_hardware_HardwareBuffer_convertToGrallocUsageBits(uint64_t usage0,
- uint64_t usage1, uint64_t* outProducerUsage,
+ uint64_t /*usage1*/, uint64_t* outProducerUsage,
uint64_t* outConsumerUsage) {
- *outProducerUsage = 0;
- *outConsumerUsage = 0;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_READ))
- *outConsumerUsage |= GRALLOC1_CONSUMER_USAGE_CPU_READ;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN))
- *outConsumerUsage |= GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_WRITE))
- *outProducerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN))
- *outProducerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE))
- *outConsumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT))
- *outProducerUsage |= GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET;
- // Not sure what this should be.
- //if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP)) bits |= 0;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER))
- *outConsumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE))
- *outConsumerUsage |= GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT))
- *outProducerUsage |= GRALLOC1_PRODUCER_USAGE_PROTECTED;
- if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA))
- *outProducerUsage |= GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA;
+ AHardwareBuffer_convertToGrallocUsageBits(usage0, outProducerUsage, outConsumerUsage);
}
uint64_t android_hardware_HardwareBuffer_convertFromGrallocUsageBits(
uint64_t producerUsage, uint64_t consumerUsage) {
- uint64_t bits = 0;
- if (containsBits(consumerUsage, GRALLOC1_CONSUMER_USAGE_CPU_READ))
- bits |= AHARDWAREBUFFER_USAGE0_CPU_READ;
- if (containsBits(consumerUsage, GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN))
- bits |= AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN;
- if (containsBits(producerUsage, GRALLOC1_PRODUCER_USAGE_CPU_WRITE))
- bits |= AHARDWAREBUFFER_USAGE0_CPU_WRITE;
- if (containsBits(producerUsage, GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN))
- bits |= AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN;
- if (containsBits(consumerUsage, GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE))
- bits |= AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
- if (containsBits(producerUsage, GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET))
- bits |= AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT;
- if (containsBits(consumerUsage, GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER))
- bits |= AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER;
- if (containsBits(consumerUsage, GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER))
- bits |= AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE;
- if (containsBits(producerUsage, GRALLOC1_PRODUCER_USAGE_PROTECTED))
- bits |= AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT;
- if (containsBits(producerUsage, GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA))
- bits |= AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA;
-
- return bits;
+ return AHardwareBuffer_convertFromGrallocUsageBits(producerUsage, consumerUsage);
}
} // namespace android
@@ -336,19 +248,21 @@
const char* const kClassPathName = "android/hardware/HardwareBuffer";
static const JNINativeMethod gMethods[] = {
- { "nCreateHardwareBuffer", "(IIIIJ)J", (void*) android_hardware_HardwareBuffer_create },
- { "nGetNativeFinalizer", "()J", (void*) android_hardware_HardwareBuffer_getNativeFinalizer },
+ { "nCreateHardwareBuffer", "(IIIIJ)J",
+ (void*) android_hardware_HardwareBuffer_create },
+ { "nGetNativeFinalizer", "()J",
+ (void*) android_hardware_HardwareBuffer_getNativeFinalizer },
{ "nWriteHardwareBufferToParcel", "(JLandroid/os/Parcel;)V",
(void*) android_hardware_HardwareBuffer_write },
{ "nReadHardwareBufferFromParcel", "(Landroid/os/Parcel;)J",
(void*) android_hardware_HardwareBuffer_read },
// --------------- @FastNative ----------------------
- { "nGetWidth", "(J)I", (void*) android_hardware_HardwareBuffer_getWidth },
- { "nGetHeight", "(J)I", (void*) android_hardware_HardwareBuffer_getHeight },
- { "nGetFormat", "(J)I", (void*) android_hardware_HardwareBuffer_getFormat },
- { "nGetLayers", "(J)I", (void*) android_hardware_HardwareBuffer_getLayers },
- { "nGetUsage", "(J)J", (void*) android_hardware_HardwareBuffer_getUsage },
+ { "nGetWidth", "(J)I", (void*) android_hardware_HardwareBuffer_getWidth },
+ { "nGetHeight", "(J)I", (void*) android_hardware_HardwareBuffer_getHeight },
+ { "nGetFormat", "(J)I", (void*) android_hardware_HardwareBuffer_getFormat },
+ { "nGetLayers", "(J)I", (void*) android_hardware_HardwareBuffer_getLayers },
+ { "nGetUsage", "(J)J", (void*) android_hardware_HardwareBuffer_getUsage },
};
int register_android_hardware_HardwareBuffer(JNIEnv* env) {
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index eaf9e91..4322105 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -1021,17 +1021,13 @@
env->ReleaseStringCritical(fileName, str);
}
- int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */
+ int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND, 0666);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
return;
}
- if (lseek(fd, 0, SEEK_END) < 0) {
- fprintf(stderr, "lseek: %s\n", strerror(errno));
- } else {
- dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);
- }
+ dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);
close(fd);
}
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index 45d5061..f1bc76e 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -65,11 +65,6 @@
#pragma clang diagnostic pop
-inline static void AllowSyscall(filter& f, __u32 num) {
- f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
- Allow(f);
-}
-
inline static void ExamineSyscall(filter& f) {
f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
}
@@ -125,34 +120,6 @@
// arm64-only filter - autogenerated from bionic syscall usage
for (size_t i = 0; i < arm64_filter_size; ++i)
f.push_back(arm64_filter[i]);
-
- // Syscalls needed to boot Android
- AllowSyscall(f, 41); // __NR_pivot_root
- AllowSyscall(f, 31); // __NR_ioprio_get
- AllowSyscall(f, 30); // __NR_ioprio_set
- AllowSyscall(f, 178); // __NR_gettid
- AllowSyscall(f, 98); // __NR_futex
- AllowSyscall(f, 220); // __NR_clone
- AllowSyscall(f, 139); // __NR_rt_sigreturn
- AllowSyscall(f, 240); // __NR_rt_tgsigqueueinfo
- AllowSyscall(f, 128); // __NR_restart_syscall
- AllowSyscall(f, 278); // __NR_getrandom
-
- // Needed for performance tools
- AllowSyscall(f, 241); // __NR_perf_event_open
-
- // Needed for strace
- AllowSyscall(f, 130); // __NR_tkill
-
- // Needed for kernel to restart syscalls
- AllowSyscall(f, 128); // __NR_restart_syscall
-
- // b/35034743
- AllowSyscall(f, 267); // __NR_syncfs
-
- // b/34763393
- AllowSyscall(f, 277); // __NR_seccomp
-
Trap(f);
if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
@@ -164,64 +131,6 @@
// arm32 filter - autogenerated from bionic syscall usage
for (size_t i = 0; i < arm_filter_size; ++i)
f.push_back(arm_filter[i]);
-
- // Syscalls needed to boot android
- AllowSyscall(f, 120); // __NR_clone
- AllowSyscall(f, 240); // __NR_futex
- AllowSyscall(f, 119); // __NR_sigreturn
- AllowSyscall(f, 173); // __NR_rt_sigreturn
- AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
- AllowSyscall(f, 224); // __NR_gettid
-
- // Syscalls needed to run Chrome
- AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
- AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
-
- // Syscalls needed to run GFXBenchmark
- AllowSyscall(f, 190); // __NR_vfork
-
- // Needed for strace
- AllowSyscall(f, 238); // __NR_tkill
-
- // Needed for kernel to restart syscalls
- AllowSyscall(f, 0); // __NR_restart_syscall
-
- // Needed for debugging 32-bit Chrome
- AllowSyscall(f, 42); // __NR_pipe
-
- // b/34732712
- AllowSyscall(f, 364); // __NR_perf_event_open
-
- // b/34651972
- AllowSyscall(f, 33); // __NR_access
- AllowSyscall(f, 195); // __NR_stat64
-
- // b/34813887
- AllowSyscall(f, 5); // __NR_open
- AllowSyscall(f, 141); // __NR_getdents
- AllowSyscall(f, 217); // __NR_getdents64
-
- // b/34719286
- AllowSyscall(f, 351); // __NR_eventfd
-
- // b/34817266
- AllowSyscall(f, 252); // __NR_epoll_wait
-
- // Needed by sanitizers (b/34606909)
- // 5 (__NR_open) and 195 (__NR_stat64) are also required, but they are
- // already allowed.
- AllowSyscall(f, 85); // __NR_readlink
-
- // b/34908783
- AllowSyscall(f, 250); // __NR_epoll_create
-
- // b/34979910
- AllowSyscall(f, 8); // __NR_creat
- AllowSyscall(f, 10); // __NR_unlink
-
- // b/35059702
- AllowSyscall(f, 196); // __NR_lstat64
-
Trap(f);
return install_filter(f);
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 723dce6..314595f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -119,96 +119,6 @@
return block;
}
-// This is called by zygote (running as user root) as part of preloadResources.
-static void verifySystemIdmaps()
-{
- pid_t pid;
- char system_id[10];
-
- snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
-
- switch (pid = fork()) {
- case -1:
- ALOGE("failed to fork for idmap: %s", strerror(errno));
- break;
- case 0: // child
- {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata;
-
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
-
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
-
- if (capget(&capheader, &capdata) != 0) {
- ALOGE("capget: %s\n", strerror(errno));
- exit(1);
- }
-
- capdata.effective = capdata.permitted;
- if (capset(&capheader, &capdata) != 0) {
- ALOGE("capset: %s\n", strerror(errno));
- exit(1);
- }
-
- if (setgid(AID_SYSTEM) != 0) {
- ALOGE("setgid: %s\n", strerror(errno));
- exit(1);
- }
-
- if (setuid(AID_SYSTEM) != 0) {
- ALOGE("setuid: %s\n", strerror(errno));
- exit(1);
- }
-
- // Generic idmap parameters
- const char* argv[8];
- int argc = 0;
- struct stat st;
-
- memset(argv, NULL, sizeof(argv));
- argv[argc++] = AssetManager::IDMAP_BIN;
- argv[argc++] = "--scan";
- argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
- argv[argc++] = AssetManager::TARGET_APK_PATH;
- argv[argc++] = AssetManager::IDMAP_DIR;
-
- // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
- char subdir[PROP_VALUE_MAX];
- int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY,
- subdir);
- if (len == 0) {
- len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
- }
- if (len > 0) {
- String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
- if (stat(overlayPath.string(), &st) == 0) {
- argv[argc++] = overlayPath.string();
- }
- }
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
- }
-
- // Finally, invoke idmap (if any overlay directory exists)
- if (argc > 5) {
- execv(AssetManager::IDMAP_BIN, (char* const*)argv);
- ALOGE("failed to execv for idmap: %s", strerror(errno));
- exit(1); // should never get here
- } else {
- exit(0);
- }
- }
- break;
- default: // parent
- waitpid(pid, NULL, 0);
- break;
- }
-}
-
// ----------------------------------------------------------------------------
// this guy is exported to other jni routines
@@ -1597,9 +1507,6 @@
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
- if (isSystem) {
- verifySystemIdmaps();
- }
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0171562..a81901d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -356,9 +356,9 @@
}
static void nativeSetMatrix(JNIEnv* env, jclass clazz, jlong nativeObject,
- jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) {
+ jfloat dsdx, jfloat dtdx, jfloat dtdy, jfloat dsdy) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- status_t err = ctrl->setMatrix(dsdx, dtdx, dsdy, dtdy);
+ status_t err = ctrl->setMatrix(dsdx, dtdx, dtdy, dsdy);
if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
diff --git a/core/proto/android/os/looper.proto b/core/proto/android/os/looper.proto
new file mode 100644
index 0000000..9fcc781
--- /dev/null
+++ b/core/proto/android/os/looper.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.os;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/os/messagequeue.proto";
+
+message LooperProto {
+ string thread_name = 1;
+ int64 thread_id = 2;
+ int32 identity_hash_code = 3;
+ android.os.MessageQueueProto queue = 4;
+}
diff --git a/core/proto/android/os/message.proto b/core/proto/android/os/message.proto
new file mode 100644
index 0000000..604935d
--- /dev/null
+++ b/core/proto/android/os/message.proto
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.os;
+
+option java_multiple_files = true;
+
+message MessageProto {
+ int64 when = 1;
+ // Name of callback class.
+ string callback = 2;
+ // User-defined message code so that the recipient can identify what this
+ // message is about.
+ int32 what = 3;
+ int32 arg1 = 4;
+ int32 arg2 = 5;
+ // String representation of an arbitrary object to send to the recipient.
+ string obj = 6;
+ // Name of target class.
+ string target = 7;
+ int32 barrier = 8;
+}
diff --git a/core/proto/android/os/messagequeue.proto b/core/proto/android/os/messagequeue.proto
new file mode 100644
index 0000000..9bff13e
--- /dev/null
+++ b/core/proto/android/os/messagequeue.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.os;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/os/message.proto";
+
+message MessageQueueProto {
+ repeated android.os.MessageProto messages = 1;
+ bool is_polling_locked = 2;
+ bool is_quitting = 3;
+}
diff --git a/core/proto/android/os/worksource.proto b/core/proto/android/os/worksource.proto
new file mode 100644
index 0000000..c2aa5cb
--- /dev/null
+++ b/core/proto/android/os/worksource.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.os;
+
+option java_multiple_files = true;
+
+message WorkSourceProto {
+ message WorkSourceContentProto {
+ int32 uid = 1;
+ string name = 2;
+ }
+
+ repeated WorkSourceContentProto work_source_contents = 1;
+}
\ No newline at end of file
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
new file mode 100644
index 0000000..326b0eb
--- /dev/null
+++ b/core/proto/android/service/package.proto
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.service.pm;
+
+option java_multiple_files = true;
+option java_outer_classname = "PackageServiceProto";
+
+message PackageServiceDumpProto {
+ message PackageShortProto {
+ // Name of package. e.g. "com.android.providers.telephony".
+ string name = 1;
+ // UID for this package as assigned by Android OS.
+ int32 uid = 2;
+ }
+ message SharedLibraryProto {
+ string name = 1;
+ // True if library path is not null (jar), false otherwise (apk)
+ bool is_jar = 2;
+ // Should be filled if is_jar is true
+ string path = 3;
+ // Should be filled if is_jar is false
+ string apk = 4;
+ }
+ message FeatureProto {
+ string name = 1;
+ int32 version = 2;
+ }
+ message SharedUserProto {
+ int32 user_id = 1;
+ string name = 2;
+ }
+
+ // Installed packages.
+ PackageShortProto required_verifier_package = 1;
+ PackageShortProto verifier_package = 2;
+ repeated SharedLibraryProto shared_libraries = 3;
+ repeated FeatureProto features = 4;
+ repeated PackageProto packages = 5;
+ repeated SharedUserProto shared_users = 6;
+ // Messages from the settings problem file
+ repeated string messages = 7;
+}
+
+message PackageProto {
+ message SplitProto {
+ string name = 1;
+ int32 revision_code = 2;
+ }
+ message UserInfoProto {
+ enum InstallType {
+ NOT_INSTALLED_FOR_USER = 0;
+ FULL_APP_INSTALL = 1;
+ INSTANT_APP_INSTALL = 2;
+ }
+ // Enum values gotten from PackageManger.java
+ enum EnabledState {
+ // This component or application is in its default enabled state
+ // (as specified in its manifest).
+ COMPONENT_ENABLED_STATE_DEFAULT = 0;
+ // This component or application has been explictily enabled, regardless
+ // of what it has specified in its manifest.
+ COMPONENT_ENABLED_STATE_ENABLED = 1;
+ // This component or application has been explicitly disabled, regardless of
+ // what it has specified in its manifest.
+ COMPONENT_ENABLED_STATE_DISABLED = 2;
+ // The user has explicitly disabled the application, regardless of what it has
+ // specified in its manifest.
+ COMPONENT_ENABLED_STATE_DISABLED_USER = 3;
+ // This application should be considered, until the point where the user actually
+ // wants to use it.
+ COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
+ }
+
+ int32 id = 1;
+ InstallType install_type = 2;
+ // Is the app restricted by owner / admin
+ bool is_hidden = 3;
+ bool is_suspended = 4;
+ bool is_stopped = 5;
+ bool is_launched = 6;
+ EnabledState enabled_state = 7;
+ string last_disabled_app_caller = 8;
+ }
+
+ // Name of package. e.g. "com.android.providers.telephony".
+ string name = 1;
+ // UID for this package as assigned by Android OS.
+ int32 uid = 2;
+ // Package's reported version.
+ int32 version_code = 3;
+ // Package's reported version string (what's displayed to the user).
+ string version_string = 4;
+ // UTC timestamp of install
+ int64 install_time_ms = 5;
+ // Millisecond UTC timestamp of latest update adjusted to Google's server clock.
+ int64 update_time_ms = 6;
+ // From "dumpsys package" - name of package which installed this one.
+ // Typically "" if system app or "com.android.vending" if Play Store.
+ string installer_name = 7;
+ // Split APKs.
+ repeated SplitProto splits = 8;
+ // Per-user package info.
+ repeated UserInfoProto users = 9;
+}
diff --git a/core/proto/android/service/power.proto b/core/proto/android/service/power.proto
new file mode 100644
index 0000000..1830dbf
--- /dev/null
+++ b/core/proto/android/service/power.proto
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.service.power;
+
+option java_multiple_files = true;
+option java_outer_classname = "PowerServiceProto";
+
+import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/os/worksource.proto";
+import "frameworks/base/core/proto/android/service/wirelesschargerdetector.proto";
+
+message PowerServiceDumpProto {
+ message ConstantsProto {
+ bool is_no_cached_wake_locks = 1;
+ }
+ message ActiveWakeLocksProto {
+ bool is_cpu = 1;
+ bool is_screen_bright = 2;
+ bool is_screen_dim = 3;
+ bool is_button_bright = 4;
+ bool is_proximity_screen_off = 5;
+ // only set if already awake
+ bool is_stay_awake = 6;
+ bool is_doze = 7;
+ bool is_draw = 8;
+ }
+ message UserActivityProto {
+ bool is_screen_bright = 1;
+ bool is_screen_dim = 2;
+ bool is_screen_dream = 3;
+ }
+ message UidProto {
+ // Enum values gotten from ActivityManager.java
+ enum ProcessState {
+ // Process is a persistent system process.
+ PROCESS_STATE_PERSISTENT = 0;
+ // Process is a persistent system process and is doing UI.
+ PROCESS_STATE_PERSISTENT_UI = 1;
+ // Process is hosting the current top activities. Note that this
+ // covers all activities that are visible to the user.
+ PROCESS_STATE_TOP = 2;
+ // Process is hosting a foreground service due to a system binding.
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
+ // Process is hosting a foreground service.
+ PROCESS_STATE_FOREGROUND_SERVICE = 4;
+ // Same as {@link #PROCESS_STATE_TOP} but while device is sleeping.
+ PROCESS_STATE_TOP_SLEEPING = 5;
+ // Process is important to the user, and something they are aware of.
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
+ // Process is important to the user, but not something they are aware of.
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
+ // Process is in the background running a backup/restore operation.
+ PROCESS_STATE_BACKUP = 8;
+ // Process is in the background, but it can't restore its state so
+ // we want to try to avoid killing it.
+ PROCESS_STATE_HEAVY_WEIGHT = 9;
+ // Process is in the background running a service.
+ PROCESS_STATE_SERVICE = 10;
+ // Process is in the background running a receiver.
+ PROCESS_STATE_RECEIVER = 11;
+ // Process is in the background but hosts the home activity.
+ PROCESS_STATE_HOME = 12;
+ // Process is in the background but hosts the last shown activity.
+ PROCESS_STATE_LAST_ACTIVITY = 13;
+ // Process is being cached for later use and contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY = 14;
+ // Process is being cached for later use and is a client of another
+ // cached process that contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
+ // Process is being cached for later use and is empty.
+ PROCESS_STATE_CACHED_EMPTY = 16;
+ // Process does not exist.
+ PROCESS_STATE_NONEXISTENT = 17;
+ }
+ int32 uid = 1;
+ string uid_string = 2;
+ bool is_active = 3;
+ int32 num_wake_locks = 4;
+ bool is_process_state_unknown = 5;
+ ProcessState process_state = 6;
+ }
+
+ // Enum values gotten from PowerManagerInternal.java
+ enum Wakefulness {
+ WAKEFULNESS_ASLEEP = 0;
+ WAKEFULNESS_AWAKE = 1;
+ WAKEFULNESS_DREAMING = 2;
+ WAKEFULNESS_DOZING = 3;
+ WAKEFULNESS_UNKNOWN = 4;
+ }
+ // Enum values gotten from BatteryManager.java
+ enum PlugType {
+ PLUG_TYPE_NONE = 0;
+ PLUG_TYPE_PLUGGED_AC = 1;
+ PLUG_TYPE_PLUGGED_USB = 2;
+ PLUG_TYPE_PLUGGED_WIRELESS = 4;
+ }
+ // Enum values gotten from Intent.java
+ enum DockState {
+ DOCK_STATE_UNDOCKED = 0;
+ DOCK_STATE_DESK = 1;
+ DOCK_STATE_CAR = 2;
+ DOCK_STATE_LE_DESK = 3;
+ DOCK_STATE_HE_DESK = 4;
+ }
+
+ ConstantsProto constants = 1;
+ // A bitfield that indicates what parts of the power state have
+ // changed and need to be recalculated.
+ int32 dirty = 2;
+ // Indicates whether the device is awake or asleep or somewhere in between.
+ Wakefulness wakefulness = 3;
+ bool is_wakefulness_changing = 4;
+ // True if the device is plugged into a power source.
+ bool is_powered = 5;
+ // The current plug type
+ PlugType plug_type = 6;
+ // The current battery level percentage.
+ int32 battery_level = 7;
+ // The battery level percentage at the time the dream started.
+ int32 battery_level_when_dream_started = 8;
+ // The current dock state.
+ DockState dock_state = 9;
+ // True if the device should stay on.
+ bool is_stay_on = 10;
+ // True if the proximity sensor reads a positive result.
+ bool is_proximity_positive = 11;
+ // True if boot completed occurred. We keep the screen on until this happens.
+ bool is_boot_completed = 12;
+ // True if systemReady() has been called.
+ bool is_system_ready = 13;
+ // True if auto-suspend mode is enabled.
+ bool is_hal_auto_suspend_mode_enabled = 14;
+ // True if interactive mode is enabled.
+ bool is_hal_auto_interactive_mode_enabled = 15;
+ // Summarizes the state of all active wakelocks.
+ ActiveWakeLocksProto active_wake_locks = 16;
+ // Have we scheduled a message to check for long wake locks? This is when
+ // we will check. (In milliseconds timestamp)
+ int64 notify_long_scheduled_ms = 17;
+ // Last time we checked for long wake locks. (In milliseconds timestamp)
+ int64 notify_long_dispatched_ms = 18;
+ // The time we decided to do next long check. (In milliseconds timestamp)
+ int64 notify_long_next_check_ms = 19;
+ // Summarizes the effect of the user activity timer.
+ UserActivityProto user_activity = 20;
+ // If true, instructs the display controller to wait for the proximity
+ // sensor to go negative before turning the screen on.
+ bool is_request_wait_for_negative_proximity = 21;
+ // True if MSG_SANDMAN has been scheduled.
+ bool is_sandman_scheduled = 22;
+ // True if the sandman has just been summoned for the first time since entering
+ // the dreaming or dozing state. Indicates whether a new dream should begin.
+ bool is_sandman_summoned = 23;
+ // If true, the device is in low power mode.
+ bool is_low_power_mode_enabled = 24;
+ // True if the battery level is currently considered low.
+ bool is_battery_level_low = 25;
+ // True if we are currently in light device idle mode.
+ bool is_light_device_idle_mode = 26;
+ // True if we are currently in device idle mode.
+ bool is_device_idle_mode = 27;
+ // Set of app ids that we will always respect the wake locks for.
+ repeated int32 device_idle_whitelist = 28;
+ // Set of app ids that are temporarily allowed to acquire wakelocks due to
+ // high-pri message
+ repeated int32 device_idle_temp_whitelist = 29;
+ // Timestamp of the last time the device was awoken.
+ int64 last_wake_time_ms = 30;
+ // Timestamp of the last time the device was put to sleep.
+ int64 last_sleep_time_ms = 31;
+ // Timestamp of the last call to user activity.
+ int64 last_user_activity_time_ms = 32;
+ int64 last_user_activity_time_no_change_lights_ms = 33;
+ // Timestamp of last interactive power hint.
+ int64 last_interactive_power_hint_time_ms = 34;
+ // Timestamp of the last screen brightness boost.
+ int64 last_screen_brightness_boost_time_ms = 35;
+ // True if screen brightness boost is in progress.
+ bool is_screen_brightness_boost_in_progress = 36;
+ // True if the display power state has been fully applied, which means the
+ // display is actually on or actually off or whatever was requested.
+ bool is_display_ready = 37;
+ // True if the wake lock suspend blocker has been acquired.
+ bool is_holding_wake_lock_suspend_blocker = 38;
+ // The suspend blocker used to keep the CPU alive when the display is on, the
+ // display is getting ready or there is user activity (in which case the
+ // display must be on).
+ bool is_holding_display_suspend_blocker = 39;
+ // Settings and configuration
+ PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 40;
+ // Sleep timeout in ms
+ sint32 sleep_timeout_ms = 41;
+ // Screen off timeout in ms
+ int32 screen_off_timeout_ms = 42;
+ // Screen dim duration in ms
+ int32 screen_dim_duration_ms = 43;
+ // We are currently in the middle of a batch change of uids.
+ bool are_uids_changing = 44;
+ // Some uids have actually changed while mUidsChanging was true.
+ bool are_uids_changed = 45;
+ // List of UIDs and their states
+ repeated UidProto uids = 46;
+ android.os.LooperProto looper = 47;
+ // List of all wake locks acquired by applications.
+ repeated WakeLockProto wake_locks = 48;
+ // List of all suspend blockers.
+ repeated SuspendBlockerProto suspend_blockers = 49;
+ WirelessChargerDetectorProto wireless_charger_detector = 50;
+}
+
+message SuspendBlockerProto {
+ string name = 1;
+ int32 reference_count = 2;
+}
+
+message WakeLockProto {
+ message WakeLockFlagsProto {
+ // Turn the screen on when the wake lock is acquired.
+ bool is_acquire_causes_wakeup = 1;
+ // When this wake lock is released, poke the user activity timer
+ // so the screen stays on for a little longer.
+ bool is_on_after_release = 2;
+ }
+
+ // Enum values gotten from PowerManager.java
+ enum LockLevel {
+ WAKE_LOCK_INVALID = 0;
+ // Ensures that the CPU is running.
+ PARTIAL_WAKE_LOCK = 1;
+ // Ensures that the screen is on (but may be dimmed).
+ SCREEN_DIM_WAKE_LOCK = 6;
+ // Ensures that the screen is on at full brightness.
+ SCREEN_BRIGHT_WAKE_LOCK = 10;
+ // Ensures that the screen and keyboard backlight are on at full brightness.
+ FULL_WAKE_LOCK = 26;
+ // Turns the screen off when the proximity sensor activates.
+ PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
+ // Put the screen in a low power state and allow the CPU to suspend
+ // if no other wake locks are held.
+ DOZE_WAKE_LOCK = 64;
+ // Keep the device awake enough to allow drawing to occur.
+ DRAW_WAKE_LOCK = 128;
+ }
+
+ LockLevel lock_level = 1;
+ string tag = 2;
+ WakeLockFlagsProto flags = 3;
+ bool is_disabled = 4;
+ // Acquire time in ms
+ int64 acq_ms = 5;
+ bool is_notified_long = 6;
+ // Owner UID
+ int32 uid = 7;
+ // Owner PID
+ int32 pid = 8;
+ android.os.WorkSourceProto work_source = 9;
+}
+
+message PowerServiceSettingsAndConfigurationDumpProto {
+ message StayOnWhilePluggedInProto {
+ bool is_stay_on_while_plugged_in_ac = 1;
+ bool is_stay_on_while_plugged_in_usb = 2;
+ bool is_stay_on_while_plugged_in_wireless = 3;
+ }
+ message ScreenBrightnessSettingLimitsProto {
+ int32 setting_minimum = 1;
+ int32 setting_maximum = 2;
+ int32 setting_default = 3;
+ int32 setting_for_vr_default = 4;
+ }
+
+ // Enum values gotten from Settings.java
+ enum ScreenBrightnessMode {
+ SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
+ SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
+ }
+ // Enum values gotten from Display.java
+ enum DisplayState {
+ DISPLAY_STATE_UNKNOWN = 0;
+ DISPLAY_STATE_OFF = 1;
+ DISPLAY_STATE_ON = 2;
+ DISPLAY_STATE_DOZE = 3;
+ DISPLAY_STATE_DOZE_SUSPEND = 4;
+ DISPLAY_STATE_VR = 5;
+ }
+
+
+ // True to decouple auto-suspend mode from the display state.
+ bool is_decouple_hal_auto_suspend_mode_from_display_config = 1;
+ // True to decouple interactive mode from the display state.
+ bool is_decouple_hal_interactive_mode_from_display_config = 2;
+ // True if the device should wake up when plugged or unplugged.
+ bool is_wake_up_when_plugged_or_unplugged_config = 3;
+ // True if the device should wake up when plugged or unplugged in theater mode.
+ bool is_wake_up_when_plugged_or_unplugged_in_theater_mode_config = 4;
+ // True if theater mode is enabled
+ bool is_theater_mode_enabled = 5;
+ // True if the device should suspend when the screen is off due to proximity.
+ bool is_suspend_when_screen_off_due_to_proximity_config = 6;
+ // True if dreams are supported on this device.
+ bool are_dreams_supported_config = 7;
+ // Default value for dreams enabled
+ bool are_dreams_enabled_by_default_config = 8;
+ // Default value for dreams activate-on-sleep
+ bool are_dreams_activated_on_sleep_by_default_config = 9;
+ // Default value for dreams activate-on-dock
+ bool are_dreams_activated_on_dock_by_default_config = 10;
+ // True if dreams can run while not plugged in.
+ bool are_dreams_enabled_on_battery_config = 11;
+ // Minimum battery level to allow dreaming when powered.
+ // Use -1 to disable this safety feature.
+ sint32 dreams_battery_level_minimum_when_powered_config = 12;
+ // Minimum battery level to allow dreaming when not powered.
+ // Use -1 to disable this safety feature.
+ sint32 dreams_battery_level_minimum_when_not_powered_config = 13;
+ // If the battery level drops by this percentage and the user activity
+ // timeout has expired, then assume the device is receiving insufficient
+ // current to charge effectively and terminate the dream. Use -1 to disable
+ // this safety feature.
+ sint32 dreams_battery_level_drain_cutoff_config = 14;
+ // True if dreams are enabled by the user.
+ bool are_dreams_enabled_setting = 15;
+ // True if dreams should be activated on sleep.
+ bool are_dreams_activate_on_sleep_setting = 16;
+ // True if dreams should be activated on dock.
+ bool are_dreams_activate_on_dock_setting = 17;
+ // True if doze should not be started until after the screen off transition.
+ bool is_doze_after_screen_off_config = 18;
+ // If true, the device is in low power mode.
+ bool is_low_power_mode_setting = 19;
+ // Current state of whether the settings are allowing auto low power mode.
+ bool is_auto_low_power_mode_configured = 20;
+ // The user turned off low power mode below the trigger level
+ bool is_auto_low_power_mode_snoozing = 21;
+ // The minimum screen off timeout, in milliseconds.
+ int32 minimum_screen_off_timeout_config_ms = 22;
+ // The screen dim duration, in milliseconds.
+ int32 maximum_screen_dim_duration_config_ms = 23;
+ // The maximum screen dim time expressed as a ratio relative to the screen off timeout.
+ float maximum_screen_dim_ratio_config = 24;
+ // The screen off timeout setting value in milliseconds.
+ int32 screen_off_timeout_setting_ms = 25;
+ // The sleep timeout setting value in milliseconds.
+ sint32 sleep_timeout_setting_ms = 26;
+ // The maximum allowable screen off timeout according to the device administration policy.
+ int32 maximum_screen_off_timeout_from_device_admin_ms = 27;
+ bool is_maximum_screen_off_timeout_from_device_admin_enforced_locked = 28;
+ // The stay on while plugged in setting.
+ // A set of battery conditions under which to make the screen stay on.
+ StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
+ // The screen brightness setting, from 0 to 255.
+ // Use -1 if no value has been set.
+ sint32 screen_brightness_setting = 30;
+ // The screen auto-brightness adjustment setting, from -1 to 1.
+ // Use 0 if there is no adjustment.
+ float screen_auto_brightness_adjustment_setting = 31;
+ // The screen brightness mode.
+ ScreenBrightnessMode screen_brightness_mode_setting = 32;
+ // The screen brightness setting override from the window manager
+ // to allow the current foreground activity to override the brightness.
+ // Use -1 to disable.
+ sint32 screen_brightness_override_from_window_manager = 33;
+ // The user activity timeout override from the window manager
+ // to allow the current foreground activity to override the user activity
+ // timeout. Use -1 to disable.
+ sint64 user_activity_timeout_override_from_window_manager_ms = 34;
+ // The window manager has determined the user to be inactive via other means.
+ // Set this to false to disable.
+ bool is_user_inactive_override_from_window_manager = 35;
+ // The screen brightness setting override from the settings application
+ // to temporarily adjust the brightness until next updated,
+ // Use -1 to disable.
+ sint32 temporary_screen_brightness_setting_override = 36;
+ // The screen brightness adjustment setting override from the settings
+ // application to temporarily adjust the auto-brightness adjustment factor
+ // until next updated, in the range -1..1.
+ // Use NaN to disable.
+ float temporary_screen_auto_brightness_adjustment_setting_override = 37;
+ // The screen state to use while dozing.
+ DisplayState doze_screen_state_override_from_dream_manager = 38;
+ // The screen brightness to use while dozing.
+ float dozed_screen_brightness_override_from_dream_manager = 39;
+ // Screen brightness settings limits.
+ ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 40;
+ // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
+ int32 screen_brightness_for_vr_setting = 41;
+ // True if double tap to wake is enabled
+ bool is_double_tap_wake_enabled = 42;
+ // True if we are currently in VR Mode.
+ bool is_vr_mode_enabled = 43;
+}
diff --git a/core/proto/android/service/wirelesschargerdetector.proto b/core/proto/android/service/wirelesschargerdetector.proto
new file mode 100644
index 0000000..7ba7c17
--- /dev/null
+++ b/core/proto/android/service/wirelesschargerdetector.proto
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.service.power;
+
+option java_multiple_files = true;
+
+message WirelessChargerDetectorProto {
+ message VectorProto {
+ float x = 1;
+ float y = 2;
+ float z = 3;
+ }
+
+ // Previously observed wireless power state.
+ bool is_powered_wirelessly = 1;
+ // True if the device is thought to be at rest on a wireless charger.
+ bool is_at_rest = 2;
+ // The gravity vector most recently observed while at rest.
+ VectorProto rest = 3;
+ // True if detection is in progress.
+ bool is_detection_in_progress = 4;
+ // The time when detection was last performed.
+ int64 detection_start_time_ms = 5;
+ // True if the rest position should be updated if at rest.
+ bool is_must_update_rest_position = 6;
+ // The total number of samples collected.
+ int32 total_samples = 7;
+ // The number of samples collected that showed evidence of not being at rest.
+ int32 moving_samples = 8;
+ // The value of the first sample that was collected.
+ VectorProto first_sample = 9;
+ // The value of the last sample that was collected.
+ VectorProto last_sample = 10;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 64759ff..b3ae891 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2138,7 +2138,7 @@
runtime resource overlays.
@hide -->
<permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged" />
<!-- ========================================= -->
<!-- Permissions for special development tools -->
diff --git a/core/res/res/layout/app_permission_item_money.xml b/core/res/res/layout/app_permission_item_money.xml
index 2056285..764c883 100644
--- a/core/res/res/layout/app_permission_item_money.xml
+++ b/core/res/res/layout/app_permission_item_money.xml
@@ -57,14 +57,14 @@
android:layout_alignParentStart="true"
android:layout_alignBottom="@+id/perm_money_label"
android:scaleType="fitCenter"
- android:tint="@color/perms_costs_money"
+ android:tint="?android:attr/colorError"
android:tintMode="src_in"
android:src="@android:drawable/ic_coins_s" />
<TextView
android:id="@+id/perm_money_label"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="16sp"
- android:textColor="@color/perms_costs_money"
+ android:textColor="?android:attr/colorError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/perm_money_icon"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index d55a012..8453cd35 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -14,44 +14,79 @@
limitations under the License.
-->
-<!-- TODO(b/33197203) remove hardcoded color once color is final -->
-<RelativeLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:paddingTop="16dip"
android:paddingBottom="16dip"
- android:background="#FDF8C8">
+ android:elevation="16dp"
+ android:background="?android:attr/colorBackground"
+ android:orientation="vertical">
- <!-- TODO(b/33197203) use.R.string once final wording is done -->
- <TextView
- android:id="@+id/autofill_save_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Save for autofill?"
- android:singleLine="true"/>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
- <TextView
- android:id="@+id/autofill_save_no"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/autofill_save_title"
- android:layout_toLeftOf="@+id/autofill_save_yes"
- android:layout_marginRight="16dip"
- android:text="No thanks"
- android:textAllCaps="true"
- android:singleLine="true"/>
+ <TextView
+ android:id="@+id/autofill_save_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_title"
+ android:singleLine="true">
+ </TextView>
- <TextView
- android:id="@+id/autofill_save_yes"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/autofill_save_title"
- android:layout_alignParentRight="true"
- android:text="Save"
- android:textAllCaps="true"
- android:singleLine="true"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" >
+ </Space>
-</RelativeLayout>
+ <ImageView
+ android:id="@+id/autofill_save_close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@android:drawable/ic_close"
+ android:background="?android:attr/selectableItemBackgroundBorderless">
+ </ImageView>
+
+ </LinearLayout>
+
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginTop="16dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" >
+ </Space>
+
+ <Button
+ android:id="@+id/autofill_save_no"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/autofill_save_no" >
+ </Button>
+
+ <Button
+ android:id="@+id/autofill_save_yes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/autofill_save_yes" >
+ </Button>
+
+ </com.android.internal.widget.ButtonBarLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/time_picker_text_input_material.xml b/core/res/res/layout/time_picker_text_input_material.xml
index f17b80e..632a4c1 100644
--- a/core/res/res/layout/time_picker_text_input_material.xml
+++ b/core/res/res/layout/time_picker_text_input_material.xml
@@ -77,7 +77,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/input_hour"
android:layout_alignStart="@id/input_hour"
- android:textColor="?attr/textColorError"
+ android:textColor="?attr/colorError"
android:text="@string/time_picker_input_error" />
</RelativeLayout>
<Spinner
@@ -87,4 +87,4 @@
android:layout_alignBaseline="@id/input_block"
android:layout_alignParentEnd="true"/>
-</merge>
\ No newline at end of file
+</merge>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8031f19..f55538f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -66,6 +66,9 @@
<attr name="primaryContentAlpha" format="float" />
<!-- The alpha applied to the foreground color to create the secondary text color. -->
<attr name="secondaryContentAlpha" format="float" />
+ <!-- Color used for error states and things that need to be drawn to
+ the users attention.. -->
+ <attr name="colorError" format="reference|color" />
<!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
<attr name="backgroundDimAmount" format="float" />
<!-- Control whether dimming behind the window is enabled. The default
@@ -135,9 +138,6 @@
<!-- Color of list item text in alert dialogs. -->
<attr name="textColorAlertDialogListItem" format="reference|color" />
- <!-- Text color for errors. -->
- <attr name="textColorError" format="reference|color" />
-
<!-- Search widget more corpus result item background. -->
<attr name="searchWidgetCorpusItemBackground" format="reference|color" />
@@ -8531,4 +8531,6 @@
<attr name="reverseLayout" format="boolean" />
<attr name="stackFromEnd" format="boolean" />
</declare-styleable>
+
+ <attr name="lockPatternStyle" format="reference" />
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b28c6f2..6015ed5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -75,8 +75,6 @@
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
<color name="input_method_navigation_guard">#ff000000</color>
- <color name="system_error">#fff4511e</color> <!-- deep orange 600 -->
-
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
@@ -88,7 +86,6 @@
<color name="perms_dangerous_grp_color">#33b5e5</color>
<color name="perms_dangerous_perm_color">#33b5e5</color>
<color name="shadow">#cc222222</color>
- <color name="perms_costs_money">#fff4511e</color>
<!-- For search-related UIs -->
<color name="search_url_text_normal">#7fa87f</color>
@@ -121,7 +118,6 @@
<!-- LockPatternView -->
<color name="lock_pattern_view_regular_color">#ffffffff</color>
<color name="lock_pattern_view_success_color">#ffffffff</color>
- <color name="lock_pattern_view_error_color">@color/system_error</color>
<!-- FaceLock -->
<color name="facelock_spotlight_mask">#CC000000</color>
@@ -156,7 +152,6 @@
<color name="accessibility_focus_highlight">#bf39b500</color>
<color name="system_notification_accent_color">#ff607D8B</color>
- <color name="battery_saver_mode_color">#fff4511e</color><!-- deep orange 600 -->
<!-- Default user icon colors -->
<color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 -->
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 0a24565..e0cc5b5 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -46,6 +46,7 @@
<color name="button_material_dark">#ff5a595b</color>
<color name="button_material_light">#ffd6d7d7</color>
+ <color name="error_color_material">#F4511E</color>
<color name="switch_thumb_normal_material_dark">#ffbdbdbd</color>
<color name="switch_thumb_normal_material_light">#fff1f1f1</color>
@@ -65,9 +66,6 @@
<!-- 70% white -->
<color name="secondary_text_default_material_dark">#b3ffffff</color>
- <color name="error_text_material_light">@color/material_red_A700</color>
- <color name="error_text_material_dark">@color/material_red_A100</color>
-
<item name="hint_alpha_material_dark" format="float" type="dimen">0.50</item>
<item name="hint_alpha_material_light" format="float" type="dimen">0.38</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d233e24..35aff80 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2748,8 +2748,8 @@
<!-- Flag indicates that whether non-system apps can be installed on internal storage. -->
<bool name="config_allow3rdPartyAppOnInternal">true</bool>
- <!-- Component name of the default cell broadcast receiver -->
- <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
+ <!-- Package name of the default cell broadcast receiver -->
+ <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
<string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 78549b5..e5660b5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2780,7 +2780,7 @@
<public name="targetProcess" />
<public name="nextClusterForward" />
<public name="__removed1" />
- <public name="textColorError" />
+ <public name="colorError" />
<public name="focusedByDefault" />
<public name="appCategory" />
<public name="autoSizeMaxTextSize" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f6da660..6abc009 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4510,4 +4510,11 @@
<!-- Accessibility string used for describing the button in time picker that changes the dialog to circular clock mode. [CHAR LIMIT=NONE] -->
<string name="time_picker_radial_mode_description">Switch to clock mode for the time input.</string>
+ <!-- Title for the auto-fill save dialog shown when the user entered savable text [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_title">Save to <xliff:g id="label" example="MyPass">%1$s</xliff:g>?</string>
+ <!-- Label for the auto-fill save button [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_yes">Save</string>
+ <!-- Label for the auto-fill cancel button [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_no">No thanks</string>
+
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index faf451b..25873d2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1475,4 +1475,10 @@
<item name="padding">16dp</item>
</style>
+ <style name="Widget.LockPatternView">
+ <item name="regularColor">@color/lock_pattern_view_regular_color</item>
+ <item name="errorColor">?attr/colorError</item>
+ <item name="successColor">@color/lock_pattern_view_success_color</item>
+ </style>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eb248e5..49216d9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1195,7 +1195,6 @@
<java-symbol type="drawable" name="btn_check_off" />
<java-symbol type="color" name="lock_pattern_view_regular_color" />
<java-symbol type="color" name="lock_pattern_view_success_color" />
- <java-symbol type="color" name="lock_pattern_view_error_color" />
<java-symbol type="dimen" name="lock_pattern_dot_line_width" />
<java-symbol type="dimen" name="lock_pattern_dot_size" />
<java-symbol type="dimen" name="lock_pattern_dot_size_activated" />
@@ -2310,7 +2309,6 @@
<java-symbol type="layout" name="select_dialog_singlechoice_material" />
<java-symbol type="layout" name="select_dialog_multichoice_material" />
<java-symbol type="array" name="no_ems_support_sim_operators" />
- <java-symbol type="color" name="battery_saver_mode_color" />
<java-symbol type="color" name="system_notification_accent_color" />
<java-symbol type="dimen" name="text_handle_min_size" />
<java-symbol type="id" name="transitionTransform" />
@@ -2798,7 +2796,7 @@
<java-symbol type="drawable" name="lockscreen_selected" />
<java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
- <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" />
+ <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" />
<java-symbol type="color" name="notification_primary_text_color_light" />
<java-symbol type="color" name="notification_primary_text_color_dark" />
@@ -2842,6 +2840,10 @@
<java-symbol type="id" name="autofill_save_title" />
<java-symbol type="id" name="autofill_save_no" />
<java-symbol type="id" name="autofill_save_yes" />
+ <java-symbol type="id" name="autofill_save_close" />
+ <java-symbol type="string" name="autofill_save_title" />
+ <java-symbol type="string" name="autofill_save_yes" />
+ <java-symbol type="string" name="autofill_save_no" />
<!-- Accessibility fingerprint gestures -->
<java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
@@ -2849,7 +2851,7 @@
<!-- android.service.trust -->
<java-symbol type="bool" name="config_allowEscrowTokenForTrustAgent"/>
-
+
<!-- Time picker -->
<java-symbol type="id" name="toggle_mode"/>
<java-symbol type="id" name="input_mode"/>
@@ -2876,6 +2878,8 @@
<java-symbol type="string" name="alert_windows_notification_message" />
<java-symbol type="string" name="alert_windows_notification_turn_off_action" />
<java-symbol type="drawable" name="alert_window_layer" />
+ <java-symbol type="style" name="Widget.LockPatternView" />
+ <java-symbol type="attr" name="lockPatternStyle" />
<!-- Colon separated list of package names that should be granted Notification Listener access -->
<java-symbol type="string" name="config_defaultListenerAccessPackages" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index e357678..d100c63 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -63,6 +63,7 @@
<item name="colorControlHighlight">@color/legacy_button_pressed</item>
<item name="colorButtonNormal">@color/legacy_button_normal</item>
<item name="colorEdgeEffect">?attr/colorPrimary</item>
+ <item name="colorError">@color/red</item>
<item name="disabledAlpha">0.5</item>
<item name="backgroundDimAmount">0.6</item>
@@ -93,7 +94,6 @@
<item name="textColorLink">@color/link_text_dark</item>
<item name="textColorLinkInverse">@color/link_text_light</item>
<item name="textColorAlertDialogListItem">@color/primary_text_light_disable_only</item>
- <item name="textColorError">@color/red</item>
<item name="textAppearanceLarge">@style/TextAppearance.Large</item>
<item name="textAppearanceMedium">@style/TextAppearance.Medium</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 400fb47..008c817 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -51,6 +51,7 @@
<item name="primaryContentAlpha">@dimen/primary_content_alpha_material_dark</item>
<item name="secondaryContentAlpha">@dimen/secondary_content_alpha_material_dark</item>
<item name="backgroundDimAmount">0.6</item>
+ <item name="colorError">@color/error_color_material</item>
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.Material</item>
@@ -73,7 +74,6 @@
<item name="textColorLinkInverse">?attr/colorAccent</item>
<item name="textColorSearchUrl">@color/search_url_text_material_dark</item>
<item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item>
- <item name="textColorError">@color/error_text_material_dark</item>
<item name="textAppearanceLarge">@style/TextAppearance.Material.Large</item>
<item name="textAppearanceLargeInverse">@style/TextAppearance.Material.Large.Inverse</item>
@@ -441,7 +441,6 @@
<item name="textColorLinkInverse">?attr/colorAccent</item>
<item name="textColorSearchUrl">@color/search_url_text_material_light</item>
<item name="textColorAlertDialogListItem">@color/primary_text_material_light</item>
- <item name="textColorError">@color/error_text_material_light</item>
<item name="textAppearanceLarge">@style/TextAppearance.Material.Large</item>
<item name="textAppearanceLargeInverse">@style/TextAppearance.Material.Large.Inverse</item>
@@ -823,7 +822,6 @@
<item name="textColorHighlightInverse">@color/highlighted_text_material</item>
<item name="textColorSearchUrl">@color/search_url_text_material_light</item>
<item name="textColorAlertDialogListItem">@color/primary_text_material_light</item>
- <item name="textColorError">@color/error_text_material_light</item>
<item name="textCheckMark">@drawable/indicator_check_mark_light</item>
<item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item>
@@ -856,7 +854,6 @@
<item name="textColorHighlightInverse">@color/highlighted_text_material</item>
<item name="textColorSearchUrl">@color/search_url_text_material_dark</item>
<item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item>
- <item name="textColorError">@color/error_text_material_dark</item>
<item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
<item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index bdc0200..497bc5c 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -3,61 +3,62 @@
import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.fail;
+import static junit.framework.TestCase.assertEquals;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+
+import android.Manifest.permission;
import android.content.Context;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IRemoteCallback;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Unit test for the {@link NetworkRecommendationProvider}.
*/
-public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class NetworkRecommendationProviderTest {
@Mock private IRemoteCallback mMockRemoteCallback;
+ @Mock private Context mContext;
private NetworkRecProvider mRecProvider;
- private Handler mHandler;
private INetworkRecommendationProvider mStub;
private CountDownLatch mRecRequestLatch;
private CountDownLatch mScoreRequestLatch;
private NetworkKey[] mTestNetworkKeys;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
- // Configuration needed to make mockito/dexcache work.
- final Context context = getInstrumentation().getTargetContext();
- System.setProperty("dexmaker.dexcache",
- context.getCacheDir().getPath());
- ClassLoader newClassLoader = getInstrumentation().getClass().getClassLoader();
- Thread.currentThread().setContextClassLoader(newClassLoader);
-
MockitoAnnotations.initMocks(this);
- HandlerThread thread = new HandlerThread("NetworkRecommendationProviderTest");
- thread.start();
+ Executor executor = Executors.newSingleThreadExecutor();
mRecRequestLatch = new CountDownLatch(1);
mScoreRequestLatch = new CountDownLatch(1);
- mHandler = new Handler(thread.getLooper());
- mRecProvider = new NetworkRecProvider(mHandler, mRecRequestLatch, mScoreRequestLatch);
+ mRecProvider = new NetworkRecProvider(mContext, executor, mRecRequestLatch,
+ mScoreRequestLatch);
mStub = INetworkRecommendationProvider.Stub.asInterface(mRecProvider.getBinder());
mTestNetworkKeys = new NetworkKey[2];
mTestNetworkKeys[0] = new NetworkKey(new WifiKey("\"ssid_01\"", "00:00:00:00:00:11"));
mTestNetworkKeys[1] = new NetworkKey(new WifiKey("\"ssid_02\"", "00:00:00:00:00:22"));
}
- @MediumTest
+ @Test
public void testRecommendationRequestReceived() throws Exception {
final RecommendationRequest request = new RecommendationRequest.Builder().build();
final int sequence = 100;
@@ -71,7 +72,23 @@
assertEquals(expectedResultCallback, mRecProvider.mCapturedCallback);
}
- @SmallTest
+ @Test
+ public void testRecommendationRequest_permissionsEnforced() throws Exception {
+ final RecommendationRequest request = new RecommendationRequest.Builder().build();
+ final int sequence = 100;
+ Mockito.doThrow(new SecurityException())
+ .when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString());
+
+ try {
+ mStub.requestRecommendation(request, mMockRemoteCallback, sequence);
+ fail("SecurityException expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
public void testResultCallbackOnResult() throws Exception {
final int sequence = 100;
final NetworkRecommendationProvider.ResultCallback callback =
@@ -87,7 +104,7 @@
assertSame(result, capturedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT));
}
- @SmallTest
+ @Test
public void testResultCallbackOnResult_runTwice_throwsException() throws Exception {
final int sequence = 100;
final NetworkRecommendationProvider.ResultCallback callback =
@@ -104,7 +121,7 @@
}
}
- @MediumTest
+ @Test
public void testScoreRequestReceived() throws Exception {
mStub.requestScores(mTestNetworkKeys);
@@ -114,7 +131,7 @@
assertSame(mTestNetworkKeys, mRecProvider.mCapturedNetworks);
}
- @MediumTest
+ @Test
public void testScoreRequest_nullInput() throws Exception {
mStub.requestScores(null);
@@ -122,7 +139,7 @@
assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
}
- @MediumTest
+ @Test
public void testScoreRequest_emptyInput() throws Exception {
mStub.requestScores(new NetworkKey[0]);
@@ -130,6 +147,20 @@
assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testScoreRequest_permissionsEnforced() throws Exception {
+ Mockito.doThrow(new SecurityException())
+ .when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString());
+
+ try {
+ mStub.requestScores(mTestNetworkKeys);
+ fail("SecurityException expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
private static class NetworkRecProvider extends NetworkRecommendationProvider {
private final CountDownLatch mRecRequestLatch;
private final CountDownLatch mScoreRequestLatch;
@@ -137,9 +168,9 @@
ResultCallback mCapturedCallback;
NetworkKey[] mCapturedNetworks;
- NetworkRecProvider(Handler handler, CountDownLatch recRequestLatch,
+ NetworkRecProvider(Context context, Executor executor, CountDownLatch recRequestLatch,
CountDownLatch networkRequestLatch) {
- super(handler);
+ super(context, executor);
mRecRequestLatch = recRequestLatch;
mScoreRequestLatch = networkRequestLatch;
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index da6a294..b718263 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -393,6 +393,7 @@
Settings.Secure.BACKUP_TRANSPORT,
Settings.Secure.BLUETOOTH_HCI_LOG,
Settings.Secure.CARRIER_APPS_HANDLED,
+ Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
Settings.Secure.COMPLETED_CATEGORY_PREFIX,
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
Settings.Secure.DEFAULT_INPUT_METHOD,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 5226fe5..bf3e793 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -253,6 +253,7 @@
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_CONFIGURATION"/>
+ <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DELETE_CACHE_FILES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index ec00c45..b1a433c 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -805,7 +805,8 @@
*/
public enum Adaptation {
/**
- * Bradford matrix for the von Kries chromatic adaptation transform.
+ * Bradford chromatic adaptation transform, as defined in the
+ * CIECAM97s color appearance model.
*/
BRADFORD(new float[] {
0.8951f, -0.7502f, 0.0389f,
@@ -813,12 +814,21 @@
-0.1614f, 0.0367f, 1.0296f
}),
/**
- * von Kries matrix for the von Kries chromatic adaptation transform.
+ * von Kries chromatic adaptation transform.
*/
VON_KRIES(new float[] {
0.40024f, -0.22630f, 0.00000f,
0.70760f, 1.16532f, 0.00000f,
-0.08081f, 0.04570f, 0.91822f
+ }),
+ /**
+ * CIECAT02 chromatic adaption transform, as defined in the
+ * CIECAM02 color appearance model.
+ */
+ CIECAT02(new float[] {
+ 0.7328f, -0.7036f, 0.0030f,
+ 0.4296f, 1.6975f, 0.0136f,
+ -0.1624f, 0.0061f, 0.9834f
});
final float[] mTransform;
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 0a814f3..05fec5e 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -50,5 +50,5 @@
LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
LOCAL_MODULE := legacy-performance-test-hostdex
-include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
endif # HOST_OS == linux
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index acacd76..84111ae 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -202,15 +202,6 @@
*cookie = static_cast<int32_t>(mAssetPaths.size());
}
-#ifdef __ANDROID__
- // Load overlays, if any
- asset_path oap;
- for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
- oap.isSystemAsset = isSystemAsset;
- mAssetPaths.add(oap);
- }
-#endif
-
if (mResources != NULL) {
appendPathToResTable(ap, appAsLib);
}
@@ -493,11 +484,6 @@
}
bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
- // skip those ap's that correspond to system overlays
- if (ap.isSystemOverlay) {
- return true;
- }
-
Asset* ass = NULL;
ResTable* sharedRes = NULL;
bool shared = true;
@@ -539,14 +525,6 @@
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes = new ResTable();
sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
-#ifdef __ANDROID__
- const char* data = getenv("ANDROID_DATA");
- LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
- String8 overlaysListPath(data);
- overlaysListPath.appendPath(kResourceCache);
- overlaysListPath.appendPath("overlays.list");
- addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
-#endif
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
@@ -655,58 +633,6 @@
return ass;
}
-void AssetManager::addSystemOverlays(const char* pathOverlaysList,
- const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
-{
- FILE* fin = fopen(pathOverlaysList, "r");
- if (fin == NULL) {
- return;
- }
-
-#ifndef _WIN32
- if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) {
- fclose(fin);
- return;
- }
-#endif
- char buf[1024];
- while (fgets(buf, sizeof(buf), fin)) {
- // format of each line:
- // <path to apk><space><path to idmap><newline>
- char* space = strchr(buf, ' ');
- char* newline = strchr(buf, '\n');
- asset_path oap;
-
- if (space == NULL || newline == NULL || newline < space) {
- continue;
- }
-
- oap.path = String8(buf, space - buf);
- oap.type = kFileTypeRegular;
- oap.idmap = String8(space + 1, newline - space - 1);
- oap.isSystemOverlay = true;
-
- Asset* oass = const_cast<AssetManager*>(this)->
- openNonAssetInPathLocked("resources.arsc",
- Asset::ACCESS_BUFFER,
- oap);
-
- if (oass != NULL) {
- Asset* oidmap = openIdmapLocked(oap);
- offset++;
- sharedRes->add(oass, oidmap, offset + 1, false);
- const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
- const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
- delete oidmap;
- }
- }
-
-#ifndef _WIN32
- TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN));
-#endif
- fclose(fin);
-}
-
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
@@ -1446,20 +1372,6 @@
return mModWhen == modWhen;
}
-void AssetManager::SharedZip::addOverlay(const asset_path& ap)
-{
- mOverlays.add(ap);
-}
-
-bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
-{
- if (idx >= mOverlays.size()) {
- return false;
- }
- *out = mOverlays[idx];
- return true;
-}
-
AssetManager::SharedZip::~SharedZip()
{
if (kIsDebug) {
@@ -1578,22 +1490,6 @@
return true;
}
-void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
-{
- int idx = getIndex(path);
- sp<SharedZip> zip = mZipFile[idx];
- zip->addOverlay(overlay);
-}
-
-bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
-{
- sp<SharedZip> zip = SharedZip::get(path, false);
- if (zip == NULL) {
- return false;
- }
- return zip->getOverlay(idx, out);
-}
-
/*
* Compute the zip file's index.
*
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index becd307..f1e8b93 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -202,12 +202,10 @@
private:
struct asset_path
{
- asset_path() : path(""), type(kFileTypeRegular), idmap(""),
- isSystemOverlay(false), isSystemAsset(false) {}
+ asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemAsset(false) {}
String8 path;
FileType type;
String8 idmap;
- bool isSystemOverlay;
bool isSystemAsset;
};
@@ -237,9 +235,6 @@
Asset* openIdmapLocked(const struct asset_path& ap) const;
- void addSystemOverlays(const char* pathOverlaysList, const String8& targetPackagePath,
- ResTable* sharedRes, size_t offset) const;
-
class SharedZip : public RefBase {
public:
static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
@@ -254,9 +249,6 @@
bool isUpToDate();
- void addOverlay(const asset_path& ap);
- bool getOverlay(size_t idx, asset_path* out) const;
-
protected:
~SharedZip();
@@ -271,8 +263,6 @@
Asset* mResourceTableAsset;
ResTable* mResourceTable;
- Vector<asset_path> mOverlays;
-
static Mutex gLock;
static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
@@ -306,9 +296,6 @@
bool isUpToDate();
- void addOverlay(const String8& path, const asset_path& overlay);
- bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
-
private:
void closeZip(int idx);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 2931255..aad81df 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -213,10 +213,13 @@
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_RENDERER, prop, "opengl");
if (!strcmp(prop, "skiagl") ) {
+ ALOGD("Skia GL Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaGL;
} else if (!strcmp(prop, "skiavk") ) {
+ ALOGD("Skia Vulkan Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaVulkan;
} else { //"opengl"
+ ALOGD("HWUI GL Pipeline");
sRenderPipelineType = RenderPipelineType::OpenGL;
}
return sRenderPipelineType;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index f2b0eb3..ae13131 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -26,7 +26,6 @@
#include "SkiaProfileRenderer.h"
#include "utils/TraceUtils.h"
-#include <android/native_window.h>
#include <cutils/properties.h>
#include <strings.h>
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index c63dce1..d28e605 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -31,7 +31,6 @@
#include <GrTypes.h>
#include <vk/GrVkTypes.h>
-#include <android/native_window.h>
#include <cutils/properties.h>
#include <strings.h>
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 860725b..44af5fd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -133,7 +133,7 @@
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
- options.fDisableDistanceFieldPaths = true;
+ options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField;
options.fAllowPathMaskCaching = true;
mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
(GrBackendContext)glInterface.get(), options));
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index acd6110..e1ae585 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -24,7 +24,6 @@
#include "renderstate/RenderState.h"
#include "OpenGLReadback.h"
-#include <android/native_window.h>
#include <cutils/properties.h>
#include <strings.h>
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index a391d1e..95d9459 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -579,7 +579,7 @@
SkPaint textPaint;
textPaint.setAntiAlias(true);
textPaint.setTextSize(20);
- textPaint.setStrikeThruText(true);
+ textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag);
textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
for (int i = 0; i < LOOPS; i++) {
TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 669f03c..b2ea9acc 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -200,8 +200,18 @@
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
- paint.setUnderlineText(i != 0);
- paint.setStrikeThruText(j != 0);
+ uint32_t flags = paint.getFlags();
+ if (i != 0) {
+ flags |= SkPaint::kUnderlineText_ReserveFlag;
+ } else {
+ flags &= ~SkPaint::kUnderlineText_ReserveFlag;
+ }
+ if (j != 0) {
+ flags |= SkPaint::kStrikeThruText_ReserveFlag;
+ } else {
+ flags &= ~SkPaint::kStrikeThruText_ReserveFlag;
+ }
+ paint.setFlags(flags);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
}
}
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
index 2bbfdcc..e824275 100644
--- a/libs/input/Android.mk
+++ b/libs/input/Android.mk
@@ -28,7 +28,8 @@
libgui \
libui \
libinput \
- libinputflinger
+ libinputflinger \
+ libnativewindow
LOCAL_C_INCLUDES := \
frameworks/native/services
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index fc31f32..8f341a8 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,5 +113,6 @@
// for reporting callback completion
void locationCallbackFinished(ILocationListener listener);
-
+ // used by gts tests to verify throttling whitelist
+ String[] getBackgroundThrottlingWhitelist();
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index ddd8a65..7f5d3a0 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -79,6 +79,7 @@
*/
public class AudioTrack extends PlayerBase
implements AudioRouting
+ , VolumeAutomation
{
//---------------------------------------------------------
// Constants
@@ -1753,6 +1754,12 @@
return native_getVolumeShaperState(id);
}
+ @Override
+ public @NonNull VolumeShaper createVolumeShaper(
+ @NonNull VolumeShaper.Configuration configuration) {
+ return new VolumeShaper(configuration, this);
+ }
+
/**
* Sets the playback sample rate for this track. This sets the sampling rate at which
* the audio data will be consumed and played back
diff --git a/media/java/android/media/ICas.aidl b/media/java/android/media/ICas.aidl
new file mode 100644
index 0000000..6b2ce4a
--- /dev/null
+++ b/media/java/android/media/ICas.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 android.media.MediaCas;
+
+/** @hide */
+interface ICas {
+ void setPrivateData(in byte[] pvtData);
+ byte[] openSession(int program_number);
+ byte[] openSessionForStream(int program_number, int elementary_PID);
+ void closeSession(in byte[] sessionId);
+ void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData);
+ void processEcm(in byte[] sessionId, in MediaCas.ParcelableCasData ecm);
+ void processEmm(in MediaCas.ParcelableCasData emm);
+ void sendEvent(int event, int arg, in @nullable byte[] eventData);
+ void provision(String provisionString);
+ void refreshEntitlements(int refreshType, in @nullable byte[] refreshData);
+ void release();
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ManifestDigest.aidl b/media/java/android/media/ICasListener.aidl
similarity index 68%
copy from core/java/android/content/pm/ManifestDigest.aidl
copy to media/java/android/media/ICasListener.aidl
index ebabab0..01a5abc 100644
--- a/core/java/android/content/pm/ManifestDigest.aidl
+++ b/media/java/android/media/ICasListener.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2011, The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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,
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.content.pm;
+package android.media;
-parcelable ManifestDigest;
+/** @hide */
+interface ICasListener {
+ void onEvent(int event, int arg, in @nullable byte[] data);
+}
\ No newline at end of file
diff --git a/media/java/android/media/IDescrambler.aidl b/media/java/android/media/IDescrambler.aidl
new file mode 100644
index 0000000..fdf99eb
--- /dev/null
+++ b/media/java/android/media/IDescrambler.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 android.media.MediaDescrambler;
+
+/** @hide */
+interface IDescrambler {
+ void setMediaCasSession(in byte[] sessionId);
+ boolean requiresSecureDecoderComponent(String mime);
+ int descramble(in MediaDescrambler.DescrambleInfo descrambleInfo);
+ void release();
+}
\ No newline at end of file
diff --git a/media/java/android/media/IMediaCasService.aidl b/media/java/android/media/IMediaCasService.aidl
new file mode 100644
index 0000000..44f6825
--- /dev/null
+++ b/media/java/android/media/IMediaCasService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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 android.media.IDescrambler;
+import android.media.ICas;
+import android.media.ICasListener;
+import android.media.MediaCas;
+
+/** @hide */
+interface IMediaCasService {
+ MediaCas.ParcelableCasPluginDescriptor[] enumeratePlugins();
+ boolean isSystemIdSupported(int CA_system_id);
+ ICas createPlugin(int CA_system_id, ICasListener listener);
+ boolean isDescramblerSupported(int CA_system_id);
+ IDescrambler createDescrambler(int CA_system_id);
+}
+
diff --git a/core/java/android/content/pm/ManifestDigest.aidl b/media/java/android/media/MediaCas.aidl
similarity index 61%
copy from core/java/android/content/pm/ManifestDigest.aidl
copy to media/java/android/media/MediaCas.aidl
index ebabab0..cb8d0c6 100644
--- a/core/java/android/content/pm/ManifestDigest.aidl
+++ b/media/java/android/media/MediaCas.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2011, The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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,
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.content.pm;
+package android.media;
-parcelable ManifestDigest;
+/** @hide */
+parcelable MediaCas.ParcelableCasPluginDescriptor cpp_header "media/MediaCasDefs.h";
+
+/** @hide */
+parcelable MediaCas.ParcelableCasData cpp_header "media/MediaCasDefs.h";
\ No newline at end of file
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
new file mode 100644
index 0000000..2e22132
--- /dev/null
+++ b/media/java/android/media/MediaCas.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Singleton;
+
+/**
+ * MediaCas can be used to obtain keys for descrambling protected media streams, in
+ * conjunction with {@link android.media.MediaDescrambler}. The MediaCas APIs are
+ * designed to support conditional access such as those in the ISO/IEC13818-1.
+ * The CA system is identified by a 16-bit integer CA_system_id. The scrambling
+ * algorithms are usually proprietary and implemented by vendor-specific CA plugins
+ * installed on the device.
+ * <p>
+ * The app is responsible for constructing a MediaCas object for the CA system it
+ * intends to use. The app can query if a certain CA system is supported using static
+ * method {@link #isSystemIdSupported}. It can also obtain the entire list of supported
+ * CA systems using static method {@link #enumeratePlugins}.
+ * <p>
+ * Once the MediaCas object is constructed, the app should properly provision it by
+ * using method {@link #provision} and/or {@link #processEmm}. The EMMs (Entitlement
+ * management messages) can be distributed out-of-band, or in-band with the stream.
+ * <p>
+ * To descramble elementary streams, the app first calls {@link #openSession} to
+ * generate a sessionId that will uniquely identify a session. A session provides
+ * a context for subsequent key updates and descrambling activities. The ECMs
+ * (Entitlement control messages) are sent to the session via method {@link #processEcm}.
+ * <p>
+ * The app next constructs a MediaDescrambler object, and initializes it with the
+ * sessionId using {@link MediaDescrambler#setMediaCasSession}. This ties the
+ * descrambler to the session, and the descrambler can then be used to descramble
+ * content secured with the session's key, either during extraction, or during decoding
+ * with {@link android.media.MediaCodec}.
+ * <p>
+ * If the app handles sample extraction using its own extractor, it can use
+ * MediaDescrambler to descramble samples into clear buffers (if the session's license
+ * doesn't require secure decoders), or descramble a small amount of data to retrieve
+ * information necessary for the downstream pipeline to process the sample (if the
+ * session's license requires secure decoders).
+ * <p>
+ * If the session requires a secure decoder, a MediaDescrambler needs to be provided to
+ * MediaCodec to descramble samples queued by {@link MediaCodec#queueSecureInputBuffer}
+ * into protected buffers. The app should use {@link MediaCodec#configure(MediaFormat,
+ * android.view.Surface, int, MediaDescrambler)} instead of the normal {@link
+ * MediaCodec#configure(MediaFormat, android.view.Surface, MediaCrypto, int)} method
+ * to configure MediaCodec.
+ * <p>
+ * <h3>Using Android's MediaExtractor</h3>
+ * <p>
+ * If the app uses {@link MediaExtractor}, it can delegate the CAS session
+ * management to MediaExtractor by calling {@link MediaExtractor#setMediaCas}.
+ * MediaExtractor will take over and call {@link #openSession}, {@link #processEmm}
+ * and/or {@link #processEcm}, etc.. if necessary.
+ * <p>
+ * When using {@link MediaExtractor}, the app would still need a MediaDescrambler
+ * to use with {@link MediaCodec} if the licensing requires a secure decoder. The
+ * sessionId of the descrambler can be retrieved by {@link MediaExtractor#getDrmInitData}
+ * and used to initialize a MediaDescrambler object for MediaCodec.
+ * <p>
+ * TODO: determine exception handling schemes.
+ * <p>
+ * <h3>Listeners</h3>
+ * <p>The app may register a listener to receive events from the CA system using
+ * method {@link #setEventListener}. The exact format of the event is scheme-specific
+ * and is not specified by this API.
+ */
+public final class MediaCas {
+ private static final String TAG = "MediaCas";
+ private final ParcelableCasData mCasData = new ParcelableCasData();
+ private ICas mICas;
+ private EventListener mListener;
+ private HandlerThread mHandlerThread;
+ private EventHandler mEventHandler;
+
+ private static final Singleton<IMediaCasService> gDefault =
+ new Singleton<IMediaCasService>() {
+ @Override
+ protected IMediaCasService create() {
+ return IMediaCasService.Stub.asInterface(
+ ServiceManager.getService("media.cas"));
+ }
+ };
+
+ static IMediaCasService getService() {
+ return gDefault.get();
+ }
+
+ private void validateInternalStates() {
+ if (mICas == null) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private void cleanupAndRethrowIllegalState() {
+ mICas = null;
+ throw new IllegalStateException();
+ }
+
+ private class EventHandler extends Handler
+ {
+ private static final int MSG_CAS_EVENT = 0;
+
+ public EventHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_CAS_EVENT) {
+ mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2, (byte[]) msg.obj);
+ }
+ }
+ }
+
+ private final ICasListener.Stub mBinder = new ICasListener.Stub() {
+ @Override
+ public void onEvent(int event, int arg, @Nullable byte[] data)
+ throws RemoteException {
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ EventHandler.MSG_CAS_EVENT, event, arg, data));
+ }
+ };
+
+ /**
+ * Class for parceling byte array data over ICas binder.
+ */
+ static class ParcelableCasData implements Parcelable {
+ private byte[] mData;
+ private int mOffset;
+ private int mLength;
+
+ ParcelableCasData() {
+ mData = null;
+ mOffset = mLength = 0;
+ }
+
+ private ParcelableCasData(Parcel in) {
+ this();
+ }
+
+ void set(@NonNull byte[] data, int offset, int length) {
+ mData = data;
+ mOffset = offset;
+ mLength = length;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mData, mOffset, mLength);
+ }
+
+ public static final Parcelable.Creator<ParcelableCasData> CREATOR
+ = new Parcelable.Creator<ParcelableCasData>() {
+ public ParcelableCasData createFromParcel(Parcel in) {
+ return new ParcelableCasData(in);
+ }
+
+ public ParcelableCasData[] newArray(int size) {
+ return new ParcelableCasData[size];
+ }
+ };
+ }
+
+ /**
+ * Describe a CAS plugin with its CA_system_ID and string name.
+ *
+ * Returned as results of {@link #enumeratePlugins}.
+ *
+ */
+ public static class PluginDescriptor {
+ private final int mCASystemId;
+ private final String mName;
+
+ private PluginDescriptor() {
+ mCASystemId = 0xffff;
+ mName = null;
+ }
+
+ PluginDescriptor(int CA_system_id, String name) {
+ mCASystemId = CA_system_id;
+ mName = name;
+ }
+
+ public int getSystemId() {
+ return mCASystemId;
+ }
+
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public String toString() {
+ return "PluginDescriptor {" + mCASystemId + ", " + mName + "}";
+ }
+ }
+
+ /**
+ * Class for parceling CAS plugin descriptors over IMediaCasService binder.
+ */
+ static class ParcelableCasPluginDescriptor
+ extends PluginDescriptor implements Parcelable {
+
+ private ParcelableCasPluginDescriptor(int CA_system_id, String name) {
+ super(CA_system_id, name);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ Log.w(TAG, "ParcelableCasPluginDescriptor.writeToParcel shouldn't be called!");
+ }
+
+ public static final Parcelable.Creator<ParcelableCasPluginDescriptor> CREATOR
+ = new Parcelable.Creator<ParcelableCasPluginDescriptor>() {
+ public ParcelableCasPluginDescriptor createFromParcel(Parcel in) {
+ int CA_system_id = in.readInt();
+ String name = in.readString();
+ return new ParcelableCasPluginDescriptor(CA_system_id, name);
+ }
+
+ public ParcelableCasPluginDescriptor[] newArray(int size) {
+ return new ParcelableCasPluginDescriptor[size];
+ }
+ };
+ }
+
+ /**
+ * Query if a certain CA system is supported on this device.
+ *
+ * @param CA_system_id the id of the CA system.
+ *
+ * @return Whether the specified CA system is supported on this device.
+ */
+ public static boolean isSystemIdSupported(int CA_system_id) {
+ IMediaCasService service = getService();
+
+ if (service != null) {
+ try {
+ return service.isSystemIdSupported(CA_system_id);
+ } catch (RemoteException e) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * List all available CA plugins on the device.
+ *
+ * @return an array of descriptors for the available CA plugins.
+ */
+ public static PluginDescriptor[] enumeratePlugins() {
+ IMediaCasService service = getService();
+
+ if (service != null) {
+ try {
+ ParcelableCasPluginDescriptor[] descriptors = service.enumeratePlugins();
+ if (descriptors.length == 0) {
+ return null;
+ }
+ PluginDescriptor[] results = new PluginDescriptor[descriptors.length];
+ for (int i = 0; i < results.length; i++) {
+ results[i] = descriptors[i];
+ }
+ return results;
+ } catch (RemoteException e) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Instantiate a CA system of the specified system id.
+ *
+ * @param CA_system_id The system id of the CA system.
+ *
+ * @throws UnsupportedCasException if the device does not support the
+ * specified CA system.
+ */
+ public MediaCas(int CA_system_id) throws UnsupportedCasException {
+ try {
+ mICas = getService().createPlugin(CA_system_id, mBinder);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to create plugin: " + e);
+ mICas = null;
+ } finally {
+ if (mICas == null) {
+ throw new UnsupportedCasException(
+ "Unsupported CA_system_id " + CA_system_id);
+ }
+ }
+ }
+
+ IBinder getBinder() {
+ validateInternalStates();
+
+ return mICas.asBinder();
+ }
+
+ /**
+ * An interface registered by the caller to {@link #setEventListener}
+ * to receives scheme-specific notifications from a MediaCas instance.
+ */
+ public interface EventListener {
+ /**
+ * Notify the listener of a scheme-specific event from the CA system.
+ *
+ * @param MediaCas the MediaCas object to receive this event.
+ * @param event an integer whose meaning is scheme-specific.
+ * @param arg an integer whose meaning is scheme-specific.
+ * @param data a byte array of data whose format and meaning are
+ * scheme-specific.
+ */
+ void onEvent(MediaCas MediaCas, int event, int arg, @Nullable byte[] data);
+ }
+
+ /**
+ * Set an event listener to receive notifications from the MediaCas instance.
+ *
+ * @param listener the event listener to be set.
+ * @param handler the handler whose looper the event listener will be called on.
+ * If handler is null, we'll try to use current thread's looper, or the main
+ * looper. If neither are available, an internal thread will be created instead.
+ */
+ public void setEventListener(
+ @Nullable EventListener listener, @Nullable Handler handler) {
+ mListener = listener;
+
+ if (mListener == null) {
+ mEventHandler = null;
+ return;
+ }
+
+ Looper looper = (handler != null) ? handler.getLooper() : null;
+ if (looper == null
+ && (looper = Looper.myLooper()) == null
+ && (looper = Looper.getMainLooper()) == null) {
+ if (mHandlerThread == null || !mHandlerThread.isAlive()) {
+ mHandlerThread = new HandlerThread("MediaCasEventThread",
+ Process.THREAD_PRIORITY_FOREGROUND);
+ mHandlerThread.start();
+ }
+ looper = mHandlerThread.getLooper();
+ }
+ mEventHandler = new EventHandler(looper);
+ }
+
+ /*
+ * TODO: handle ServiceSpecificException from the IMediaCas
+ * All Drm-specific failures will be thrown by mICas as
+ * ServiceSpecificException exception with Drm error code.
+ * These need to be re-thrown as crypto exceptions.
+ */
+
+ /**
+ * Send the private data for the CA system.
+ *
+ * @param data byte array of the private data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void setPrivateData(@NonNull byte[] data) {
+ validateInternalStates();
+
+ try {
+ mICas.setPrivateData(data);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Open a session for the specified program.
+ *
+ * @param programNumber program_number of the program (as in ISO/IEC13818-1).
+ *
+ * @return session id of the newly opened session.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if a session for the program already exists.
+ */
+ public byte[] openSession(int programNumber) {
+ validateInternalStates();
+
+ try {
+ return mICas.openSession(programNumber);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ return null;
+ }
+
+ /**
+ * Open a session for the specified stream.
+ *
+ * @param programNumber program_number of the stream (as in ISO/IEC13818-1).
+ * @param elementaryPID elementary_PID of the stream (as in ISO/IEC13818-1).
+ *
+ * @return session id of the newly opened session.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if a session for the stream already exists.
+ */
+ public byte[] openSession(int programNumber, int elementaryPID) {
+ validateInternalStates();
+
+ try {
+ return mICas.openSessionForStream(programNumber, elementaryPID);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ return null;
+ }
+
+ /**
+ * Close the specified session.
+ *
+ * @param sessionId the session to be closed.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if the session is not valid.
+ */
+ public void closeSession(@NonNull byte[] sessionId) {
+ validateInternalStates();
+
+ try {
+ mICas.closeSession(sessionId);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Set the private data for a session.
+ *
+ * @param sessionId the session for which the private data is intended.
+ * @param data byte array of the private data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if the session is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void setSessionPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data) {
+ validateInternalStates();
+
+ try {
+ mICas.setSessionPrivateData(sessionId, data);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Send a received ECM packet to the specified session of the CA system.
+ *
+ * @param sessionId the session for which the ECM is intended.
+ * @param data byte array of the ECM data.
+ * @param offset position within data where the ECM data begins.
+ * @param length length of the data (starting from offset).
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if the session is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void processEcm(
+ @NonNull byte[] sessionId, @NonNull byte[] data, int offset, int length) {
+ validateInternalStates();
+
+ try {
+ mCasData.set(data, offset, length);
+ mICas.processEcm(sessionId, mCasData);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Send a received ECM packet to the specified session of the CA system.
+ * This is similar to {@link #processEcm(byte[], byte[], int, int)}
+ * except that the entire byte array is sent.
+ *
+ * @param sessionId the session for which the ECM is intended.
+ * @param data byte array of the ECM data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid,
+ * or IllegalArgumentException if the session is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data) {
+ processEcm(sessionId, data, 0, data.length);
+ }
+
+ /**
+ * Send a received EMM packet to the CA system.
+ *
+ * @param data byte array of the EMM data.
+ * @param offset position within data where the EMM data begins.
+ * @param length length of the data (starting from offset).
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void processEmm(@NonNull byte[] data, int offset, int length) {
+ validateInternalStates();
+
+ try {
+ mCasData.set(data, offset, length);
+ mICas.processEmm(mCasData);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Send a received EMM packet to the CA system. This is similar to
+ * {@link #processEmm(byte[], int, int)} except that the entire byte
+ * array is sent.
+ *
+ * @param data byte array of the EMM data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ /*
+ * TODO: need to re-throw DRM-specific exceptions
+ */
+ public void processEmm(@NonNull byte[] data) {
+ processEmm(data, 0, data.length);
+ }
+
+ /**
+ * Send an event to a CA system. The format of the event is scheme-specific
+ * and is opaque to the framework.
+ *
+ * @param event an integer denoting a scheme-specific event to be sent.
+ * @param arg a scheme-specific integer argument for the event.
+ * @param data a byte array containing scheme-specific data for the event.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ public void sendEvent(int event, int arg, @Nullable byte[] data) {
+ validateInternalStates();
+
+ try {
+ mICas.sendEvent(event, arg, data);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Initiate a provisioning operation for a CA system.
+ *
+ * @param provisionString string containing information needed for the
+ * provisioning operation, the format of which is scheme and implementation
+ * specific.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ public void provision(@NonNull String provisionString) {
+ validateInternalStates();
+
+ try {
+ mICas.provision(provisionString);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Notify the CA system to refresh entitlement keys.
+ *
+ * @param refreshType the type of the refreshment.
+ * @param refreshData private data associated with the refreshment.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ */
+ /*
+ * TODO: define enums for refreshType
+ */
+ public void refreshEntitlements(int refreshType, @Nullable byte[] refreshData) {
+ validateInternalStates();
+
+ try {
+ mICas.refreshEntitlements(refreshType, refreshData);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Release the MediaCas instance.
+ */
+ public void release() {
+ if (mICas != null) {
+ try {
+ mICas.release();
+ } catch (RemoteException e) {
+ } finally {
+ mICas = null;
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ release();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ManifestDigest.aidl b/media/java/android/media/MediaCasException.java
similarity index 61%
copy from core/java/android/content/pm/ManifestDigest.aidl
copy to media/java/android/media/MediaCasException.java
index ebabab0..1d5d3cd 100644
--- a/core/java/android/content/pm/ManifestDigest.aidl
+++ b/media/java/android/media/MediaCasException.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2011, The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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,
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package android.content.pm;
+package android.media;
-parcelable ManifestDigest;
+/**
+ * Base class for MediaCas exceptions
+ */
+public class MediaCasException extends Exception {
+ public MediaCasException(String detailMessage) {
+ super(detailMessage);
+ }
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 75ccffee..1ca9658 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -25,6 +25,7 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.view.Surface;
@@ -1567,6 +1568,13 @@
*/
public static final int BUFFER_FLAG_END_OF_STREAM = 4;
+ /**
+ * This indicates that the buffer only contains part of a frame,
+ * and the decoder should batch the data until a buffer without
+ * this flag appears before decoding the frame.
+ */
+ public static final int BUFFER_FLAG_PARTIAL_FRAME = 8;
+
/** @hide */
@IntDef(
flag = true,
@@ -1575,6 +1583,7 @@
BUFFER_FLAG_KEY_FRAME,
BUFFER_FLAG_CODEC_CONFIG,
BUFFER_FLAG_END_OF_STREAM,
+ BUFFER_FLAG_PARTIAL_FRAME,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BufferFlag {}
@@ -1851,6 +1860,48 @@
@Nullable MediaFormat format,
@Nullable Surface surface, @Nullable MediaCrypto crypto,
@ConfigureFlag int flags) {
+ configure(format, surface, crypto, null, flags);
+ }
+
+ /**
+ * Configure a component to be used with a descrambler.
+ * @param format The format of the input data (decoder) or the desired
+ * format of the output data (encoder). Passing {@code null}
+ * as {@code format} is equivalent to passing an
+ * {@link MediaFormat#MediaFormat an empty mediaformat}.
+ * @param surface Specify a surface on which to render the output of this
+ * decoder. Pass {@code null} as {@code surface} if the
+ * codec does not generate raw video output (e.g. not a video
+ * decoder) and/or if you want to configure the codec for
+ * {@link ByteBuffer} output.
+ * @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
+ * component as an encoder.
+ * @param descrambler Specify a descrambler object to facilitate secure
+ * descrambling of the media data. descrambler must not be
+ * null if this method is used. For non-secure codecs, use
+ * {@link #configure} and with null crypto parameter.
+ * @throws IllegalArgumentException if the surface has been released (or is invalid),
+ * or the format is unacceptable (e.g. missing a mandatory key),
+ * or the flags are not set properly
+ * (e.g. missing {@link #CONFIGURE_FLAG_ENCODE} for an encoder).
+ * @throws IllegalStateException if not in the Uninitialized state.
+ * @throws CryptoException upon DRM error.
+ * @throws CodecException upon codec error.
+ */
+ public void configure(
+ @Nullable MediaFormat format, @Nullable Surface surface,
+ @ConfigureFlag int flags, @NonNull MediaDescrambler descrambler) {
+ configure(format, surface, null, descrambler.getBinder(), flags);
+ }
+
+ private void configure(
+ @Nullable MediaFormat format, @Nullable Surface surface,
+ @Nullable MediaCrypto crypto, @Nullable IBinder descramblerBinder,
+ @ConfigureFlag int flags) {
+ if (crypto != null && descramblerBinder != null) {
+ throw new IllegalArgumentException("Can't use crypto and descrambler together!");
+ }
+
String[] keys = null;
Object[] values = null;
@@ -1881,7 +1932,7 @@
mHasSurface = surface != null;
- native_configure(keys, values, surface, crypto, flags);
+ native_configure(keys, values, surface, crypto, descramblerBinder, flags);
}
/**
@@ -1959,7 +2010,8 @@
private native final void native_configure(
@Nullable String[] keys, @Nullable Object[] values,
- @Nullable Surface surface, @Nullable MediaCrypto crypto, @ConfigureFlag int flags);
+ @Nullable Surface surface, @Nullable MediaCrypto crypto,
+ @Nullable IBinder descramblerBinder, @ConfigureFlag int flags);
/**
* Requests a Surface to use as the input to an encoder, in place of input buffers. This
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 8ada295f..3ac4c34 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -460,6 +460,11 @@
public static final String FEATURE_TunneledPlayback = "tunneled-playback";
/**
+ * <b>video decoder only</b>: codec supports queuing partial frames.
+ */
+ public static final String FEATURE_PartialFrame = "partial-frame";
+
+ /**
* <b>video encoder only</b>: codec supports intra refresh.
*/
public static final String FEATURE_IntraRefresh = "intra-refresh";
@@ -489,6 +494,7 @@
new Feature(FEATURE_AdaptivePlayback, (1 << 0), true),
new Feature(FEATURE_SecurePlayback, (1 << 1), false),
new Feature(FEATURE_TunneledPlayback, (1 << 2), false),
+ new Feature(FEATURE_PartialFrame, (1 << 3), false),
};
private static final Feature[] encoderFeatures = {
diff --git a/core/java/android/content/pm/ManifestDigest.aidl b/media/java/android/media/MediaDescrambler.aidl
similarity index 69%
rename from core/java/android/content/pm/ManifestDigest.aidl
rename to media/java/android/media/MediaDescrambler.aidl
index ebabab0..e789244 100644
--- a/core/java/android/content/pm/ManifestDigest.aidl
+++ b/media/java/android/media/MediaDescrambler.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2011, The Android Open Source Project
+ * Copyright (C) 2017 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
+ * 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,
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.content.pm;
+package android.media;
-parcelable ManifestDigest;
+/** @hide */
+parcelable MediaDescrambler.DescrambleInfo cpp_header "media/MediaCasDefs.h";
\ No newline at end of file
diff --git a/media/java/android/media/MediaDescrambler.java b/media/java/android/media/MediaDescrambler.java
new file mode 100644
index 0000000..f5eede8
--- /dev/null
+++ b/media/java/android/media/MediaDescrambler.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/**
+ * MediaDescrambler class can be used in conjunction with {@link android.media.MediaCodec}
+ * and {@link android.media.MediaExtractor} to decode media data scrambled by conditional
+ * access (CA) systems such as those in the ISO/IEC13818-1.
+ *
+ * A MediaDescrambler object is initialized from a session opened by a MediaCas object,
+ * and can be used to descramble media streams scrambled with that session's keys.
+ *
+ * Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id.
+ *
+ */
+public final class MediaDescrambler {
+ private static final String TAG = "MediaDescrambler";
+ private IDescrambler mIDescrambler;
+
+ private final void validateInternalStates() {
+ if (mIDescrambler == null) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private final void cleanupAndRethrowIllegalState() {
+ mIDescrambler = null;
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Class for parceling descrambling parameters over IDescrambler binder.
+ */
+ static class DescrambleInfo implements Parcelable {
+ private DescrambleInfo() {
+ // TODO: implement
+ }
+
+ private DescrambleInfo(Parcel in) {
+ // TODO: disable
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ // TODO: implement
+ }
+
+ public static final Parcelable.Creator<DescrambleInfo> CREATOR
+ = new Parcelable.Creator<DescrambleInfo>() {
+ public DescrambleInfo createFromParcel(Parcel in) {
+ return new DescrambleInfo(in);
+ }
+
+ public DescrambleInfo[] newArray(int size) {
+ return new DescrambleInfo[size];
+ }
+ };
+ }
+
+ /**
+ * Instantiate a MediaDescrambler.
+ *
+ * @param CA_system_id The system id of the scrambling scheme.
+ *
+ * @throws UnsupportedCasException if the scrambling scheme is not supported.
+ */
+ public MediaDescrambler(int CA_system_id) throws UnsupportedCasException {
+ try {
+ mIDescrambler = MediaCas.getService().createDescrambler(CA_system_id);
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to create descrambler: " + e);
+ mIDescrambler = null;
+ } finally {
+ if (mIDescrambler == null) {
+ throw new UnsupportedCasException("Unsupported CA_system_id " + CA_system_id);
+ }
+ }
+ native_setup(mIDescrambler.asBinder());
+ }
+
+ IBinder getBinder() {
+ validateInternalStates();
+
+ return mIDescrambler.asBinder();
+ }
+
+ /*
+ * TODO: handle ServiceSpecificException from the mIDescrambler
+ * All Drm-specific failures will be thrown by mIDescrambler as
+ * ServiceSpecificException exception with Drm error code.
+ * These need to be re-thrown as crypto exceptions.
+ */
+
+ /**
+ * Query if the scrambling scheme requires the use of a secure decoder
+ * to decode data of the given mime type.
+ *
+ * @param mime The mime type of the media data
+ *
+ * @throws IllegalStateException if the descrambler instance is not valid.
+ */
+ public final boolean requiresSecureDecoderComponent(@NonNull String mime) {
+ validateInternalStates();
+
+ try {
+ return mIDescrambler.requiresSecureDecoderComponent(mime);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ return true;
+ }
+
+ /**
+ * Associate a MediaCas session with this MediaDescrambler instance.
+ * The MediaCas session is used to securely load decryption keys for
+ * the descrambler. The crypto keys loaded through the MediaCas session
+ * may be selected for use during the descrambling operation performed
+ * by {@link android.media.MediaExtractor or @link
+ * android.media.MediaCodec#queueSecureInputBuffer} by specifying even
+ * or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field.
+ *
+ * @param sessionId the MediaCas sessionId to associate with this
+ * MediaDescrambler instance.
+ *
+ * @throws IllegalStateException if the descrambler instance is not valid,
+ * or IllegalArgumentException if the sessionId is not valid.
+ */
+ public final void setMediaCasSession(@NonNull byte[] sessionId) {
+ validateInternalStates();
+
+ try {
+ mIDescrambler.setMediaCasSession(sessionId);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Descramble a ByteBuffer of data described by a
+ * {@link android.media.MediaCodec.CryptoInfo} structure.
+ *
+ * @param srcBuf ByteBuffer containing the scrambled data.
+ * @param srcPos position within src where the scrambled data starts.
+ * @param dstBuf ByteBuffer to descramble into. If null, descrambling will happen
+ * in-place and src will be used as dst.
+ * @param dstPos position within dst to put the descrambled data.
+ * @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
+ * describing the subsamples contained in src.
+ *
+ * @return number of bytes that have been successfully descrambled, with negative
+ * values indicating errors.
+ *
+ * @throws IllegalStateException if the descrambler instance is not valid.
+ */
+ /*
+ * TODO: throw DRM-specific exception if decrambling is failing.
+ */
+ public final int descramble(
+ @NonNull ByteBuffer srcBuf, int srcPos, ByteBuffer dstBuf, int dstPos,
+ @NonNull MediaCodec.CryptoInfo cryptoInfo) {
+ validateInternalStates();
+
+ if (cryptoInfo.numSubSamples <= 0) {
+ throw new IllegalArgumentException(
+ "Invalid CryptoInfo: invalid numSubSamples=" + cryptoInfo.numSubSamples);
+ } else if (cryptoInfo.numBytesOfClearData == null
+ && cryptoInfo.numBytesOfEncryptedData == null) {
+ throw new IllegalArgumentException(
+ "Invalid CryptoInfo: clearData and encryptedData size arrays are both null!");
+ } else if (cryptoInfo.numBytesOfClearData != null
+ && cryptoInfo.numBytesOfClearData.length < cryptoInfo.numSubSamples) {
+ throw new IllegalArgumentException(
+ "Invalid CryptoInfo: numBytesOfClearData is too small!");
+ } else if (cryptoInfo.numBytesOfEncryptedData != null
+ && cryptoInfo.numBytesOfEncryptedData.length < cryptoInfo.numSubSamples) {
+ throw new IllegalArgumentException(
+ "Invalid CryptoInfo: numBytesOfEncryptedData is too small!");
+ } else if (cryptoInfo.key == null || cryptoInfo.key.length != 16) {
+ throw new IllegalArgumentException(
+ "Invalid CryptoInfo: key array is invalid!");
+ }
+
+ return native_descramble(
+ cryptoInfo.key[0],
+ cryptoInfo.numSubSamples,
+ cryptoInfo.numBytesOfClearData,
+ cryptoInfo.numBytesOfEncryptedData,
+ srcBuf, srcPos, dstBuf, dstPos);
+ }
+
+ public final void release() {
+ if (mIDescrambler != null) {
+ try {
+ mIDescrambler.release();
+ } catch (RemoteException e) {
+ } finally {
+ mIDescrambler = null;
+ }
+ }
+ native_release();
+ }
+
+ @Override
+ protected void finalize() {
+ release();
+ }
+
+ private static native final void native_init();
+ private native final void native_setup(@NonNull IBinder decramblerBinder);
+ private native final void native_release();
+ private native final int native_descramble(
+ byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
+ @NonNull ByteBuffer srcBuf, int srcOffset, ByteBuffer dstBuf, int dstOffset);
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private long mNativeContext;
+}
\ No newline at end of file
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 2ca36ea..01ae36f 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -247,6 +247,22 @@
public native final void setDataSource(
@NonNull FileDescriptor fd, long offset, long length) throws IOException;
+ /**
+ * Sets the MediaCas instance to use. This should be called after a
+ * successful setDataSource() if at least one track reports mime type
+ * of {@link android.media.MediaFormat#MIMETYPE_AUDIO_SCRAMBLED}
+ * or {@link android.media.MediaFormat#MIMETYPE_VIDEO_SCRAMBLED}.
+ * Stream parsing will not proceed until a valid MediaCas object
+ * is provided.
+ *
+ * @param mediaCas the MediaCas object to use.
+ */
+ public final void setMediaCas(@NonNull MediaCas mediaCas) {
+ nativeSetMediaCas(mediaCas.getBinder());
+ }
+
+ private native final void nativeSetMediaCas(@NonNull IBinder casBinder);
+
@Override
protected void finalize() {
native_finalize();
@@ -290,6 +306,31 @@
return initDataMap.get(schemeUuid);
}
};
+ } else if (formatMap.containsKey("mime")
+ && "video/mp2ts".equals(formatMap.get("mime"))) {
+ final Map<UUID, DrmInitData.SchemeInitData> initDataMap =
+ new HashMap<UUID, DrmInitData.SchemeInitData>();
+
+ int numTracks = getTrackCount();
+ for (int i = 0; i < numTracks; ++i) {
+ Map<String, Object> trackFormatMap = getTrackFormatNative(i);
+ if (!trackFormatMap.containsKey("cas")) {
+ continue;
+ }
+ ByteBuffer buf = (ByteBuffer) trackFormatMap.get("cas");
+ buf.rewind();
+ final byte[] data = new byte[buf.remaining()];
+ buf.get(data);
+ initDataMap.put(new UUID(0, i), new DrmInitData.SchemeInitData("cas", data));
+ }
+ if (initDataMap.isEmpty()) {
+ return null;
+ }
+ return new DrmInitData() {
+ public SchemeInitData get(UUID schemeUuid) {
+ return initDataMap.get(schemeUuid);
+ }
+ };
} else {
int numTracks = getTrackCount();
for (int i = 0; i < numTracks; ++i) {
@@ -307,8 +348,8 @@
}
};
}
+ return null;
}
- return null;
}
/**
@@ -557,12 +598,22 @@
*/
public static final int SAMPLE_FLAG_ENCRYPTED = 2;
+ /**
+ * This indicates that the buffer only contains part of a frame,
+ * and the decoder should batch the data until a buffer without
+ * this flag appears before decoding the frame.
+ *
+ * @see MediaCodec#BUFFER_FLAG_PARTIAL_FRAME
+ */
+ public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4;
+
/** @hide */
@IntDef(
flag = true,
value = {
SAMPLE_FLAG_SYNC,
SAMPLE_FLAG_ENCRYPTED,
+ SAMPLE_FLAG_PARTIAL_FRAME,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SampleFlag {}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index d74aa81..4791cf0 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -105,6 +105,8 @@
public static final String MIMETYPE_VIDEO_H263 = "video/3gpp";
public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
public static final String MIMETYPE_VIDEO_RAW = "video/raw";
+ public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
+ public static final String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
@@ -120,7 +122,7 @@
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
- public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
+ public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
/**
* MIME type for WebVTT subtitle data.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 5008a5f..1ebbe85 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -579,6 +579,7 @@
*/
public class MediaPlayer extends PlayerBase
implements SubtitleController.Listener
+ , VolumeAutomation
{
/**
Constant to retrieve only the new metadata since the last
@@ -1373,6 +1374,12 @@
return native_getVolumeShaperState(id);
}
+ @Override
+ public @NonNull VolumeShaper createVolumeShaper(
+ @NonNull VolumeShaper.Configuration configuration) {
+ return new VolumeShaper(configuration, this);
+ }
+
private native int native_applyVolumeShaper(
@NonNull VolumeShaper.Configuration configuration,
@NonNull VolumeShaper.Operation operation);
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index b397b45..1f5986f 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -58,7 +58,7 @@
protected float mAuxEffectSendLevel = 0.0f;
// for AppOps
- private IAppOpsService mAppOps;
+ private IAppOpsService mAppOps; // may be null
private IAppOpsCallback mAppOpsCallback;
private boolean mHasAppOpsPlayAudio = true; // sync'd on mLock
private final Object mLock = new Object();
@@ -251,7 +251,9 @@
Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
}
try {
- mAppOps.stopWatchingMode(mAppOpsCallback);
+ if (mAppOps != null) {
+ mAppOps.stopWatchingMode(mAppOpsCallback);
+ }
} catch (RemoteException e) {
// nothing to do here, the object is supposed to be released anyway
}
@@ -264,9 +266,12 @@
void updateAppOpsPlayAudio_sync() {
boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
try {
- final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ int mode = AppOpsManager.MODE_IGNORED;
+ if (mAppOps != null) {
+ mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
mAttributes.getUsage(),
Process.myUid(), ActivityThread.currentPackageName());
+ }
mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
} catch (RemoteException e) {
mHasAppOpsPlayAudio = false;
diff --git a/media/java/android/media/UnsupportedCasException.java b/media/java/android/media/UnsupportedCasException.java
new file mode 100644
index 0000000..3167637
--- /dev/null
+++ b/media/java/android/media/UnsupportedCasException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * Exception thrown when an attempt is made to construct a MediaCas object
+ * using a CA_system_id that is not supported by the device
+ */
+public final class UnsupportedCasException extends MediaCasException {
+ public UnsupportedCasException(String detailMessage) {
+ super(detailMessage);
+ }
+}
diff --git a/media/java/android/media/VolumeAutomation.java b/media/java/android/media/VolumeAutomation.java
new file mode 100644
index 0000000..dff8801
--- /dev/null
+++ b/media/java/android/media/VolumeAutomation.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 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 android.annotation.NonNull;
+import android.media.VolumeShaper.Configuration;
+
+/**
+ * {@code VolumeAutomation} defines an interface for automatic volume control
+ * of {@link AudioTrack} and {@link MediaPlayer} objects.
+ */
+public interface VolumeAutomation {
+ /**
+ * Returns a {@link VolumeShaper} object that can be used modify the volume envelope
+ * of the player or track.
+ *
+ * @param configuration the {@link VolumeShaper.Configuration configuration}
+ * that specifies the curve and duration to use.
+ * @return a {@code VolumeShaper} object
+ * @throws IllegalArgumentException if the configuration is not allowed by the player.
+ * @throws IllegalStateException if too many VolumeShapers are requested or the state of
+ * the player does not permit its creation (e.g. player is released).
+ */
+ public @NonNull VolumeShaper createVolumeShaper(
+ @NonNull VolumeShaper.Configuration configuration);
+}
diff --git a/media/java/android/media/VolumeShaper.java b/media/java/android/media/VolumeShaper.java
index 4a2c4d8..cb27d10 100644
--- a/media/java/android/media/VolumeShaper.java
+++ b/media/java/android/media/VolumeShaper.java
@@ -23,78 +23,25 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.AutoCloseable;
import java.lang.ref.WeakReference;
import java.util.Objects;
/**
- * TODO: remove @hide
* The {@code VolumeShaper} class is used to automatically control audio volume during media
- * playback, allowing for simple implementation of transition effects and ducking.
+ * playback, allowing simple implementation of transition effects and ducking.
*
* The {@link VolumeShaper} appears as an additional scaling on the audio output,
- * and can be used independently of track or stream volume controls.
+ * and adjusts independently of track or stream volume controls.
*/
-public final class VolumeShaper {
+public final class VolumeShaper implements AutoCloseable {
/* member variables */
private int mId;
private final WeakReference<PlayerBase> mWeakPlayerBase;
- private final WeakReference<PlayerProxy> mWeakPlayerProxy;
- private PlayerProxy mPlayerProxy;
-
- /**
- * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and an
- * {@link AudioTrack}.
- *
- * @param configuration
- * @param audioTrack
- */
- public VolumeShaper(@NonNull Configuration configuration, @NonNull AudioTrack audioTrack) {
- this(configuration, (PlayerBase)audioTrack);
- }
-
- /**
- * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
- * {@link MediaPlayer}.
- *
- * @param configuration
- * @param mediaPlayer
- */
- public VolumeShaper(@NonNull Configuration configuration, @NonNull MediaPlayer mediaPlayer) {
- this(configuration, (PlayerBase)mediaPlayer);
- }
/* package */ VolumeShaper(
@NonNull Configuration configuration, @NonNull PlayerBase playerBase) {
mWeakPlayerBase = new WeakReference<PlayerBase>(playerBase);
- mPlayerProxy = null;
- mWeakPlayerProxy = null;
- mId = applyPlayer(configuration, new Operation.Builder().defer().build());
- }
-
- /**
- * @hide
- * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
- * {@code PlayerProxy} object. The PlayerProxy object requires that the configuration
- * be set with a system VolumeShaper id (this is a reserved value).
- *
- * @param configuration
- * @param playerProxy
- */
- public VolumeShaper(
- @NonNull Configuration configuration,
- @NonNull PlayerProxy playerProxy,
- boolean keepReference) {
- if (configuration.getId() < 0) {
- throw new IllegalArgumentException("playerProxy configuration id must be specified");
- }
- if (keepReference) {
- mPlayerProxy = playerProxy;
- mWeakPlayerProxy = null;
- } else {
- mWeakPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
- mPlayerProxy = null;
- }
- mWeakPlayerBase = null;
mId = applyPlayer(configuration, new Operation.Builder().defer().build());
}
@@ -104,7 +51,7 @@
/**
* Applies the {@link VolumeShaper.Operation} to the {@code VolumeShaper}.
- * @param operation
+ * @param operation the {@code operation} to apply.
*/
public void apply(@NonNull Operation operation) {
/* void */ applyPlayer(new VolumeShaper.Configuration(mId), operation);
@@ -112,17 +59,16 @@
/**
* Replaces the current {@code VolumeShaper}
- * configuration with a new configuration.
+ * {@code configuration} with a new {@code configuration}.
*
- * This can be used to dynamically change the {@code VolumeShaper}
- * configuration by joining several
- * {@code VolumeShaper} configurations together.
- * This is useful if the user changes the volume while the
- * {@code VolumeShaper} is in effect.
+ * This allows the user to change the volume shape
+ * while the existing {@code VolumeShaper} is in effect.
*
- * @param configuration
- * @param operation
- * @param join
+ * @param configuration the new {@code configuration} to use.
+ * @param operation the operation to apply to the {@code VolumeShaper}
+ * @param join if true, match the start volume of the
+ * new {@code configuration} to the current volume of the existing
+ * {@code VolumeShaper}, to avoid discontinuity.
*/
public void replace(
@NonNull Configuration configuration, @NonNull Operation operation, boolean join) {
@@ -141,10 +87,11 @@
}
/**
- * Releases the {@code VolumeShaper}. Any volume scale due to the
+ * Releases the {@code VolumeShaper} object; any volume scale due to the
* {@code VolumeShaper} is removed.
*/
- public void release() {
+ @Override
+ public void close() {
try {
/* void */ applyPlayer(
new VolumeShaper.Configuration(mId),
@@ -155,15 +102,11 @@
if (mWeakPlayerBase != null) {
mWeakPlayerBase.clear();
}
- if (mWeakPlayerProxy != null) {
- mWeakPlayerProxy.clear();
- }
- mPlayerProxy = null;
}
@Override
protected void finalize() {
- release(); // ensure we remove the native volume shaper
+ close(); // ensure we remove the native volume shaper
}
/**
@@ -177,20 +120,7 @@
@NonNull VolumeShaper.Configuration configuration,
@NonNull VolumeShaper.Operation operation) {
final int id;
- if (mPlayerProxy != null || mWeakPlayerProxy != null) {
- // The PlayerProxy accepts only one way transactions so
- // the Configuration must have an id set to one of the system
- // ids (a positive value less than 16).
- PlayerProxy player = mWeakPlayerProxy != null ? mWeakPlayerProxy.get() : mPlayerProxy;
- if (player == null) {
- throw new IllegalStateException("player deallocated");
- }
- id = configuration.getId();
- if (id < 0) {
- throw new IllegalArgumentException("proxy requires configuration with id");
- }
- player.applyVolumeShaper(configuration, operation);
- } else if (mWeakPlayerBase != null) {
+ if (mWeakPlayerBase != null) {
PlayerBase player = mWeakPlayerBase.get();
if (player == null) {
throw new IllegalStateException("player deallocated");
@@ -220,9 +150,7 @@
*/
private @NonNull VolumeShaper.State getStatePlayer(int id) {
final VolumeShaper.State state;
- if (mPlayerProxy != null || mWeakPlayerProxy != null) {
- throw new IllegalStateException("getStatePlayer not permitted through proxy");
- } else if (mWeakPlayerBase != null) {
+ if (mWeakPlayerBase != null) {
PlayerBase player = mWeakPlayerBase.get();
if (player == null) {
throw new IllegalStateException("player deallocated");
@@ -238,11 +166,17 @@
}
/**
- * The {@code VolumeShaper.Configuration} class contains curve shape
- * and parameter information for constructing a {@code VolumeShaper}.
- * This curve shape and parameter information is specified
- * on {@code VolumeShaper} creation
- * and may be replaced through {@link VolumeShaper#replace}.
+ * The {@code VolumeShaper.Configuration} class contains curve
+ * and duration information.
+ * It is constructed by the {@link VolumeShaper.Configuration.Builder}.
+ * <p>
+ * A {@code VolumeShaper.Configuration} is used by
+ * {@link VolumeAutomation#createVolumeShaper(Configuration)
+ * VolumeAutomation#createVolumeShaper(Configuration)} to create
+ * a {@code VolumeShaper} and
+ * by {@link VolumeShaper#replace(Configuration, Operation, boolean)
+ * VolumeShaper#replace(Configuration, Operation, boolean)}
+ * to replace an existing {@code configuration}.
*/
public static final class Configuration implements Parcelable {
private static final int MAXIMUM_CURVE_POINTS = 16;
@@ -310,9 +244,9 @@
/**
* Cubic interpolated volume curve
- * with local monotonicity preservation.
+ * that preserves local monotonicity.
* So long as the control points are locally monotonic,
- * the curve interpolation will also be locally monotonic.
+ * the curve interpolation between those points are monotonic.
* This is useful for cubic spline interpolated
* volume ramps and ducks.
*/
@@ -328,6 +262,7 @@
public @interface OptionFlag {}
/**
+ * @hide
* Use a dB full scale volume range for the volume curve.
*<p>
* The volume scale is typically from 0.f to 1.f on a linear scale;
@@ -337,6 +272,7 @@
public static final int OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0);
/**
+ * @hide
* Use clock time instead of media time.
*<p>
* The default implementation of {@code VolumeShaper} is to apply
@@ -354,7 +290,8 @@
/**
* A one second linear ramp from silence to full volume.
- * Use {@link VolumeShaper.Builder#reflectTimes()} to generate
+ * Use {@link VolumeShaper.Builder#reflectTimes()}
+ * or {@link VolumeShaper.Builder#invertVolumes()} to generate
* the matching linear duck.
*/
public static final Configuration LINEAR_RAMP = new VolumeShaper.Configuration.Builder()
@@ -366,7 +303,8 @@
/**
* A one second cubic ramp from silence to full volume.
- * Use {@link VolumeShaper.Builder#reflectTimes()} to generate
+ * Use {@link VolumeShaper.Builder#reflectTimes()}
+ * or {@link VolumeShaper.Builder#invertVolumes()} to generate
* the matching cubic duck.
*/
public static final Configuration CUBIC_RAMP = new VolumeShaper.Configuration.Builder()
@@ -377,17 +315,19 @@
.build();
/**
- * A one second sine curve for energy preserving cross fades.
+ * A one second sine curve
+ * from silence to full volume for energy preserving cross fades.
* Use {@link VolumeShaper.Builder#reflectTimes()} to generate
* the matching cosine duck.
*/
public static final Configuration SINE_RAMP;
/**
- * A one second sine-squared s-curve ramp.
+ * A one second sine-squared s-curve ramp
+ * from silence to full volume.
* Use {@link VolumeShaper.Builder#reflectTimes()}
* or {@link VolumeShaper.Builder#invertVolumes()} to generate
- * the matching s-curve duck.
+ * the matching sine-squared s-curve duck.
*/
public static final Configuration SCURVE_RAMP;
@@ -510,6 +450,7 @@
};
/**
+ * @hide
* Constructs a volume shaper from an id.
*
* This is an opaque handle for controlling a {@code VolumeShaper} that has
@@ -522,7 +463,7 @@
* @param id
* @throws IllegalArgumentException if id is negative.
*/
- private Configuration(int id) {
+ public Configuration(int id) {
if (id < 0) {
throw new IllegalArgumentException("negative id " + id);
}
@@ -557,6 +498,7 @@
}
/**
+ * @hide
* Returns the {@code VolumeShaper} type.
*/
public @Type int getType() {
@@ -579,6 +521,7 @@
}
/**
+ * @hide
* Returns the option flags
*/
public @OptionFlag int getOptionFlags() {
@@ -590,7 +533,7 @@
}
/**
- * Returns the duration of the effect in milliseconds.
+ * Returns the duration of the volume shape in milliseconds.
*/
public double getDurationMs() {
return mDurationMs;
@@ -712,21 +655,22 @@
private int mType = TYPE_SCALE;
private int mId = -1; // invalid
private int mInterpolatorType = INTERPOLATOR_TYPE_CUBIC;
- private int mOptionFlags = 0;
+ private int mOptionFlags = OPTION_FLAG_CLOCK_TIME;
private double mDurationMs = 1000.;
private float[] mTimes = null;
private float[] mVolumes = null;
/**
- * Constructs a new Builder with the defaults.
+ * Constructs a new {@code Builder} with the defaults.
*/
public Builder() {
}
/**
- * Constructs a new Builder from a given {@code VolumeShaper.Configuration}
+ * Constructs a new {@code Builder} with settings
+ * copied from a given {@code VolumeShaper.Configuration}.
* @param configuration prototypical configuration
- * which will be reused in the new Builder.
+ * which will be reused in the new {@code Builder}.
*/
public Builder(@NonNull Configuration configuration) {
mType = configuration.getType();
@@ -740,8 +684,6 @@
/**
* @hide
- * TODO make SystemApi
- *
* Set the id for system defined shapers.
* @param id
* @return
@@ -757,7 +699,11 @@
* If omitted the interplator type is {@link #INTERPOLATOR_TYPE_CUBIC}.
*
* @param interpolatorType method of interpolation used for the volume curve.
- * @return the same Builder instance.
+ * One of {@link #INTERPOLATOR_TYPE_STEP},
+ * {@link #INTERPOLATOR_TYPE_LINEAR},
+ * {@link #INTERPOLATOR_TYPE_CUBIC},
+ * {@link #INTERPOLATOR_TYPE_CUBIC_MONOTONIC}.
+ * @return the same {@code Builder} instance.
* @throws IllegalArgumentException if {@code interpolatorType} is not valid.
*/
public @NonNull Builder setInterpolatorType(@InterpolatorType int interpolatorType) {
@@ -776,6 +722,7 @@
}
/**
+ * @hide
* Sets the optional flags
*
* If omitted, flags are 0. If {@link #OPTION_FLAG_VOLUME_IN_DBFS} has
@@ -783,7 +730,7 @@
* volume domain has changed.
*
* @param optionFlags new value to replace the old {@code optionFlags}.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
* @throws IllegalArgumentException if flag is not recognized.
*/
public @NonNull Builder setOptionFlags(@OptionFlag int optionFlags) {
@@ -800,8 +747,9 @@
* If omitted, the default duration is 1 second.
*
* @param durationMs
- * @return the same Builder instance.
- * @throws IllegalArgumentException if duration is not positive.
+ * @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if {@code durationMs}
+ * is not strictly positive.
*/
public @NonNull Builder setDurationMs(double durationMs) {
if (durationMs <= 0.) {
@@ -823,19 +771,23 @@
* and no greater than {@link VolumeShaper.Configuration#getMaximumCurvePoints()}.
* <p>
* The volume curve is normalized as follows:
- * (1) time (x) coordinates should be monotonically increasing, from 0.f to 1.f;
- * (2) volume (y) coordinates must be within 0.f to 1.f for linear and be non-positive
- * for log scaling.
+ * time (x) coordinates should be monotonically increasing, from 0.f to 1.f;
+ * volume (y) coordinates must be within 0.f to 1.f.
* <p>
- * The time scale is set by {@link #setDurationMs} in seconds.
+ * The time scale is set by {@link #setDurationMs}.
* <p>
* @param times an array of float values representing
* the time line of the volume curve.
* @param volumes an array of float values representing
* the amplitude of the volume curve.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
* @throws IllegalArgumentException if {@code times} or {@code volumes} is invalid.
*/
+
+ /* Note: volume (y) coordinates must be non-positive for log scaling,
+ * if {@link VolumeShaper.Configuration#OPTION_FLAG_VOLUME_IN_DBFS} is set.
+ */
+
public @NonNull Builder setCurve(@NonNull float[] times, @NonNull float[] volumes) {
String error = checkCurveForErrors(
times, volumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
@@ -852,7 +804,7 @@
* the shaper changes volume from the end
* to the start.
*
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder reflectTimes() {
int i;
@@ -871,7 +823,7 @@
* Inverts the volume curve so that the max volume
* becomes the min volume and vice versa.
*
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder invertVolumes() {
if (mVolumes.length >= 2) {
@@ -899,8 +851,9 @@
* Keeps the start volume the same.
* This works best if the volume curve is monotonic.
*
- * @return the same Builder instance.
- * @throws IllegalArgumentException if volume is not valid.
+ * @param volume the target end volume to use.
+ * @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if {@code volume} is not valid.
*/
public @NonNull Builder scaleToEndVolume(float volume) {
final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
@@ -930,8 +883,9 @@
* Keeps the end volume the same.
* This works best if the volume curve is monotonic.
*
- * @return the same Builder instance.
- * @throws IllegalArgumentException if volume is not valid.
+ * @param volume the target start volume to use.
+ * @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if {@code volume} is not valid.
*/
public @NonNull Builder scaleToStartVolume(float volume) {
final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
@@ -978,6 +932,8 @@
public static final class Operation implements Parcelable {
/**
* Forward playback from current volume time position.
+ * At the end of the {@code VolumeShaper} curve,
+ * the last volume value persists.
*/
public static final Operation PLAY =
new VolumeShaper.Operation.Builder()
@@ -985,6 +941,8 @@
/**
* Reverse playback from current volume time position.
+ * When the position reaches the start of the {@code VolumeShaper} curve,
+ * the first volume value persists.
*/
public static final Operation REVERSE =
new VolumeShaper.Operation.Builder()
@@ -1039,6 +997,13 @@
*/
private static final int FLAG_DEFER = 1 << 3;
+ /**
+ * Use the id specified in the configuration, creating
+ * VolumeShaper as needed; the configuration should be
+ * TYPE_SCALE.
+ */
+ private static final int FLAG_CREATE_IF_NEEDED = 1 << 4;
+
private static final int FLAG_PUBLIC_ALL = FLAG_REVERSE | FLAG_TERMINATE;
private final int mFlags;
@@ -1126,15 +1091,13 @@
}
/**
- * Replaces the previous {@code VolumeShaper}.
+ * Replaces the previous {@code VolumeShaper} specified by id.
* It has no other effect if the {@code VolumeShaper} is
- * already expired. If the replaceId is the same as the id associated with
- * the {@code VolumeShaper} in a {@code setVolumeShaper()} call,
- * an error is returned.
- * @param handle is a previous volumeShaper {@code VolumeShaper}.
- * @param join the start to match the current volume of the previous
- * shaper.
- * @return the same Builder instance.
+ * already expired.
+ * @param id the id of the previous {@code VolumeShaper}.
+ * @param join if true, match the volume of the previous
+ * shaper to the start volume of the new {@code VolumeShaper}.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder replace(int id, boolean join) {
mReplaceId = id;
@@ -1148,7 +1111,7 @@
/**
* Defers all operations.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder defer() {
mFlags |= FLAG_DEFER;
@@ -1158,7 +1121,7 @@
/**
* Terminates the VolumeShaper.
* Do not call directly, use {@link VolumeShaper#release()}.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder terminate() {
mFlags |= FLAG_TERMINATE;
@@ -1167,7 +1130,7 @@
/**
* Reverses direction.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
public @NonNull Builder reverse() {
mFlags ^= FLAG_REVERSE;
@@ -1175,11 +1138,22 @@
}
/**
+ * Use the id specified in the configuration, creating
+ * VolumeShaper as needed; the configuration should be
+ * TYPE_SCALE.
+ * @return the same {@code Builder} instance.
+ */
+ public @NonNull Builder createIfNeeded() {
+ mFlags |= FLAG_CREATE_IF_NEEDED;
+ return this;
+ }
+
+ /**
* Sets the operation flag. Do not call this directly but one of the
* other builder methods.
*
* @param flags new value for {@code flags}, consisting of ORed flags.
- * @return the same Builder instance.
+ * @return the same {@code Builder} instance.
*/
private @NonNull Builder setFlags(@Flag int flags) {
if ((flags & ~FLAG_PUBLIC_ALL) != 0) {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 6d8296a..1b53d72 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -25,6 +25,7 @@
import android.content.ContentUris;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -72,6 +73,98 @@
private static final String PATH_PASSTHROUGH = "passthrough";
/**
+ * The method name to get existing columns in the given table of the specified content provider.
+ *
+ * <p>The method caller must provide the following parameter:
+ * <ul>
+ * <li>{@code arg}: The content URI of the target table as a {@link String}.</li>
+ * </ul>
+
+ * <p>On success, the returned {@link android.os.Bundle} will include existing column names
+ * with the key {@link #EXTRA_EXISTING_COLUMN_NAMES}. Otherwise, the return value will be {@code null}.
+ *
+ * @see ContentResolver#call(Uri, String, String, Bundle)
+ * @see #EXTRA_EXISTING_COLUMN_NAMES
+ * @hide
+ */
+ @SystemApi
+ public static final String METHOD_GET_COLUMNS = "get_columns";
+
+ /**
+ * The method name to add a new column in the given table of the specified content provider.
+ *
+ * <p>The method caller must provide the following parameter:
+ * <ul>
+ * <li>{@code arg}: The content URI of the target table as a {@link String}.</li>
+ * <li>{@code extra}: Name, data type, and default value of the new column in a Bundle:
+ * <ul>
+ * <li>{@link #EXTRA_COLUMN_NAME} the column name as a {@link String}.</li>
+ * <li>{@link #EXTRA_DATA_TYPE} the data type as a {@link String}.</li>
+ * <li>{@link #EXTRA_DEFAULT_VALUE} the default value as a {@link String}.
+ * (optional)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * <p>On success, the returned {@link android.os.Bundle} will include current colum names after
+ * the addition operation with the key {@link #EXTRA_EXISTING_COLUMN_NAMES}. Otherwise, the
+ * return value will be {@code null}.
+ *
+ * @see ContentResolver#call(Uri, String, String, Bundle)
+ * @see #EXTRA_COLUMN_NAME
+ * @see #EXTRA_DATA_TYPE
+ * @see #EXTRA_DEFAULT_VALUE
+ * @see #EXTRA_EXISTING_COLUMN_NAMES
+ * @hide
+ */
+ @SystemApi
+ public static final String METHOD_ADD_COLUMN = "add_column";
+
+ /**
+ * The key for a returned {@link Bundle} value containing existing column names in the given
+ * table as an {@link ArrayList} of {@link String}.
+ *
+ * @see #METHOD_GET_COLUMNS
+ * @see #METHOD_ADD_COLUMN
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_EXISTING_COLUMN_NAMES =
+ "android.media.tv.extra.EXISTING_COLUMN_NAMES";
+
+ /**
+ * The key for a {@link Bundle} parameter containing the new column name to be added in the
+ * given table as a non-empty {@link CharSequence}.
+ *
+ * @see #METHOD_ADD_COLUMN
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_COLUMN_NAME = "android.media.tv.extra.COLUMN_NAME";
+
+ /**
+ * The key for a {@link Bundle} parameter containing the data type of the new column to be added
+ * in the given table as a non-empty {@link CharSequence}, which should be one of the following
+ * values: {@code "TEXT"}, {@code "INTEGER"}, {@code "REAL"}, or {@code "BLOB"}.
+ *
+ * @see #METHOD_ADD_COLUMN
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DATA_TYPE = "android.media.tv.extra.DATA_TYPE";
+
+ /**
+ * The key for a {@link Bundle} parameter containing the default value of the new column to be
+ * added in the given table as a {@link CharSequence}, which represents a valid default value
+ * according to the data type provided with {@link #EXTRA_DATA_TYPE}.
+ *
+ * @see #METHOD_ADD_COLUMN
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEFAULT_VALUE = "android.media.tv.extra.DEFAULT_VALUE";
+
+ /**
* An optional query, update or delete URI parameter that allows the caller to specify TV input
* ID to filter channels.
* @hide
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index f69313c..861ed0a 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -8,6 +8,7 @@
android_media_MediaCodec.cpp \
android_media_MediaCodecList.cpp \
android_media_MediaDataSource.cpp \
+ android_media_MediaDescrambler.cpp \
android_media_MediaDrm.cpp \
android_media_MediaExtractor.cpp \
android_media_MediaHTTPConnection.cpp \
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 6f9883c..293e5dd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -25,9 +25,12 @@
#include "android_media_Utils.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
+#include "android_util_Binder.h"
#include "jni.h"
#include "JNIHelp.h"
+#include <android/media/IDescrambler.h>
+
#include <cutils/compiler.h>
#include <gui/Surface.h>
@@ -269,6 +272,7 @@
const sp<AMessage> &format,
const sp<IGraphicBufferProducer> &bufferProducer,
const sp<ICrypto> &crypto,
+ const sp<IDescrambler> &descrambler,
int flags) {
sp<Surface> client;
if (bufferProducer != NULL) {
@@ -278,7 +282,8 @@
mSurfaceTextureClient.clear();
}
- return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
+ return mCodec->configure(
+ format, mSurfaceTextureClient, crypto, descrambler, flags);
}
status_t JMediaCodec::setSurface(
@@ -967,6 +972,7 @@
jobjectArray keys, jobjectArray values,
jobject jsurface,
jobject jcrypto,
+ jobject descramblerBinderObj,
jint flags) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -1002,7 +1008,13 @@
crypto = JCrypto::GetCrypto(env, jcrypto);
}
- err = codec->configure(format, bufferProducer, crypto, flags);
+ sp<IDescrambler> descrambler;
+ if (descramblerBinderObj != NULL) {
+ sp<IBinder> binder = ibinderForJavaObject(env, descramblerBinderObj);
+ descrambler = interface_cast<IDescrambler>(binder);
+ }
+
+ err = codec->configure(format, bufferProducer, crypto, descrambler, flags);
throwExceptionAsNecessary(env, err);
}
@@ -1942,7 +1954,7 @@
{ "native_configure",
"([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
- "Landroid/media/MediaCrypto;I)V",
+ "Landroid/media/MediaCrypto;Landroid/os/IBinder;I)V",
(void *)android_media_MediaCodec_native_configure },
{ "native_setSurface",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index b3b1b3a..a8c76c5 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -35,6 +35,10 @@
struct MediaCodec;
struct PersistentSurface;
class Surface;
+namespace media {
+class IDescrambler;
+};
+using namespace media;
struct JMediaCodec : public AHandler {
JMediaCodec(
@@ -54,6 +58,7 @@
const sp<AMessage> &format,
const sp<IGraphicBufferProducer> &bufferProducer,
const sp<ICrypto> &crypto,
+ const sp<IDescrambler> &descrambler,
int flags);
status_t setSurface(
diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp
new file mode 100644
index 0000000..7585664
--- /dev/null
+++ b/media/jni/android_media_MediaDescrambler.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2017, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaDescrambler-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaDescrambler.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
+#include "JNIHelp.h"
+
+#include <android/media/IDescrambler.h>
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+using media::MediaDescrambler::DescrambleInfo;
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
+ return (JDescrambler *)env->GetLongField(thiz, gFields.context);
+}
+
+static void setDescrambler(
+ JNIEnv *env, jobject thiz, const sp<JDescrambler> &descrambler) {
+ sp<JDescrambler> old = (JDescrambler *)env->GetLongField(thiz, gFields.context);
+ if (descrambler != NULL) {
+ descrambler->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetLongField(thiz, gFields.context, (jlong)descrambler.get());
+}
+
+static status_t getBufferAndSize(
+ JNIEnv *env, jobject byteBuf, jint offset, size_t length,
+ void **outPtr, jbyteArray *outByteArray) {
+ void *ptr = env->GetDirectBufferAddress(byteBuf);
+
+ size_t bufSize;
+ jbyteArray byteArray = NULL;
+
+ ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
+ CHECK(byteBufClass.get() != NULL);
+
+ if (ptr == NULL) {
+ jmethodID arrayID =
+ env->GetMethodID(byteBufClass.get(), "array", "()[B");
+ CHECK(arrayID != NULL);
+
+ byteArray =
+ (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
+
+ if (byteArray == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ jboolean isCopy;
+ ptr = env->GetByteArrayElements(byteArray, &isCopy);
+
+ bufSize = (size_t) env->GetArrayLength(byteArray);
+ } else {
+ bufSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
+ }
+
+ if (length + offset > bufSize) {
+ if (byteArray != NULL) {
+ env->ReleaseByteArrayElements(byteArray, (jbyte *)ptr, 0);
+ }
+
+ return -ERANGE;
+ }
+
+ *outPtr = ptr;
+ *outByteArray = byteArray;
+
+ return OK;
+}
+
+JDescrambler::JDescrambler(JNIEnv *env, jobject descramblerBinderObj) {
+ sp<IDescrambler> cas;
+ if (descramblerBinderObj != NULL) {
+ sp<IBinder> binder = ibinderForJavaObject(env, descramblerBinderObj);
+ mDescrambler = interface_cast<IDescrambler>(binder);
+ }
+}
+
+JDescrambler::~JDescrambler() {
+ // Don't call release() here, it's called by Java class
+ mDescrambler.clear();
+ mMem.clear();
+ mDealer.clear();
+}
+
+void JDescrambler::ensureBufferCapacity(size_t neededSize) {
+ if (mMem != NULL && mMem->size() >= neededSize) {
+ return;
+ }
+
+ ALOGV("ensureBufferCapacity: current size %zu, new size %zu",
+ mMem == NULL ? 0 : mMem->size(), neededSize);
+
+ size_t alignment = MemoryDealer::getAllocationAlignment();
+ neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1);
+ // Align to multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+ mDealer = new MemoryDealer(neededSize, "JDescrambler");
+ mMem = mDealer->allocate(neededSize);
+}
+
+ssize_t JDescrambler::descramble(
+ jbyte key,
+ size_t numSubSamples,
+ ssize_t totalLength,
+ DescramblerPlugin::SubSample *subSamples,
+ const void *srcPtr,
+ jint srcOffset,
+ void *dstPtr,
+ jint dstOffset) {
+ // TODO: IDescrambler::descramble() is re-entrant, however because we
+ // only have 1 shared mem buffer, we can only do 1 descramble at a time.
+ // Concurrency might be improved by allowing on-demand allocation of up
+ // to 2 shared mem buffers.
+ Mutex::Autolock autolock(mSharedMemLock);
+
+ ensureBufferCapacity(totalLength);
+
+ memcpy(mMem->pointer(),
+ (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
+
+ DescrambleInfo info;
+ info.dstType = DescrambleInfo::kDestinationTypeVmPointer;
+ info.numSubSamples = numSubSamples;
+ info.scramblingControl = (DescramblerPlugin::ScramblingControl) key;
+ info.subSamples = subSamples;
+ info.srcMem = mMem;
+ info.srcOffset = 0;
+ info.dstPtr = NULL;
+ info.dstOffset = 0;
+
+ int32_t result;
+ binder::Status status = mDescrambler->descramble(info, &result);
+
+ if (!status.isOk() || result > totalLength) {
+ return -1;
+ }
+ if (result > 0) {
+ memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), result);
+ }
+ return result;
+}
+
+} // namespace android
+
+using namespace android;
+
+static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
+ setDescrambler(env, thiz, NULL);
+}
+
+static void android_media_MediaDescrambler_native_init(JNIEnv *env) {
+ ScopedLocalRef<jclass> clazz(
+ env, env->FindClass("android/media/MediaDescrambler"));
+ CHECK(clazz.get() != NULL);
+
+ gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
+ CHECK(gFields.context != NULL);
+}
+
+static void android_media_MediaDescrambler_native_setup(
+ JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
+ setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
+}
+
+static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
+ jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
+ DescramblerPlugin::SubSample **outSubSamples) {
+
+ if (numSubSamples <= 0 || numSubSamples >=
+ (signed)(INT32_MAX / sizeof(DescramblerPlugin::SubSample)) ) {
+ // subSamples array may silently overflow if number of samples are
+ // too large. Use INT32_MAX as maximum allocation size may be less
+ // than SIZE_MAX on some platforms.
+ ALOGE("numSubSamples is invalid!");
+ return -1;
+ }
+
+ jboolean isCopy;
+ ssize_t totalSize = 0;
+
+ jint *numBytesOfClearData =
+ (numBytesOfClearDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
+
+ jint *numBytesOfEncryptedData =
+ (numBytesOfEncryptedDataObj == NULL)
+ ? NULL
+ : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+
+ DescramblerPlugin::SubSample *subSamples =
+ new(std::nothrow) DescramblerPlugin::SubSample[numSubSamples];
+
+ if (subSamples == NULL) {
+ ALOGE("Failed to allocate SubSample array!");
+ return -1;
+ }
+
+ for (jint i = 0; i < numSubSamples; ++i) {
+ subSamples[i].mNumBytesOfClearData =
+ (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
+
+ subSamples[i].mNumBytesOfEncryptedData =
+ (numBytesOfEncryptedData == NULL)
+ ? 0 : numBytesOfEncryptedData[i];
+
+ totalSize += subSamples[i].mNumBytesOfClearData +
+ subSamples[i].mNumBytesOfEncryptedData;
+ }
+
+ if (numBytesOfEncryptedData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
+ numBytesOfEncryptedData = NULL;
+ }
+
+ if (numBytesOfClearData != NULL) {
+ env->ReleaseIntArrayElements(
+ numBytesOfClearDataObj, numBytesOfClearData, 0);
+ numBytesOfClearData = NULL;
+ }
+
+ *outSubSamples = subSamples;
+
+ return totalSize;
+}
+
+static jint android_media_MediaDescrambler_native_descramble(
+ JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
+ jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
+ jobject srcBuf, jint srcOffset, jobject dstBuf, jint dstOffset) {
+ sp<JDescrambler> descrambler = getDescrambler(env, thiz);
+ if (descrambler == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1;
+ }
+
+ DescramblerPlugin::SubSample *subSamples = NULL;
+ ssize_t totalLength = getSubSampleInfo(
+ env, numSubSamples, numBytesOfClearDataObj,
+ numBytesOfEncryptedDataObj, &subSamples);
+ if (totalLength < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Invalid sub sample info!");
+ return -1;
+ }
+
+ ssize_t result = -1;
+ void *srcPtr = NULL, *dstPtr = NULL;
+ jbyteArray srcArray = NULL, dstArray = NULL;
+ status_t err = getBufferAndSize(
+ env, srcBuf, srcOffset, totalLength, &srcPtr, &srcArray);
+
+ if (err == OK) {
+ if (dstBuf == NULL) {
+ dstPtr = srcPtr;
+ } else {
+ err = getBufferAndSize(
+ env, dstBuf, dstOffset, totalLength, &dstPtr, &dstArray);
+ }
+ }
+
+ if (err == OK) {
+ result = descrambler->descramble(
+ key, numSubSamples, totalLength, subSamples,
+ srcPtr, srcOffset, dstPtr, dstOffset);
+ }
+
+ delete[] subSamples;
+ if (srcArray != NULL) {
+ env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
+ }
+ if (dstArray != NULL) {
+ env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
+ }
+ return result;
+}
+
+static const JNINativeMethod gMethods[] = {
+ { "native_release", "()V",
+ (void *)android_media_MediaDescrambler_native_release },
+ { "native_init", "()V",
+ (void *)android_media_MediaDescrambler_native_init },
+ { "native_setup", "(Landroid/os/IBinder;)V",
+ (void *)android_media_MediaDescrambler_native_setup },
+ { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;I)I",
+ (void *)android_media_MediaDescrambler_native_descramble },
+};
+
+int register_android_media_Descrambler(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_MediaDescrambler.h b/media/jni/android_media_MediaDescrambler.h
new file mode 100644
index 0000000..e944a90
--- /dev/null
+++ b/media/jni/android_media_MediaDescrambler.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017, 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_DESCRAMBLER_H_
+#define _ANDROID_MEDIA_DESCRAMBLER_H_
+
+#include "jni.h"
+
+#include <media/cas/DescramblerAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+namespace android {
+class IMemory;
+class MemoryDealer;
+namespace media {
+class IDescrambler;
+};
+using namespace media;
+
+struct JDescrambler : public RefBase {
+ JDescrambler(JNIEnv *env, jobject descramberBinderObj);
+
+ ssize_t descramble(
+ jbyte key,
+ size_t numSubSamples,
+ ssize_t totalLength,
+ DescramblerPlugin::SubSample *subSamples,
+ const void *srcPtr,
+ jint srcOffset,
+ void *dstPtr,
+ jint dstOffset);
+
+protected:
+ virtual ~JDescrambler();
+
+private:
+ sp<IDescrambler> mDescrambler;
+ sp<IMemory> mMem;
+ sp<MemoryDealer> mDealer;
+ Mutex mSharedMemLock;
+
+ void ensureBufferCapacity(size_t neededSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JDescrambler);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_DESCRAMBLER_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 2008f8d..3c33493 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -37,6 +37,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/NuMediaExtractor.h>
+#include <android/media/ICas.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -88,6 +89,10 @@
return mImpl->setDataSource(datasource);
}
+status_t JMediaExtractor::setMediaCas(const sp<ICas> &cas) {
+ return mImpl->setMediaCas(cas);
+}
+
size_t JMediaExtractor::countTracks() const {
return mImpl->countTracks();
}
@@ -734,6 +739,36 @@
}
}
+static void android_media_MediaExtractor_setMediaCas(
+ JNIEnv *env, jobject thiz, jobject casBinderObj) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ if (casBinderObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ sp<ICas> cas;
+ if (casBinderObj != NULL) {
+ sp<IBinder> binder = ibinderForJavaObject(env, casBinderObj);
+ cas = interface_cast<ICas>(binder);
+ }
+ status_t err = extractor->setMediaCas(cas);
+
+ if (err != OK) {
+ cas.clear();
+ jniThrowException(
+ env,
+ "java/io/IllegalArgumentException",
+ "Failed to set MediaCas on extractor.");
+ }
+}
+
static jlong android_media_MediaExtractor_getCachedDurationUs(
JNIEnv *env, jobject thiz) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -861,6 +896,9 @@
{ "setDataSource", "(Landroid/media/MediaDataSource;)V",
(void *)android_media_MediaExtractor_setDataSourceCallback },
+ { "nativeSetMediaCas", "(Landroid/os/IBinder;)V",
+ (void *)android_media_MediaExtractor_setMediaCas },
+
{ "getCachedDuration", "()J",
(void *)android_media_MediaExtractor_getCachedDurationUs },
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index c747ef5..3d8c50b 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -28,6 +28,10 @@
#include "jni.h"
namespace android {
+namespace media {
+class ICas;
+};
+using namespace media;
struct IMediaHTTPService;
class MetaData;
@@ -44,6 +48,8 @@
status_t setDataSource(int fd, off64_t offset, off64_t size);
status_t setDataSource(const sp<DataSource> &source);
+ status_t setMediaCas(const sp<ICas> &cas);
+
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 636727e..27724a1 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1445,6 +1445,7 @@
extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
+extern int register_android_media_Descrambler(JNIEnv *env);
extern int register_android_media_MediaCodec(JNIEnv *env);
extern int register_android_media_MediaExtractor(JNIEnv *env);
extern int register_android_media_MediaCodecList(JNIEnv *env);
@@ -1561,6 +1562,11 @@
goto bail;
}
+ if (register_android_media_Descrambler(env) < 0) {
+ ALOGE("ERROR: MediaDescrambler native registration failed");
+ goto bail;
+ }
+
if (register_android_media_MediaHTTPConnection(env) < 0) {
ALOGE("ERROR: MediaHTTPConnection native registration failed");
goto bail;
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 69544f5..1c1ff82 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -11,12 +11,10 @@
asset_manager.cpp \
choreographer.cpp \
configuration.cpp \
- hardware_buffer.cpp \
hardware_buffer_jni.cpp \
input.cpp \
looper.cpp \
native_activity.cpp \
- native_window.cpp \
native_window_jni.cpp \
net.c \
obb.cpp \
@@ -38,7 +36,10 @@
LOCAL_STATIC_LIBRARIES := \
libstorage \
- libarect
+ libarect \
+
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+ libnativewindow
LOCAL_C_INCLUDES += \
frameworks/base/native/include \
@@ -46,7 +47,9 @@
bionic/libc/dns/include \
system/netd/include \
-LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libarect
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+ libarect \
+ libnativewindow \
LOCAL_MODULE := libandroid
diff --git a/native/android/hardware_buffer.cpp b/native/android/hardware_buffer.cpp
deleted file mode 100644
index 77ebd52..0000000
--- a/native/android/hardware_buffer.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "AHardwareBuffer"
-
-#include <android/hardware_buffer.h>
-
-#include <errno.h>
-#include <sys/socket.h>
-
-#include <memory>
-
-#include <cutils/native_handle.h>
-
-#include <utils/Log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <binder/IServiceManager.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/IGraphicBufferAlloc.h>
-
-#include <android_runtime/android_hardware_HardwareBuffer.h>
-
-
-static constexpr int kDataBufferSize = 64 * sizeof(int); // 64 ints
-
-using namespace android;
-
-static inline const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(
- const AHardwareBuffer* buffer) {
- return reinterpret_cast<const GraphicBuffer*>(buffer);
-}
-
-static inline GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(
- AHardwareBuffer* buffer) {
- return reinterpret_cast<GraphicBuffer*>(buffer);
-}
-
-static inline AHardwareBuffer* GraphicBuffer_to_AHardwareBuffer(
- GraphicBuffer* buffer) {
- return reinterpret_cast<AHardwareBuffer*>(buffer);
-}
-
-// ----------------------------------------------------------------------------
-// Public functions
-// ----------------------------------------------------------------------------
-
-int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
- AHardwareBuffer** outBuffer) {
- if (!outBuffer || !desc) return BAD_VALUE;
-
- // The holder is used to destroy the buffer if an error occurs.
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == nullptr) {
- ALOGE("Unable to connect to ServiceManager");
- return PERMISSION_DENIED;
- }
-
- // Get the SurfaceFlingerService.
- sp<ISurfaceComposer> composer = interface_cast<ISurfaceComposer>(
- sm->getService(String16("SurfaceFlinger")));
- if (composer == nullptr) {
- ALOGE("Unable to connect to surface composer");
- return PERMISSION_DENIED;
- }
- // Get an IGraphicBufferAlloc to create the buffer.
- sp<IGraphicBufferAlloc> allocator = composer->createGraphicBufferAlloc();
- if (allocator == nullptr) {
- ALOGE("Unable to obtain a buffer allocator");
- return PERMISSION_DENIED;
- }
-
- int format = android_hardware_HardwareBuffer_convertToPixelFormat(
- desc->format);
- if (format == 0) {
- ALOGE("Invalid pixel format");
- return BAD_VALUE;
- }
-
- if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
- ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB "
- "format");
- return BAD_VALUE;
- }
-
- status_t err;
- uint64_t producerUsage = 0;
- uint64_t consumerUsage = 0;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(desc->usage0,
- desc->usage1, &producerUsage, &consumerUsage);
- sp<GraphicBuffer> gbuffer = allocator->createGraphicBuffer(desc->width,
- desc->height, format, desc->layers, producerUsage, consumerUsage,
- std::string("AHardwareBuffer pid [") + std::to_string(getpid()) +
- "]", &err);
- if (err != NO_ERROR) {
- return err;
- }
-
- *outBuffer = GraphicBuffer_to_AHardwareBuffer(gbuffer.get());
- // Ensure the buffer doesn't get destroyed with the sp<> goes away.
- AHardwareBuffer_acquire(*outBuffer);
- return NO_ERROR;
-}
-
-void AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
- AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong(
- (void*)AHardwareBuffer_acquire);
-}
-
-void AHardwareBuffer_release(AHardwareBuffer* buffer) {
- AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong(
- (void*)AHardwareBuffer_acquire);
-}
-
-void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* outDesc) {
- if (!buffer || !outDesc) return;
-
- const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
-
- outDesc->width = gbuffer->getWidth();
- outDesc->height = gbuffer->getHeight();
- outDesc->layers = gbuffer->getLayerCount();
- outDesc->usage0 =
- android_hardware_HardwareBuffer_convertFromGrallocUsageBits(
- gbuffer->getUsage(), gbuffer->getUsage());
- outDesc->usage1 = 0;
- outDesc->format = android_hardware_HardwareBuffer_convertFromPixelFormat(
- static_cast<uint32_t>(gbuffer->getPixelFormat()));
-}
-
-int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
- int32_t fence, const ARect* rect, void** outVirtualAddress) {
- if (!buffer) return BAD_VALUE;
-
- if (usage0 & ~(AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)) {
- ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- " AHARDWAREBUFFER_USAGE0_CPU_* flags are allowed");
- return BAD_VALUE;
- }
-
- uint64_t producerUsage = 0;
- uint64_t consumerUsage = 0;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(usage0, 0,
- &producerUsage, &consumerUsage);
- GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
- Rect bounds;
- if (!rect) {
- bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
- } else {
- bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
- }
- return gBuffer->lockAsync(producerUsage, consumerUsage, bounds,
- outVirtualAddress, fence);
-}
-
-int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
- if (!buffer) return BAD_VALUE;
-
- GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
- return gBuffer->unlockAsync(fence);
-}
-
-int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer,
- int socketFd) {
- if (!buffer) return BAD_VALUE;
- const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
-
- size_t flattenedSize = gBuffer->getFlattenedSize();
- size_t fdCount = gBuffer->getFdCount();
-
- std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]);
- std::unique_ptr<int[]> fds(new int[fdCount]);
-
- // Make copies of needed items since flatten modifies them, and we don't
- // want to send anything if there's an error during flatten.
- size_t flattenedSizeCopy = flattenedSize;
- size_t fdCountCopy = fdCount;
- void* dataStart = data.get();
- int* fdsStart = fds.get();
- status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart,
- fdCountCopy);
- if (err != NO_ERROR) {
- return err;
- }
-
- struct iovec iov[1];
- iov[0].iov_base = data.get();
- iov[0].iov_len = flattenedSize;
-
- char buf[CMSG_SPACE(kDataBufferSize)];
- struct msghdr msg = {
- .msg_control = buf,
- .msg_controllen = sizeof(buf),
- .msg_iov = &iov[0],
- .msg_iovlen = 1,
- };
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
- int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- memcpy(fdData, fds.get(), sizeof(int) * fdCount);
- msg.msg_controllen = cmsg->cmsg_len;
-
- int result = sendmsg(socketFd, &msg, 0);
- if (result <= 0) {
- ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
- result, strerror(errno));
- return result;
- }
- return NO_ERROR;
-}
-
-int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
- AHardwareBuffer** outBuffer) {
- if (!outBuffer) return BAD_VALUE;
-
- char dataBuf[CMSG_SPACE(kDataBufferSize)];
- char fdBuf[CMSG_SPACE(kDataBufferSize)];
- struct iovec iov[1];
- iov[0].iov_base = dataBuf;
- iov[0].iov_len = sizeof(dataBuf);
-
- struct msghdr msg = {
- .msg_control = fdBuf,
- .msg_controllen = sizeof(fdBuf),
- .msg_iov = &iov[0],
- .msg_iovlen = 1,
- };
-
- int result = recvmsg(socketFd, &msg, 0);
- if (result <= 0) {
- ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
- result, strerror(errno));
- return result;
- }
-
- if (msg.msg_iovlen != 1) {
- ALOGE("Error reading AHardwareBuffer from socket: bad data length");
- return INVALID_OPERATION;
- }
-
- if (msg.msg_controllen % sizeof(int) != 0) {
- ALOGE("Error reading AHardwareBuffer from socket: bad fd length");
- return INVALID_OPERATION;
- }
-
- size_t dataLen = msg.msg_iov[0].iov_len;
- const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base);
- if (!data) {
- ALOGE("Error reading AHardwareBuffer from socket: no buffer data");
- return INVALID_OPERATION;
- }
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg) {
- ALOGE("Error reading AHardwareBuffer from socket: no fd header");
- return INVALID_OPERATION;
- }
-
- size_t fdCount = msg.msg_controllen >> 2;
- const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
- if (!fdData) {
- ALOGE("Error reading AHardwareBuffer from socket: no fd data");
- return INVALID_OPERATION;
- }
-
- GraphicBuffer* gBuffer = new GraphicBuffer();
- status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
- if (err != NO_ERROR) {
- return err;
- }
- *outBuffer = GraphicBuffer_to_AHardwareBuffer(gBuffer);
- // Ensure the buffer has a positive ref-count.
- AHardwareBuffer_acquire(*outBuffer);
-
- return NO_ERROR;
-}
-
-const struct native_handle* AHardwareBuffer_getNativeHandle(
- const AHardwareBuffer* buffer) {
- if (!buffer) return nullptr;
- const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
- return gbuffer->handle;
-}
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
deleted file mode 100644
index 8c080d7..0000000
--- a/native/android/native_window.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define LOG_TAG "Surface"
-
-#include <android/native_window.h>
-#include <system/window.h>
-
-void ANativeWindow_acquire(ANativeWindow* window) {
- window->incStrong((void*)ANativeWindow_acquire);
-}
-
-void ANativeWindow_release(ANativeWindow* window) {
- window->decStrong((void*)ANativeWindow_acquire);
-}
-
-static int32_t getWindowProp(ANativeWindow* window, int what) {
- int value;
- int res = window->query(window, what, &value);
- return res < 0 ? res : value;
-}
-
-int32_t ANativeWindow_getWidth(ANativeWindow* window) {
- return getWindowProp(window, NATIVE_WINDOW_WIDTH);
-}
-
-int32_t ANativeWindow_getHeight(ANativeWindow* window) {
- return getWindowProp(window, NATIVE_WINDOW_HEIGHT);
-}
-
-int32_t ANativeWindow_getFormat(ANativeWindow* window) {
- return getWindowProp(window, NATIVE_WINDOW_FORMAT);
-}
-
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
- int32_t height, int32_t format) {
- int32_t err = native_window_set_buffers_format(window, format);
- if (!err) {
- err = native_window_set_buffers_user_dimensions(window, width, height);
- if (!err) {
- int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (width && height) {
- mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
- }
- err = native_window_set_scaling_mode(window, mode);
- }
- }
- return err;
-}
-
-int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
- ARect* inOutDirtyBounds) {
- return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds);
-}
-
-int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
- return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST);
-}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 22a5b7f..3cc9f65e 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -56,6 +56,7 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.FileSystemProvider;
import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
@@ -63,15 +64,15 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
-public class ExternalStorageProvider extends DocumentsProvider {
+public class ExternalStorageProvider extends FileSystemProvider {
private static final String TAG = "ExternalStorage";
private static final boolean DEBUG = false;
- private static final boolean LOG_INOTIFY = false;
public static final String AUTHORITY = "com.android.externalstorage.documents";
@@ -105,20 +106,17 @@
private static final String ROOT_ID_HOME = "home";
private StorageManager mStorageManager;
- private Handler mHandler;
private final Object mRootsLock = new Object();
@GuardedBy("mRootsLock")
private ArrayMap<String, RootInfo> mRoots = new ArrayMap<>();
- @GuardedBy("mObservers")
- private ArrayMap<File, DirectoryObserver> mObservers = new ArrayMap<>();
-
@Override
public boolean onCreate() {
+ super.onCreate(DEFAULT_DOCUMENT_PROJECTION);
+
mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
- mHandler = new Handler();
updateVolumes();
return true;
@@ -274,11 +272,8 @@
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
- private static String[] resolveDocumentProjection(String[] projection) {
- return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
- }
-
- private String getDocIdForFile(File file) throws FileNotFoundException {
+ @Override
+ protected String getDocIdForFile(File file) throws FileNotFoundException {
return getDocIdForFileMaybeCreate(file, false);
}
@@ -344,11 +339,8 @@
return mostSpecificRoot;
}
- private File getFileForDocId(String docId) throws FileNotFoundException {
- return getFileForDocId(docId, false);
- }
-
- private File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
+ @Override
+ protected File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
RootInfo root = getRootFromDocId(docId);
return buildFile(root, docId, visible);
}
@@ -393,48 +385,9 @@
return target;
}
- private void includeFile(MatrixCursor result, String docId, File file)
- throws FileNotFoundException {
- if (docId == null) {
- docId = getDocIdForFile(file);
- } else {
- file = getFileForDocId(docId);
- }
-
- int flags = 0;
-
- if (file.canWrite()) {
- if (file.isDirectory()) {
- flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
- flags |= Document.FLAG_SUPPORTS_DELETE;
- flags |= Document.FLAG_SUPPORTS_RENAME;
- flags |= Document.FLAG_SUPPORTS_MOVE;
- } else {
- flags |= Document.FLAG_SUPPORTS_WRITE;
- flags |= Document.FLAG_SUPPORTS_DELETE;
- flags |= Document.FLAG_SUPPORTS_RENAME;
- flags |= Document.FLAG_SUPPORTS_MOVE;
- }
- }
-
- final String mimeType = getTypeForFile(file);
- final String displayName = file.getName();
- if (mimeType.startsWith("image/")) {
- flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
- }
-
- final RowBuilder row = result.newRow();
- row.add(Document.COLUMN_DOCUMENT_ID, docId);
- row.add(Document.COLUMN_DISPLAY_NAME, displayName);
- row.add(Document.COLUMN_SIZE, file.length());
- row.add(Document.COLUMN_MIME_TYPE, mimeType);
- row.add(Document.COLUMN_FLAGS, flags);
-
- // Only publish dates reasonably after epoch
- long lastModified = file.lastModified();
- if (lastModified > 31536000000L) {
- row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
- }
+ @Override
+ protected Uri buildNotificationUri(String docId) {
+ return DocumentsContract.buildChildDocumentsUri(AUTHORITY, docId);
}
@Override
@@ -455,22 +408,8 @@
}
@Override
- public boolean isChildDocument(String parentDocId, String docId) {
- try {
- final File parent = getFileForDocId(parentDocId).getCanonicalFile();
- final File doc = getFileForDocId(docId).getCanonicalFile();
- return FileUtils.contains(parent, doc);
- } catch (IOException e) {
- throw new IllegalArgumentException(
- "Failed to determine if " + docId + " is child of " + parentDocId + ": " + e);
- }
- }
-
- @Override
public Path findDocumentPath(String childDocId, @Nullable String parentDocId)
throws FileNotFoundException {
- LinkedList<String> path = new LinkedList<>();
-
final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId, false);
final RootInfo root = resolvedDocId.first;
File child = resolvedDocId.second;
@@ -479,49 +418,7 @@
? root.path
: getFileForDocId(parentDocId);
- if (!child.exists()) {
- throw new FileNotFoundException(childDocId + " is not found.");
- }
-
- if (!child.getAbsolutePath().startsWith(parent.getAbsolutePath())) {
- throw new FileNotFoundException(childDocId + " is not found under " + parentDocId);
- }
-
- while (child != null && child.getAbsolutePath().startsWith(parent.getAbsolutePath())) {
- path.addFirst(getDocIdForFile(child));
-
- child = child.getParentFile();
- }
-
- return new Path(parentDocId == null ? root.rootId : null, path);
- }
-
- @Override
- public String createDocument(String docId, String mimeType, String displayName)
- throws FileNotFoundException {
- displayName = FileUtils.buildValidFatFilename(displayName);
-
- final File parent = getFileForDocId(docId);
- if (!parent.isDirectory()) {
- throw new IllegalArgumentException("Parent document isn't a directory");
- }
-
- 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);
- }
- } else {
- try {
- if (!file.createNewFile()) {
- throw new IllegalStateException("Failed to touch " + file);
- }
- } catch (IOException e) {
- throw new IllegalStateException("Failed to touch " + file + ": " + e);
- }
- }
-
- return getDocIdForFile(file);
+ return new Path(parentDocId == null ? root.rootId : null, findDocumentPath(parent, child));
}
private Uri getDocumentUri(String path, List<UriPermission> accessUriPermissions)
@@ -587,120 +484,14 @@
}
@Override
- public String renameDocument(String docId, String displayName) throws FileNotFoundException {
- // Since this provider treats renames as generating a completely new
- // docId, we're okay with letting the MIME type change.
- displayName = FileUtils.buildValidFatFilename(displayName);
-
- final File before = getFileForDocId(docId);
- final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
- if (!before.renameTo(after)) {
- throw new IllegalStateException("Failed to rename to " + after);
- }
- final String afterDocId = getDocIdForFile(after);
- if (!TextUtils.equals(docId, afterDocId)) {
- return afterDocId;
- } else {
- return null;
- }
- }
-
- @Override
- public void deleteDocument(String docId) throws FileNotFoundException {
- final File file = getFileForDocId(docId);
- final File visibleFile = getFileForDocId(docId, true);
-
- final boolean isDirectory = file.isDirectory();
- if (isDirectory) {
- FileUtils.deleteContents(file);
- }
- if (!file.delete()) {
- throw new IllegalStateException("Failed to delete " + file);
- }
-
- if (visibleFile != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri externalUri = MediaStore.Files.getContentUri("external");
-
- // Remove media store entries for any files inside this directory, using
- // path prefix match. Logic borrowed from MtpDatabase.
- if (isDirectory) {
- final String path = visibleFile.getAbsolutePath() + "/";
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[] { path + "%", Integer.toString(path.length()), path });
- }
-
- // Remove media store entry for this exact file.
- final String path = visibleFile.getAbsolutePath();
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(_data)=lower(?2)",
- new String[] { path, path });
- }
- }
-
- @Override
- public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
- String targetParentDocumentId)
- throws FileNotFoundException {
- final File before = getFileForDocId(sourceDocumentId);
- final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
-
- if (after.exists()) {
- throw new IllegalStateException("Already exists " + after);
- }
- if (!before.renameTo(after)) {
- throw new IllegalStateException("Failed to move to " + after);
- }
- return getDocIdForFile(after);
- }
-
- @Override
- public Cursor queryDocument(String documentId, String[] projection)
- throws FileNotFoundException {
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
- includeFile(result, documentId, null);
- return result;
- }
-
- @Override
- public Cursor queryChildDocuments(
- String parentDocumentId, String[] projection, String sortOrder)
- throws FileNotFoundException {
- final File parent = getFileForDocId(parentDocumentId);
- final MatrixCursor result = new DirectoryCursor(
- resolveDocumentProjection(projection), parentDocumentId, parent);
- for (File file : parent.listFiles()) {
- includeFile(result, null, file);
- }
- return result;
- }
-
- @Override
public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException {
- final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
-
- query = query.toLowerCase();
final File parent;
synchronized (mRootsLock) {
parent = mRoots.get(rootId).path;
}
- final LinkedList<File> pending = new LinkedList<>();
- pending.add(parent);
- while (!pending.isEmpty() && result.getCount() < 24) {
- final File file = pending.removeFirst();
- if (file.isDirectory()) {
- for (File child : file.listFiles()) {
- pending.add(child);
- }
- }
- if (file.getName().toLowerCase().contains(query)) {
- includeFile(result, null, file);
- }
- }
- return result;
+ return querySearchDocuments(parent, query, projection, Collections.emptySet());
}
@Override
@@ -722,48 +513,6 @@
}
@Override
- public String getDocumentType(String documentId) throws FileNotFoundException {
- final File file = getFileForDocId(documentId);
- return getTypeForFile(file);
- }
-
- @Override
- public ParcelFileDescriptor openDocument(
- String documentId, String mode, CancellationSignal signal)
- throws FileNotFoundException {
- final File file = getFileForDocId(documentId);
- final File visibleFile = getFileForDocId(documentId, true);
-
- final int pfdMode = ParcelFileDescriptor.parseMode(mode);
- if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
- return ParcelFileDescriptor.open(file, pfdMode);
- } else {
- try {
- // When finished writing, kick off media scanner
- return ParcelFileDescriptor.open(file, pfdMode, mHandler, new OnCloseListener() {
- @Override
- public void onClose(IOException e) {
- final Intent intent = new Intent(
- Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(visibleFile));
- getContext().sendBroadcast(intent);
- }
- });
- } catch (IOException e) {
- throw new FileNotFoundException("Failed to open for writing: " + e);
- }
- }
- }
-
- @Override
- public AssetFileDescriptor openDocumentThumbnail(
- String documentId, Point sizeHint, CancellationSignal signal)
- throws FileNotFoundException {
- final File file = getFileForDocId(documentId);
- return DocumentsContract.openImageThumbnail(file);
- }
-
- @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
synchronized (mRootsLock) {
@@ -826,107 +575,4 @@
}
return bundle;
}
-
- private static String getTypeForFile(File file) {
- if (file.isDirectory()) {
- return Document.MIME_TYPE_DIR;
- } else {
- return getTypeForName(file.getName());
- }
- }
-
- private static String getTypeForName(String name) {
- final int lastDot = name.lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1).toLowerCase();
- final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mime != null) {
- return mime;
- }
- }
-
- return "application/octet-stream";
- }
-
- private void startObserving(File file, Uri notifyUri) {
- synchronized (mObservers) {
- DirectoryObserver observer = mObservers.get(file);
- if (observer == null) {
- observer = new DirectoryObserver(
- file, getContext().getContentResolver(), notifyUri);
- observer.startWatching();
- mObservers.put(file, observer);
- }
- observer.mRefCount++;
-
- if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
- }
- }
-
- private void stopObserving(File file) {
- synchronized (mObservers) {
- DirectoryObserver observer = mObservers.get(file);
- if (observer == null) return;
-
- observer.mRefCount--;
- if (observer.mRefCount == 0) {
- mObservers.remove(file);
- observer.stopWatching();
- }
-
- if (LOG_INOTIFY) Log.d(TAG, "after stop: " + observer);
- }
- }
-
- private static class DirectoryObserver extends FileObserver {
- private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO
- | CREATE | DELETE | DELETE_SELF | MOVE_SELF;
-
- private final File mFile;
- private final ContentResolver mResolver;
- private final Uri mNotifyUri;
-
- private int mRefCount = 0;
-
- public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
- super(file.getAbsolutePath(), NOTIFY_EVENTS);
- mFile = file;
- mResolver = resolver;
- mNotifyUri = notifyUri;
- }
-
- @Override
- public void onEvent(int event, String path) {
- if ((event & NOTIFY_EVENTS) != 0) {
- if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
- mResolver.notifyChange(mNotifyUri, null, false);
- }
- }
-
- @Override
- public String toString() {
- return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}";
- }
- }
-
- private class DirectoryCursor extends MatrixCursor {
- private final File mFile;
-
- public DirectoryCursor(String[] columnNames, String docId, File file) {
- super(columnNames);
-
- final Uri notifyUri = DocumentsContract.buildChildDocumentsUri(
- AUTHORITY, docId);
- setNotificationUri(getContext().getContentResolver(), notifyUri);
-
- mFile = file;
- startObserving(mFile, notifyUri);
- }
-
- @Override
- public void close() {
- super.close();
- stopObserving(mFile);
- }
- }
}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index d207c35..9ff22c7 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -538,7 +538,7 @@
<item>100</item>
</array>
<array name="batterymeter_color_values">
- <item>@*android:color/battery_saver_mode_color</item>
+ <item>?android:attr/colorError</item>
<item>@android:color/white</item>
</array>
<array name="batterymeter_bolt_points">
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 99d7f1e..49fc2ea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -16,6 +16,11 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
@@ -29,6 +34,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.text.Spanned;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
@@ -108,47 +114,68 @@
}
/**
- * Checks if keyguard features are disabled by policy.
+ * Checks whether keyguard features are disabled by policy.
*
- * @param keyguardFeatures Could be any of keyguard features that can be
+ * @param context {@link Context} for the calling user.
+ *
+ * @param keyguardFeatures Any one of keyguard features that can be
* disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
+ *
+ * @param userId User to check enforced admin status for.
+ *
* @return EnforcedAdmin Object containing the enforced admin component and admin user details,
* or {@code null} If the notification features are not disabled. If the restriction is set by
* multiple admins, then the admin component will be set to {@code null} and userId to
* {@link UserHandle#USER_NULL}.
*/
public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
- int keyguardFeatures, int userId) {
- final LockSettingCheck check =
- (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int checkUser) ->
- (dpm.getKeyguardDisabledFeatures(admin, checkUser) & keyguardFeatures) != 0;
+ int keyguardFeatures, final @UserIdInt int userId) {
+ final LockSettingCheck check = (dpm, admin, checkUser) -> {
+ int effectiveFeatures = dpm.getKeyguardDisabledFeatures(admin, checkUser);
+ if (checkUser != userId) {
+ effectiveFeatures &= PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+ }
+ return (effectiveFeatures & keyguardFeatures) != KEYGUARD_DISABLE_FEATURES_NONE;
+ };
+ if (UserManager.get(context).getUserInfo(userId).isManagedProfile()) {
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ return findEnforcedAdmin(dpm.getActiveAdminsAsUser(userId), dpm, userId, check);
+ }
+ return checkForLockSetting(context, userId, check);
+ }
- final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (dpm == null) {
+ /**
+ * Filter a set of device admins based on a predicate {@code check}. This is equivalent to
+ * {@code admins.stream().filter(check).map(x → new EnforcedAdmin(admin, userId)} except it's
+ * returning a zero/one/many-type thing.
+ *
+ * @param admins set of candidate device admins identified by {@link ComponentName}.
+ * @param userId user to create the resultant {@link EnforcedAdmin} as.
+ * @param check filter predicate.
+ *
+ * @return {@code null} if none of the {@param admins} match.
+ * An {@link EnforcedAdmin} if exactly one of the admins matches.
+ * Otherwise, {@link EnforcedAdmin#MULTIPLE_ENFORCED_ADMIN} for multiple matches.
+ */
+ @Nullable
+ private static EnforcedAdmin findEnforcedAdmin(@Nullable List<ComponentName> admins,
+ @NonNull DevicePolicyManager dpm, @UserIdInt int userId,
+ @NonNull LockSettingCheck check) {
+ if (admins == null) {
return null;
}
-
- final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
- if (um.getUserInfo(userId).isManagedProfile()) {
- final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
- if (admins == null) {
- return null;
- }
- EnforcedAdmin enforcedAdmin = null;
- for (ComponentName admin : admins) {
- if (check.isEnforcing(dpm, admin, userId)) {
- if (enforcedAdmin == null) {
- enforcedAdmin = new EnforcedAdmin(admin, userId);
- } else {
- return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
- }
+ EnforcedAdmin enforcedAdmin = null;
+ for (ComponentName admin : admins) {
+ if (check.isEnforcing(dpm, admin, userId)) {
+ if (enforcedAdmin == null) {
+ enforcedAdmin = new EnforcedAdmin(admin, userId);
+ } else {
+ return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
}
- return enforcedAdmin;
- } else {
- return checkForLockSetting(context, userId, check);
}
+ return enforcedAdmin;
}
public static EnforcedAdmin checkIfUninstallBlocked(Context context,
@@ -361,7 +388,7 @@
}
LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
- if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) {
// userId is managed profile and has a separate challenge, only consider
// the admins in that user.
final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
@@ -428,7 +455,7 @@
if (userInfo.isManagedProfile()) {
// If userInfo.id is a managed profile, we also need to look at
// the policies set on the parent.
- final DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
+ DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo);
if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
@@ -448,8 +475,9 @@
/**
* Checks whether any of the user's profiles enforce the lock setting. A managed profile is only
- * included if it does not have a separate challenege but the settings for it's parent (i.e. the
- * user being checked) are always included.
+ * included if it does not have a separate challenge.
+ *
+ * The user identified by {@param userId} is always included.
*/
private static EnforcedAdmin checkForLockSetting(
Context context, @UserIdInt int userId, LockSettingCheck check) {
@@ -468,7 +496,7 @@
continue;
}
final boolean isSeparateProfileChallengeEnabled =
- lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+ sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id);
for (ComponentName admin : admins) {
if (!isSeparateProfileChallengeEnabled) {
if (check.isEnforcing(dpm, admin, userInfo.id)) {
@@ -487,7 +515,7 @@
if (userInfo.isManagedProfile()) {
// If userInfo.id is a managed profile, we also need to look at
// the policies set on the parent.
- final DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
+ DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo);
if (check.isEnforcing(parentDpm, admin, userInfo.id)) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
@@ -733,4 +761,23 @@
other.userId = userId;
}
}
+
+ /**
+ * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes.
+ * {@link LockPatternUtils} is an internal API not supported by robolectric.
+ * {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric.
+ */
+ @VisibleForTesting
+ static Proxy sProxy = new Proxy();
+
+ @VisibleForTesting
+ static class Proxy {
+ public boolean isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle) {
+ return utils.isSeparateProfileChallengeEnabled(userHandle);
+ }
+
+ public DevicePolicyManager getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui) {
+ return dpm.getParentProfileInstance(ui);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 7a4514a..7e7b391 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -184,10 +184,7 @@
@ColorInt
public static int getColorError(Context context) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.textColorError});
- @ColorInt int colorError = ta.getColor(0, 0);
- ta.recycle();
- return colorError;
+ return getColorAttr(context, android.R.attr.colorError);
}
@ColorInt
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 6e10aab8..f31c09b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -33,7 +33,6 @@
import android.os.UserManager;
import android.provider.Settings.Global;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -353,10 +352,7 @@
CharSequence title = null;
String summary = null;
String keyHint = null;
- String uriString = null;
Uri uri = null;
- // Several resources can be using the same provider. Only acquire a single provider.
- Map<String, IContentProvider> providerMap = new ArrayMap<>();
// Get the activity's meta-data
try {
@@ -365,11 +361,7 @@
Bundle metaData = activityInfo.metaData;
if (res != null && metaData != null) {
- if (metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
- iconFromUri = getIconFromUri(context, activityInfo.packageName,
- metaData.getString(META_DATA_PREFERENCE_ICON_URI), providerMap);
- }
- if (iconFromUri == null && metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+ if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
}
if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
@@ -379,13 +371,7 @@
title = metaData.getString(META_DATA_PREFERENCE_TITLE);
}
}
- if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
- summary = getTextFromUri(context,
- metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI), providerMap,
- META_DATA_PREFERENCE_SUMMARY);
- }
- if (TextUtils.isEmpty(summary)
- && metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
+ if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
} else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index fd2e7ca..6764a6b 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -29,7 +29,8 @@
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
-import android.provider.Settings;
+import android.util.TypedValue;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
@@ -97,9 +98,13 @@
final int N = levels.length();
mColors = new int[2 * N];
- for (int i = 0; i < N; i++) {
+ for (int i=0; i < N; i++) {
mColors[2 * i] = levels.getInt(i, 0);
- mColors[2 * i + 1] = colors.getColor(i, 0);
+ if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
+ mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getResourceId(i, 0));
+ } else {
+ mColors[2 * i + 1] = colors.getColor(i, 0);
+ }
}
levels.recycle();
colors.recycle();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 2dcbf90..26ca3d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -265,6 +265,9 @@
public String toString() {
StringBuilder builder = new StringBuilder().append("AccessPoint(")
.append(ssid);
+ if (bssid != null) {
+ builder.append(":").append(bssid);
+ }
if (isSaved()) {
builder.append(',').append("saved");
}
@@ -280,6 +283,7 @@
if (security != SECURITY_NONE) {
builder.append(',').append(securityToString(security, pskType));
}
+ builder.append(",level=").append(getLevel());
builder.append(",rankingScore=").append(mRankingScore);
builder.append(",badge=").append(mBadge);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 11bcdca..8421c2c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -271,7 +271,6 @@
if (mWifiManager.isWifiEnabled()) {
mScanner.resume();
}
- mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
}
/**
@@ -715,9 +714,9 @@
mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED);
- mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
.sendToTarget();
+ mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
}
}
};
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 2018c13..e100884 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.wifi;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -28,6 +30,7 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkBadging;
+import android.net.NetworkInfo;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
import android.net.ScoredNetwork;
@@ -35,6 +38,7 @@
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
@@ -68,6 +72,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+// TODO(sghuman): Change these to robolectric tests b/35766684.
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WifiTrackerTest {
@@ -115,9 +121,10 @@
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+
mWorkerThread = new HandlerThread("TestHandlerWorkerThread");
mWorkerThread.start();
mLooper = mWorkerThread.getLooper();
@@ -221,9 +228,15 @@
SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
}
- private WifiTracker createTrackerAndInjectInitialScanResults() throws InterruptedException {
+ private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
+ Intent ... intents)
+ throws InterruptedException {
WifiTracker tracker = createMockedWifiTracker();
+
startTracking(tracker);
+ for (Intent intent : intents) {
+ tracker.mReceiver.onReceive(mContext, intent);
+ }
mAccessPointsChangedLatch = new CountDownLatch(1);
sendScanResultsAndProcess(tracker);
@@ -235,16 +248,16 @@
private WifiTracker createMockedWifiTracker() {
WifiTracker tracker =
new WifiTracker(
- mContext,
- mockWifiListener,
- mLooper,
- true,
- true,
- true,
- mockWifiManager,
- mockConnectivityManager,
- mockNetworkScoreManager,
- mMainLooper
+ mContext,
+ mockWifiListener,
+ mLooper,
+ true,
+ true,
+ true,
+ mockWifiManager,
+ mockConnectivityManager,
+ mockNetworkScoreManager,
+ mMainLooper
);
return tracker;
@@ -294,6 +307,31 @@
scoreCache.updateScores(Arrays.asList(sc1, sc2));
}
+ private WifiTracker createTrackerWithScanResultsAndAccessPoint1Connected()
+ throws InterruptedException {
+ int networkId = 123;
+
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(SSID_1));
+ wifiInfo.setBSSID(BSSID_1);
+ wifiInfo.setNetworkId(networkId);
+ when(mockWifiManager.getConnectionInfo()).thenReturn(wifiInfo);
+
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = SSID_1;
+ configuration.BSSID = BSSID_1;
+ configuration.networkId = networkId;
+ when(mockWifiManager.getConfiguredNetworks()).thenReturn(Arrays.asList(configuration));
+
+ NetworkInfo networkInfo = new NetworkInfo(
+ ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test");
+
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
+ return createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(intent);
+ }
+
@Test
public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
ScanResult scanResult = new ScanResult();
@@ -357,12 +395,21 @@
}
@Test
+ public void startTrackingShouldSetConnectedAccessPointAsActive() throws InterruptedException {
+ WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
+
+ List<AccessPoint> aps = tracker.getAccessPoints();
+
+ assertThat(aps).hasSize(2);
+ assertThat(aps.get(0).isActive()).isTrue();
+ }
+
+ @Test
public void startTrackingShouldRequestScoresForCurrentAccessPoints() throws InterruptedException {
// Start the tracker and inject the initial scan results and then stop tracking
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
tracker.stopTracking();
- android.util.Log.d("WifiTrackerTest", "Clearing previously captured requested keys");
mRequestedKeys.clear();
mRequestScoresLatch = new CountDownLatch(1);
@@ -370,7 +417,6 @@
assertTrue("Latch timed out",
mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
- android.util.Log.d("WifiTrackerTest", "requested keys: " + mRequestedKeys);
assertTrue(mRequestedKeys.contains(NETWORK_KEY_1));
assertTrue(mRequestedKeys.contains(NETWORK_KEY_2));
}
@@ -394,7 +440,7 @@
@Test
public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
List<AccessPoint> aps = tracker.getAccessPoints();
assertTrue(aps.size() == 2);
assertEquals(aps.get(0).getSsidStr(), SSID_1);
@@ -416,7 +462,7 @@
Settings.Global.NETWORK_SCORING_UI_ENABLED,
0 /* disabled */);
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
List<AccessPoint> aps = tracker.getAccessPoints();
assertTrue(aps.size() == 2);
assertEquals(aps.get(0).getSsidStr(), SSID_1);
@@ -432,7 +478,7 @@
@Test
public void scoreCacheUpdateScoresShouldInsertBadgeIntoAccessPoint() throws InterruptedException {
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
updateScoresAndWaitForAccessPointsChangedCallback();
List<AccessPoint> aps = tracker.getAccessPoints();
@@ -454,7 +500,7 @@
Settings.Global.NETWORK_SCORING_UI_ENABLED,
0 /* disabled */);
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
updateScoresAndWaitForAccessPointsChangedCallback();
List<AccessPoint> aps = tracker.getAccessPoints();
@@ -473,7 +519,7 @@
// Scores can be requested together or serially depending on how the scan results are
// processed.
mRequestScoresLatch = new CountDownLatch(2);
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
mRequestedKeys.clear();
@@ -503,7 +549,7 @@
@Test
public void scoreCacheAndListenerShouldBeUnregisteredWhenStopTrackingIsCalled() throws Exception
{
- WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+ WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue();
tracker.stopTracking();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 2958740..c506358 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -16,6 +16,17 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -25,18 +36,13 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import java.util.Arrays;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class RestrictedLockUtilsTest {
@@ -47,8 +53,11 @@
private DevicePolicyManager mDevicePolicyManager;
@Mock
private UserManager mUserManager;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private RestrictedLockUtils.Proxy mProxy;
private static final int mUserId = 194;
+ private static final int mProfileId = 160;
private static final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
private static final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
@@ -60,12 +69,13 @@
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
+
+ RestrictedLockUtils.sProxy = mProxy;
}
@Test
public void checkIfKeyguardFeaturesDisabled_noEnforcedAdminForManagedProfile() {
- setUpManagedProfile(mUserId);
- setUpActiveAdmins(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
+ setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
@@ -75,8 +85,7 @@
@Test
public void checkIfKeyguardFeaturesDisabled_oneEnforcedAdminForManagedProfile() {
- setUpManagedProfile(mUserId);
- setUpActiveAdmins(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
+ setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
@@ -89,8 +98,7 @@
@Test
public void checkIfKeyguardFeaturesDisabled_multipleEnforcedAdminForManagedProfile() {
- setUpManagedProfile(mUserId);
- setUpActiveAdmins(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
+ setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_REMOTE_INPUT);
@@ -103,9 +111,129 @@
assertThat(enforcedAdmin).isEqualTo(EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN);
}
- private UserInfo setUpManagedProfile(int userId) {
- final UserInfo userInfo = new UserInfo(userId, "myuser", UserInfo.FLAG_MANAGED_PROFILE);
+ @Test
+ public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
+ UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
+ UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
+ userInfo, profileInfo}));
+
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
+ .thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
+
+ // Querying the parent should return the policy, since it affects the parent.
+ EnforcedAdmin parent = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
+ assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId));
+
+ // Querying the child should return that too.
+ EnforcedAdmin profile = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
+ assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId));
+
+ // Querying for some unrelated feature should return nothing. Nothing!
+ assertThat(RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_REMOTE_INPUT, mUserId)).isNull();
+ assertThat(RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_REMOTE_INPUT, mProfileId)).isNull();
+ }
+
+ @Test
+ public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
+ UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
+ UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
+ userInfo, profileInfo}));
+
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ // Querying the parent should not return the policy, because it's not a policy that should
+ // affect parents even when the lock screen is unified.
+ EnforcedAdmin primary = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, mUserId);
+ assertThat(primary).isNull();
+
+ // Querying the child should still return the policy.
+ EnforcedAdmin profile = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, mProfileId);
+ assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId));
+ }
+
+ @Test
+ public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
+ UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
+ UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
+ userInfo, profileInfo}));
+
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
+ .thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
+
+ // Crucially for this test, isSeparateWorkChallengeEnabled => true.
+ doReturn(true).when(mProxy).isSeparateProfileChallengeEnabled(any(), eq(mProfileId));
+
+ // Querying the parent should not return the policy, even though it's shared by default,
+ // because the parent doesn't share a lock screen with the profile any more.
+ EnforcedAdmin parent = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
+ assertThat(parent).isNull();
+
+ // Querying the child should still return the policy.
+ EnforcedAdmin profile = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
+ assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId));
+ }
+
+ /**
+ * This test works great. The real world implementation is sketchy though.
+ * <p>
+ * DevicePolicyManager.getParentProfileInstance(UserInfo) does not do what it looks like it does
+ * (which would be to get an instance for the parent of the user that's passed in to it.)
+ * <p>
+ * Instead it just always returns a parent instance for the current user.
+ * <p>
+ * Still, the test works.
+ */
+ @Test
+ public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
+ UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
+ UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
+ userInfo, profileInfo}));
+
+ when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
+ .getKeyguardDisabledFeatures(mAdmin2, mProfileId))
+ .thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
+
+ // Parent should get the policy.
+ EnforcedAdmin parent = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
+ assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, mProfileId));
+
+ // Profile should not get the policy.
+ EnforcedAdmin profile = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
+ assertThat(profile).isNull();
+ }
+
+ private UserInfo setUpUser(int userId, ComponentName[] admins) {
+ UserInfo userInfo = new UserInfo(userId, "primary", 0);
when(mUserManager.getUserInfo(userId)).thenReturn(userInfo);
+ setUpActiveAdmins(userId, admins);
+ return userInfo;
+ }
+
+ private UserInfo setUpManagedProfile(int userId, ComponentName[] admins) {
+ UserInfo userInfo = new UserInfo(userId, "profile", UserInfo.FLAG_MANAGED_PROFILE);
+ when(mUserManager.getUserInfo(userId)).thenReturn(userInfo);
+ setUpActiveAdmins(userId, admins);
return userInfo;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 1683901..2d3c4a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -282,26 +282,11 @@
when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
.thenReturn(info);
- Bundle bundle = new Bundle();
- bundle.putInt("com.android.settings.icon", 161803);
- bundle.putString("com.android.settings.icon_package", "abc");
- bundle.putString("com.android.settings.summary", "dynamic-summary");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_ICON))), eq(URI_GET_ICON), any()))
- .thenReturn(bundle);
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
- when(mContentResolver.acquireProvider(anyString())).thenReturn(mIContentProvider);
- when(mContentResolver.acquireProvider(any(Uri.class))).thenReturn(mIContentProvider);
-
TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */,
false /* checkCategory */);
assertThat(outTiles.size()).isEqualTo(1);
- assertThat(outTiles.get(0).icon.getResId()).isEqualTo(161803);
- assertThat(outTiles.get(0).summary).isEqualTo("dynamic-summary");
}
public static ResolveInfo newInfo(boolean systemApp, String category) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 34164b16..5b4dd48 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -118,6 +118,8 @@
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<!-- Permission needed to hold a wakelock in dumpstate.cpp (drop_root_user()) -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <!-- Permission needed to enable/disable overlays -->
+ <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
index 11e83a1..a7fb1a1 100644
--- a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
+++ b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml
@@ -19,12 +19,12 @@
android:viewportWidth="32.0"
android:viewportHeight="32.0">
<path
- android:fillColor="@color/system_warning_color"
+ android:fillColor="?android:attr/colorError"
android:pathData="M15.99,2.5C8.53,2.5 2.5,8.54 2.5,16.0s6.03,13.5 13.49,13.5S29.5,23.46 29.5,16.0S23.45,2.5 15.99,2.5zM16.0,26.8c-5.97,0.0 -10.8,-4.83 -10.8,-10.8S10.03,5.2 16.0,5.2S26.8,10.03 26.8,16.0S21.97,26.8 16.0,26.8z"/>
<path
- android:fillColor="@color/system_warning_color"
+ android:fillColor="?android:attr/colorError"
android:pathData="M14.65,20.05l2.7,0.0l0.0,2.7l-2.7,0.0z"/>
<path
- android:fillColor="@color/system_warning_color"
+ android:fillColor="?android:attr/colorError"
android:pathData="M14.65,9.25l2.7,0.0l0.0,8.1l-2.7,0.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
index e207cb3..3af2f7f 100644
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
+++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml
@@ -98,11 +98,11 @@
<path
android:name="path_2"
android:pathData="M 1.35900878906,6.76104736328 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-2.69995117188 0.0,-2.69995117188 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,2.69995117188 0.0,2.69995117188 Z"
- android:fillColor="@*android:color/system_error" />
+ android:fillColor="?android:attr/colorError" />
<path
android:name="path_1"
android:pathData="M 1.35363769531,1.36633300781 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-8.09997558594 0.0,-8.09997558594 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,8.09997558594 0.0,8.09997558594 Z"
- android:fillColor="@*android:color/system_error" />
+ android:fillColor="?android:attr/colorError" />
</group>
</group>
<group
@@ -117,7 +117,7 @@
<path
android:name="path_3"
android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="2"
android:trimPathStart="0"
android:trimPathEnd="1" />
diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
index 2b4babc..a577afc 100644
--- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
+++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml
@@ -96,7 +96,7 @@
<path
android:name="ridge_5_path_0"
android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -106,7 +106,7 @@
<path
android:name="ridge_7_path_0"
android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -116,7 +116,7 @@
<path
android:name="ridge_6_path_0"
android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -126,7 +126,7 @@
<path
android:name="ridge_2_path_0"
android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathStart="1" />
@@ -138,7 +138,7 @@
<path
android:name="ridge_1_path_0"
android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="1.45"
android:strokeLineCap="round"
android:trimPathEnd="0" />
@@ -157,11 +157,11 @@
<path
android:name="path_2"
android:pathData="M -1.3705291748,4.06730651855 c 0.0,0.0 0.036376953125,-0.0102386474609 0.036376953125,-0.0102386474609 c 0.0,0.0 -0.00682067871094,0.0040283203125 -0.00682067871093,0.0040283203125 c 0.0,0.0 -0.0161437988281,0.00479125976562 -0.0161437988281,0.00479125976563 c 0.0,0.0 -0.0134124755859,0.00141906738281 -0.0134124755859,0.00141906738281 Z"
- android:fillColor="@*android:color/system_error" />
+ android:fillColor="?android:attr/colorError" />
<path
android:name="path_1"
android:pathData="M -1.3737487793,-6.77532958984 c 0.0,0.0 0.00604248046875,0.0166625976562 0.00604248046876,0.0166625976562 c 0.0,0.0 0.0213623046875,0.0250244140625 0.0213623046875,0.0250244140625 c 0.0,0.0 -0.00604248046875,-0.0166625976562 -0.00604248046876,-0.0166625976562 c 0.0,0.0 -0.0213623046875,-0.0250244140625 -0.0213623046875,-0.0250244140625 Z"
- android:fillColor="@*android:color/system_error" />
+ android:fillColor="?android:attr/colorError" />
</group>
</group>
<group
@@ -176,7 +176,7 @@
<path
android:name="path_3"
android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z"
- android:strokeColor="@*android:color/system_error"
+ android:strokeColor="?android:attr/colorError"
android:strokeWidth="2"
android:trimPathStart="1" />
</group>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_in.xml b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
new file mode 100644
index 0000000..7e6e09b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
new file mode 100644
index 0000000..b7b6f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_out.xml b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
new file mode 100644
index 0000000..910c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
new file mode 100644
index 0000000..666127b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
new file mode 100644
index 0000000..eeba030
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
new file mode 100644
index 0000000..d577d1a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 8b10074..d988acc 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -54,4 +54,10 @@
android:src="@drawable/stat_sys_roaming"
android:contentDescription="@string/accessibility_data_connection_roaming"
android:visibility="gone" />
+ <ImageView
+ android:id="@+id/mobile_inout"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:visibility="gone"
+ />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index 222a31d..4d03f0f 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -70,6 +70,11 @@
android:layout_width="wrap_content"
android:alpha="0.0"
/>
+ <ImageView
+ android:id="@+id/wifi_inout"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
</FrameLayout>
<View
android:id="@+id/wifi_signal_spacer"
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
new file mode 100644
index 0000000..e52c5db
--- /dev/null
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2017, 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.
+ */
+-->
+<resources>
+ <!-- List of package names or class names which are considered as Settings,
+ so PIP location should be adjusted to the left of the side panel.
+ If it should be applied for all activities in a package, add the package name.
+ If it should be applied for an activity in a package, add its class name with package name.
+ The class name must follow format 'package_name/.class_name' ('/.' in between).
+ This can be overriden in an overlay.
+ -->
+ <string-array name="tv_pip_settings_class_name" translatable="false">
+ <item>com.android.tv.settings</item>
+ <item>com.google.android.leanbacklauncher/.settings.HomeScreenSettingsActivity</item>
+ <item>com.google.android.apps.mediashell/.settings.CastSettingsActivity</item>
+ <item>com.google.android.katniss.setting/.SpeechSettingsActivity</item>
+ <item>com.google.android.katniss.setting/.SearchSettingsActivity</item>
+ <item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item>
+ </string-array>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 407cddf..f461bb3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -27,7 +27,6 @@
<color name="notification_list_shadow_top">#80000000</color>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<color name="qs_batterymeter_frame_color">#FF404040</color>
- <color name="system_warning_color">@*android:color/system_error</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
<color name="qs_detail_button">@*android:color/quaternary_device_default_settings</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 48b7664..9168256 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -30,7 +30,7 @@
<!-- Recents theme -->
<style name="RecentsTheme.Wallpaper">
- <item name="android:windowBackground">@color/transparent</item>
+ <item name="android:windowBackground">@*android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowDisablePreview">true</item>
@@ -52,7 +52,7 @@
<!-- Theme used for the activity that shows when the system forced an app to be resizable -->
<style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowBackground">@drawable/forced_resizable_background</item>
- <item name="android:statusBarColor">@color/transparent</item>
+ <item name="android:statusBarColor">@*android:color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
</style>
@@ -70,7 +70,7 @@
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@null</item>
<item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:statusBarColor">@color/transparent</item>
+ <item name="android:statusBarColor">@*android:color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
</style>
@@ -164,7 +164,7 @@
<style name="TextAppearance.QS.Warning">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/system_warning_color</item>
+ <item name="android:textColor">?android:attr/colorError</item>
</style>
<style name="TextAppearance.QS.DetailButton">
@@ -366,7 +366,7 @@
</style>
<style name="TextAppearance.NotificationGuts.SecondaryWarning">
- <item name="android:textColor">@color/system_warning_color</item>
+ <item name="android:textColor">?android:attr/colorError</item>
<item name="android:textSize">12sp</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index f1e7d53..ac7ab9d 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,8 @@
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -233,6 +235,9 @@
mProviders.put(FragmentService.class, () ->
new FragmentService(mContext));
+ mProviders.put(ExtensionController.class, () ->
+ new ExtensionControllerImpl());
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 376a0b6..20866c0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -68,34 +68,10 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean DEBUG_FORCE_ONBOARDING =
SystemProperties.getBoolean("debug.tv.pip_force_onboarding", false);
+ private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
private static PipManager sPipManager;
-
- /**
- * List of package and class name which are considered as Settings,
- * so PIP location should be adjusted to the left of the side panel.
- */
- private static final List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
- static {
- sSettingsPackageAndClassNamePairList = new ArrayList<>();
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.android.tv.settings", null));
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.google.android.leanbacklauncher",
- "com.google.android.leanbacklauncher.settings.HomeScreenSettingsActivity"));
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.google.android.apps.mediashell",
- "com.google.android.apps.mediashell.settings.CastSettingsActivity"));
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.google.android.katniss",
- "com.google.android.katniss.setting.SpeechSettingsActivity"));
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.google.android.katniss",
- "com.google.android.katniss.setting.SearchSettingsActivity"));
- sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
- "com.google.android.gsf.notouch",
- "com.google.android.gsf.notouch.UsageDiagnosticsSettingActivity"));
- }
+ private static List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
/**
* State when there's no PIP.
@@ -252,6 +228,36 @@
mOnboardingShown = Prefs.getBoolean(
mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
+ if (sSettingsPackageAndClassNamePairList == null) {
+ String[] settings = mContext.getResources().getStringArray(
+ R.array.tv_pip_settings_class_name);
+ sSettingsPackageAndClassNamePairList = new ArrayList<>();
+ if (settings != null) {
+ for (int i = 0; i < settings.length; i++) {
+ Pair<String, String> entry = null;
+ String[] packageAndClassName =
+ settings[i].split(SETTINGS_PACKAGE_AND_CLASS_DELIMITER);
+ switch (packageAndClassName.length) {
+ case 1:
+ entry = Pair.<String, String>create(packageAndClassName[0], null);
+ break;
+ case 2:
+ if (packageAndClassName[1] != null
+ && packageAndClassName[1].startsWith(".")) {
+ entry = Pair.<String, String>create(
+ packageAndClassName[0],
+ packageAndClassName[0] + packageAndClassName[1]);
+ }
+ }
+ if (entry != null) {
+ sSettingsPackageAndClassNamePairList.add(entry);
+ } else {
+ Log.w(TAG, "Ignoring malformed settings name " + settings[i]);
+ }
+ }
+ }
+ }
+
loadConfigurationsAndApply();
mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
mMediaSessionManager =
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
index 8b4bd7b..0c3e40c 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
@@ -133,14 +133,7 @@
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple) {
- ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
- if (info == null) {
- throw new RuntimeException(cls + " doesn't provide an interface");
- }
- if (TextUtils.isEmpty(info.action())) {
- throw new RuntimeException(cls + " doesn't provide an action");
- }
- addPluginListener(info.action(), listener, cls, allowMultiple);
+ addPluginListener(getAction(cls), listener, cls, allowMultiple);
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
@@ -285,6 +278,17 @@
return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
}
+ public static <P> String getAction(Class<P> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return info.action();
+ }
+
private class AllPluginClassLoader extends ClassLoader {
public AllPluginClassLoader(ClassLoader classLoader) {
super(classLoader);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 09ce2ad..daf0622 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -39,6 +39,7 @@
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -175,8 +176,7 @@
.setOnlyAlertOnce(true)
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
.setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(mContext.getColor(
- com.android.internal.R.color.battery_saver_mode_color));
+ .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
}
@@ -245,8 +245,7 @@
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
- .setColor(mContext.getColor(
- com.android.internal.R.color.battery_saver_mode_color));
+ .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
SystemUI.overrideNotificationAppName(mContext, nb);
final Notification n = nb.build();
mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
index c7b6aea..9cd79f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
@@ -91,7 +91,7 @@
formatBytes(info.usageLevel));
bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit,
formatBytes(info.limitLevel));
- usageColor = mContext.getColor(R.color.system_warning_color);
+ usageColor = Utils.getDefaultColor(mContext, android.R.attr.colorError);
}
if (usageColor == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index bc992d8..fb92a67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -42,6 +42,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -365,7 +366,7 @@
if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
return;
}
- int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ int errorColor = Utils.getColorError(mContext);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
} else if (updateMonitor.isDeviceInteractive()
@@ -388,7 +389,7 @@
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
return;
}
- int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ int errorColor = Utils.getColorError(mContext);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
// When swiping up right after receiving a fingerprint error, the bouncer calls
// authenticate leading to the same message being shown again on the bouncer.
@@ -411,8 +412,7 @@
@Override
public void onScreenTurnedOn() {
if (mMessageToShowOnScreenOn != null) {
- int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
- null);
+ int errorColor = Utils.getColorError(mContext);
showTransientIndication(mMessageToShowOnScreenOn, errorColor);
// We want to keep this message around in case the screen was off
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 89041f9..67be99e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -83,6 +83,8 @@
private int mWifiStrengthId = 0;
private int mLastWifiBadgeId = -1;
private int mLastWifiStrengthId = -1;
+ private int mWifiActivityId = 0;
+ private int mLastWifiActivityId = -1;
private boolean mIsAirplaneMode = false;
private int mAirplaneIconId = 0;
private int mLastAirplaneIconId = -1;
@@ -97,6 +99,7 @@
ViewGroup mEthernetGroup, mWifiGroup;
View mNoSimsCombo;
ImageView mVpn, mEthernet, mWifi, mAirplane, mNoSims, mEthernetDark, mWifiDark, mNoSimsDark;
+ ImageView mWifiActivity;
View mWifiAirplaneSpacer;
View mWifiSignalSpacer;
LinearLayout mMobileSignalGroup;
@@ -177,6 +180,7 @@
mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo);
mWifi = (ImageView) findViewById(R.id.wifi_signal);
mWifiDark = (ImageView) findViewById(R.id.wifi_signal_dark);
+ mWifiActivity = (ImageView) findViewById(R.id.wifi_inout);
mAirplane = (ImageView) findViewById(R.id.airplane);
mNoSims = (ImageView) findViewById(R.id.no_sims);
mNoSimsDark = (ImageView) findViewById(R.id.no_sims_dark);
@@ -267,6 +271,10 @@
mWifiStrengthId = statusIcon.icon;
mWifiBadgeId = statusIcon.iconOverlay;
mWifiDescription = statusIcon.contentDescription;
+ mWifiActivityId = activityIn && activityOut ? R.drawable.stat_sys_wifi_inout
+ : activityIn ? R.drawable.stat_sys_wifi_in
+ : activityOut ? R.drawable.stat_sys_wifi_out
+ : 0;
apply();
}
@@ -286,6 +294,10 @@
state.mMobileTypeDescription = typeContentDescription;
state.mIsMobileTypeIconWide = statusType != 0 && isWide;
state.mRoaming = roaming;
+ state.mMobileActivityId = activityIn && activityOut ? R.drawable.stat_sys_signal_inout
+ : activityIn ? R.drawable.stat_sys_signal_in
+ : activityOut ? R.drawable.stat_sys_signal_out
+ : 0;
apply();
}
@@ -409,6 +421,10 @@
mLastWifiStrengthId = -1;
mLastWifiBadgeId = -1;
}
+ if (mWifiActivity != null) {
+ mWifiActivity.setImageDrawable(null);
+ mLastWifiActivityId = -1;
+ }
for (PhoneState state : mPhoneStates) {
if (state.mMobile != null) {
@@ -425,6 +441,10 @@
state.mMobileType.setImageDrawable(null);
state.mLastMobileTypeId = -1;
}
+ if (state.mMobileActivity != null) {
+ state.mMobileActivity.setImageDrawable(null);
+ state.mLastMobileActivityId = -1;
+ }
}
if (mAirplane != null) {
@@ -484,6 +504,12 @@
mLastWifiStrengthId = mWifiStrengthId;
mLastWifiBadgeId = mWifiBadgeId;
}
+ if (mWifiActivityId != mLastWifiActivityId) {
+ if (mWifiActivityId != 0) {
+ setIconForView(mWifiActivity, mWifiActivityId);
+ }
+ mLastWifiActivityId = mWifiActivityId;
+ }
mWifiGroup.setContentDescription(mWifiDescription);
mWifiGroup.setVisibility(View.VISIBLE);
} else {
@@ -495,6 +521,8 @@
(mWifiVisible ? "VISIBLE" : "GONE"),
mWifiStrengthId));
+ mWifiActivity.setVisibility(mWifiActivityId != 0 ? View.VISIBLE : View.GONE);
+
boolean anyMobileVisible = false;
int firstMobileTypeId = 0;
for (PhoneState state : mPhoneStates) {
@@ -603,6 +631,8 @@
applyDarkIntensity(
DarkIconDispatcher.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
mWifi, mWifiDark);
+ setTint(mWifiActivity,
+ DarkIconDispatcher.getTint(mTintArea, mWifiActivity, mIconTint));
applyDarkIntensity(
DarkIconDispatcher.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
mEthernet, mEthernetDark);
@@ -627,15 +657,17 @@
private class PhoneState {
private final int mSubId;
private boolean mMobileVisible = false;
- private int mMobileStrengthId = 0, mMobileTypeId = 0;
+ private int mMobileStrengthId = 0, mMobileTypeId = 0, mMobileActivityId = 0;
private int mLastMobileStrengthId = -1;
private int mLastMobileTypeId = -1;
+ private int mLastMobileActivityId = -1;
private boolean mIsMobileTypeIconWide;
private String mMobileDescription, mMobileTypeDescription;
private ViewGroup mMobileGroup;
private ImageView mMobile, mMobileDark, mMobileType, mMobileRoaming;
public boolean mRoaming;
+ private ImageView mMobileActivity;
public PhoneState(int subId, Context context) {
ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -650,6 +682,7 @@
mMobileDark = (ImageView) root.findViewById(R.id.mobile_signal_dark);
mMobileType = (ImageView) root.findViewById(R.id.mobile_type);
mMobileRoaming = (ImageView) root.findViewById(R.id.mobile_roaming);
+ mMobileActivity = (ImageView) root.findViewById(R.id.mobile_inout);
}
public boolean apply(boolean isSecondaryIcon) {
@@ -664,6 +697,11 @@
mMobileType.setImageResource(mMobileTypeId);
mLastMobileTypeId = mMobileTypeId;
}
+
+ if (mLastMobileActivityId != mMobileActivityId) {
+ mMobileActivity.setImageResource(mMobileActivityId);
+ mLastMobileActivityId = mMobileActivityId;
+ }
mMobileGroup.setContentDescription(mMobileTypeDescription
+ " " + mMobileDescription);
mMobileGroup.setVisibility(View.VISIBLE);
@@ -686,6 +724,7 @@
mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
+ mMobileActivity.setVisibility(mMobileActivityId != 0 ? View.VISIBLE : View.GONE);
return mMobileVisible;
}
@@ -747,6 +786,8 @@
setTint(mMobileType, DarkIconDispatcher.getTint(tintArea, mMobileType, tint));
setTint(mMobileRoaming, DarkIconDispatcher.getTint(tintArea, mMobileRoaming,
tint));
+ setTint(mMobileActivity,
+ DarkIconDispatcher.getTint(tintArea, mMobileActivity, tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index ed71e57..f3c2bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -33,6 +33,7 @@
import android.util.Log;
import android.view.View;
+import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -167,7 +168,7 @@
mSemiTransparent = context.getColor(
com.android.internal.R.color.system_bar_background_semi_transparent);
mTransparent = context.getColor(R.color.system_bar_background_transparent);
- mWarning = context.getColor(com.android.internal.R.color.battery_saver_mode_color);
+ mWarning = Utils.getColorAttr(context, android.R.attr.colorError);
}
mGradient = context.getDrawable(gradientResourceId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 8f63d45..2538bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,9 +19,10 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
-import static com.android.systemui.tuner.LockscreenFragment.getIntentButton;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -79,9 +80,12 @@
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.tuner.LockscreenFragment;
+import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -91,8 +95,7 @@
*/
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
UnlockMethodCache.OnUnlockMethodChangedListener,
- AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener,
- Tunable {
+ AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
final static String TAG = "StatusBar/KeyguardBottomAreaView";
@@ -159,12 +162,10 @@
private Drawable mLeftAssistIcon;
private IntentButton mRightButton = new DefaultRightButton();
- private IntentButton mRightDefault = mRightButton;
- private IntentButton mRightPlugin;
+ private Extension<IntentButton> mRightExtension;
private String mRightButtonStr;
private IntentButton mLeftButton = new DefaultLeftButton();
- private IntentButton mLeftDefault = mLeftButton;
- private IntentButton mLeftPlugin;
+ private Extension<IntentButton> mLeftExtension;
private String mLeftButtonStr;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mDozing;
@@ -261,21 +262,28 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAccessibilityController.addStateChangedCallback(this);
- Dependency.get(PluginManager.class).addPluginListener(RIGHT_BUTTON_PLUGIN,
- mRightListener, IntentButtonProvider.class, false /* Only allow one */);
- Dependency.get(PluginManager.class).addPluginListener(LEFT_BUTTON_PLUGIN,
- mLeftListener, IntentButtonProvider.class, false /* Only allow one */);
- Dependency.get(TunerService.class).addTunable(this, LockscreenFragment.LOCKSCREEN_LEFT_BUTTON,
- LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON);
+ mRightExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
+ .withPlugin(IntentButtonProvider.class, RIGHT_BUTTON_PLUGIN,
+ p -> p.getIntentButton())
+ .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_RIGHT_BUTTON))
+ .withDefault(() -> new DefaultRightButton())
+ .withCallback(button -> setRightButton(button))
+ .build();
+ mLeftExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
+ .withPlugin(IntentButtonProvider.class, LEFT_BUTTON_PLUGIN,
+ p -> p.getIntentButton())
+ .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_LEFT_BUTTON))
+ .withDefault(() -> new DefaultLeftButton())
+ .withCallback(button -> setLeftButton(button))
+ .build();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mAccessibilityController.removeStateChangedCallback(this);
- Dependency.get(PluginManager.class).removePluginListener(mRightListener);
- Dependency.get(PluginManager.class).removePluginListener(mLeftListener);
- Dependency.get(TunerService.class).removeTunable(this);
+ mRightExtension.destroy();
+ mLeftExtension.destroy();
}
private void initAccessibility() {
@@ -790,63 +798,21 @@
inflateCameraPreview();
}
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (LockscreenFragment.LOCKSCREEN_LEFT_BUTTON.equals(key)) {
- mLeftButtonStr = newValue;
- mLeftIsVoiceAssist = TextUtils.isEmpty(mLeftButtonStr) && mLeftPlugin == null;
- mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
- updateLeftAffordance();
- } else {
- mRightButtonStr = newValue;
- mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
- updateRightAffordanceIcon();
- updateCameraVisibility();
- inflateCameraPreview();
- }
- }
-
private void setRightButton(IntentButton button) {
- mRightPlugin = button;
- mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
+ mRightButton = button;
updateRightAffordanceIcon();
updateCameraVisibility();
inflateCameraPreview();
}
private void setLeftButton(IntentButton button) {
- mLeftPlugin = button;
- mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
- mLeftIsVoiceAssist = false;
+ mLeftButton = button;
+ if (!(mLeftButton instanceof DefaultLeftButton)) {
+ mLeftIsVoiceAssist = false;
+ }
updateLeftAffordance();
}
- private final PluginListener<IntentButtonProvider> mRightListener =
- new PluginListener<IntentButtonProvider>() {
- @Override
- public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) {
- setRightButton(plugin.getIntentButton());
- }
-
- @Override
- public void onPluginDisconnected(IntentButtonProvider plugin) {
- setRightButton(null);
- }
- };
-
- private final PluginListener<IntentButtonProvider> mLeftListener =
- new PluginListener<IntentButtonProvider>() {
- @Override
- public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) {
- setLeftButton(plugin.getIntentButton());
- }
-
- @Override
- public void onPluginDisconnected(IntentButtonProvider plugin) {
- setLeftButton(null);
- }
- };
-
public void setDozing(boolean dozing, boolean animate) {
mDozing = dozing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 0e7b2f3..c070869 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -29,6 +29,7 @@
import android.os.UserManager;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import android.telephony.SubscriptionInfo;
import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.View;
@@ -64,6 +65,8 @@
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import com.android.systemui.tuner.TunerService;
+import java.util.List;
+
public class QuickStatusBarHeader extends BaseStatusBarHeader implements
NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback {
@@ -440,10 +443,20 @@
}
}
+ @Override
+ public void setSubs(List<SubscriptionInfo> subs) {
+ mRoamingsBySubId.clear();
+ updateRoaming();
+ }
+
public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {
mRoamingsBySubId.put(subId, roaming);
+ updateRoaming();
+ }
+
+ private void updateRoaming() {
boolean isRoaming = calculateRoaming();
if (mIsRoaming != isRoaming) {
mIsRoaming = isRoaming;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 213b0aa..a6b145e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5742,9 +5742,8 @@
if (snoozeOption.criterion != null) {
mNotificationListener.snoozeNotification(sbn.getKey(), snoozeOption.criterion.getId());
} else {
- GregorianCalendar snoozeUntil = new GregorianCalendar();
- snoozeUntil.add(Calendar.MINUTE, snoozeOption.snoozeForMinutes);
- mNotificationListener.snoozeNotification(sbn.getKey(), snoozeUntil.getTimeInMillis());
+ mNotificationListener.snoozeNotification(sbn.getKey(),
+ snoozeOption.snoozeForMinutes * 60 * 1000);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
new file mode 100644
index 0000000..eaf8925
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.Plugin;
+
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Utility class used to select between a plugin, tuner settings, and a default implementation
+ * of an interface.
+ */
+public interface ExtensionController {
+
+ <T> ExtensionBuilder<T> newExtension(Class<T> cls);
+
+ interface Extension<T> {
+ T get();
+ void destroy();
+ }
+
+ interface ExtensionBuilder<T> {
+ ExtensionBuilder<T> withTunerFactory(TunerFactory<T> factory);
+ <P extends T> ExtensionBuilder<T> withPlugin(Class<P> cls);
+ <P extends T> ExtensionBuilder<T> withPlugin(Class<P> cls, String action);
+ <P> ExtensionBuilder<T> withPlugin(Class<P> cls, String action,
+ PluginConverter<T, P> converter);
+ ExtensionBuilder<T> withDefault(Supplier<T> def);
+ ExtensionBuilder<T> withCallback(Consumer<T> callback);
+ Extension build();
+ }
+
+ public interface PluginConverter<T, P> {
+ T getInterfaceFromPlugin(P plugin);
+ }
+
+ public interface TunerFactory<T> {
+ String[] keys();
+ T create(Map<String, String> settings);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
new file mode 100644
index 0000000..fefbaa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class ExtensionControllerImpl implements ExtensionController {
+
+ @Override
+ public <T> ExtensionBuilder<T> newExtension(Class<T> cls) {
+ return new ExtensionBuilder<>();
+ }
+
+ private interface Producer<T> {
+ T get();
+ void destroy();
+ }
+
+ private class ExtensionBuilder<T> implements ExtensionController.ExtensionBuilder<T> {
+
+ private ExtensionImpl<T> mExtension = new ExtensionImpl<>();
+
+ @Override
+ public ExtensionController.ExtensionBuilder<T> withTunerFactory(TunerFactory<T> factory) {
+ mExtension.addTunerFactory(factory, factory.keys());
+ return this;
+ }
+
+ @Override
+ public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
+ return withPlugin(cls, PluginManager.getAction(cls));
+ }
+
+ @Override
+ public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
+ String action) {
+ return withPlugin(cls, action, null);
+ }
+
+ @Override
+ public <P> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls,
+ String action, PluginConverter<T, P> converter) {
+ mExtension.addPlugin(action, cls, converter);
+ return this;
+ }
+
+ @Override
+ public ExtensionController.ExtensionBuilder<T> withDefault(Supplier<T> def) {
+ mExtension.addDefault(def);
+ return this;
+ }
+
+ @Override
+ public ExtensionController.ExtensionBuilder<T> withCallback(
+ Consumer<T> callback) {
+ mExtension.mCallbacks.add(callback);
+ return this;
+ }
+
+ @Override
+ public ExtensionController.Extension build() {
+ // Manually sort, plugins first, tuners second, defaults last.
+ Collections.sort(mExtension.mProducers, (o1, o2) -> {
+ if (o1 instanceof ExtensionImpl.PluginItem) {
+ if (o2 instanceof ExtensionImpl.PluginItem) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ if (o1 instanceof ExtensionImpl.TunerItem) {
+ if (o2 instanceof ExtensionImpl.PluginItem) {
+ return 1;
+ } else if (o2 instanceof ExtensionImpl.TunerItem) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+ });
+ mExtension.notifyChanged();
+ return mExtension;
+ }
+ }
+
+ private class ExtensionImpl<T> implements ExtensionController.Extension<T> {
+ private final ArrayList<Producer<T>> mProducers = new ArrayList<>();
+ private final ArrayList<Consumer<T>> mCallbacks = new ArrayList<>();
+ private T mItem;
+
+ @Override
+ public T get() {
+ return mItem;
+ }
+
+ @Override
+ public void destroy() {
+ for (int i = 0; i < mProducers.size(); i++) {
+ mProducers.get(i).destroy();
+ }
+ }
+
+ private void notifyChanged() {
+ for (int i = 0; i < mProducers.size(); i++) {
+ final T item = mProducers.get(i).get();
+ if (item != null) {
+ mItem = item;
+ break;
+ }
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).accept(mItem);
+ }
+ }
+
+ public void addDefault(Supplier<T> def) {
+ mProducers.add(new Default(def));
+ }
+
+ public <P> void addPlugin(String action, Class<P> cls, PluginConverter<T, P> converter) {
+ mProducers.add(new PluginItem(action, cls, converter));
+ }
+
+ public void addTunerFactory(TunerFactory<T> factory, String[] keys) {
+ mProducers.add(new TunerItem(factory, factory.keys()));
+ }
+
+ private class PluginItem<P extends Plugin> implements Producer<T>, PluginListener<P> {
+ private final PluginConverter<T, P> mConverter;
+ private T mItem;
+
+ public PluginItem(String action, Class<P> cls, PluginConverter<T, P> converter) {
+ mConverter = converter;
+ Dependency.get(PluginManager.class).addPluginListener(action, this, cls);
+ }
+
+ @Override
+ public void onPluginConnected(P plugin, Context pluginContext) {
+ if (mConverter != null) {
+ mItem = mConverter.getInterfaceFromPlugin(plugin);
+ } else {
+ mItem = (T) plugin;
+ }
+ notifyChanged();
+ }
+
+ @Override
+ public void onPluginDisconnected(P plugin) {
+ mItem = null;
+ notifyChanged();
+ }
+
+ @Override
+ public T get() {
+ return mItem;
+ }
+
+ @Override
+ public void destroy() {
+ Dependency.get(PluginManager.class).removePluginListener(this);
+ }
+ }
+
+ private class TunerItem<T> implements Producer<T>, Tunable {
+ private final TunerFactory<T> mFactory;
+ private final ArrayMap<String, String> mSettings = new ArrayMap<>();
+ private T mItem;
+
+ public TunerItem(TunerFactory<T> factory, String... setting) {
+ mFactory = factory;
+ Dependency.get(TunerService.class).addTunable(this, setting);
+ }
+
+ @Override
+ public T get() {
+ return mItem;
+ }
+
+ @Override
+ public void destroy() {
+ Dependency.get(TunerService.class).removeTunable(this);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ mSettings.put(key, newValue);
+ mItem = mFactory.create(mSettings);
+ notifyChanged();
+ }
+ }
+
+ private class Default<T> implements Producer<T> {
+ private final Supplier<T> mSupplier;
+
+ public Default(Supplier<T> supplier) {
+ mSupplier = supplier;
+ }
+
+ @Override
+ public T get() {
+ return mSupplier.get();
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index a85426c..51d931e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -717,6 +717,7 @@
mDemoMode = true;
mDemoInetCondition = mInetCondition;
mDemoWifiState = mWifiSignalController.getState();
+ mDemoWifiState.ssid = "DemoMode";
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
if (DEBUG) Log.d(TAG, "Exiting demo mode");
mDemoMode = false;
@@ -762,6 +763,25 @@
: Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
mDemoWifiState.connected = mDemoWifiState.level >= 0;
}
+ String activity = args.getString("activity");
+ if (activity != null) {
+ switch (activity) {
+ case "inout":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_INOUT);
+ break;
+ case "in":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_IN);
+ break;
+ case "out":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_OUT);
+ break;
+ default:
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ break;
+ }
+ } else {
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ }
mDemoWifiState.enabled = show;
mWifiSignalController.notifyListeners();
}
@@ -831,7 +851,23 @@
}
String activity = args.getString("activity");
if (activity != null) {
- controller.setActivity(Integer.parseInt(activity));
+ controller.getState().dataConnected = true;
+ switch (activity) {
+ case "inout":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+ break;
+ case "in":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN);
+ break;
+ case "out":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT);
+ break;
+ default:
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
+ break;
+ }
+ } else {
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
}
controller.getState().enabled = show;
controller.notifyListeners();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 0a962f1..7c98e13 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -163,7 +163,7 @@
intent.putExtra("sims", "1");
intent.putExtra("nosim", "false");
intent.putExtra("level", "4");
- intent.putExtra("datatypel", "");
+ intent.putExtra("datatype", "lte");
getContext().sendBroadcast(intent);
// Need to send this after so that the sim controller already exists.
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
index 410d3d2..2df1793 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
@@ -52,11 +52,13 @@
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
import com.android.systemui.statusbar.ScalingDrawableWrapper;
import com.android.systemui.statusbar.phone.ExpandableIndicator;
+import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
import com.android.systemui.tuner.ShortcutParser.Shortcut;
import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.function.Consumer;
public class LockscreenFragment extends PreferenceFragment {
@@ -313,26 +315,39 @@
}
}
- public static IntentButton getIntentButton(Context context, String buttonStr,
- IntentButton plugin, IntentButton def) {
- // Plugin wins.
- if (plugin != null) return plugin;
- // Then tuner options.
- if (!TextUtils.isEmpty(buttonStr)) {
- if (buttonStr.contains("::")) {
- Shortcut shortcut = getShortcutInfo(context, buttonStr);
- if (shortcut != null) {
- return new ShortcutButton(context, shortcut);
- }
- } else if (buttonStr.contains("/")) {
- ActivityInfo info = getActivityinfo(context, buttonStr);
- if (info != null) {
- return new ActivityButton(context, info);
+ public static class LockButtonFactory implements TunerFactory<IntentButton> {
+
+ private final String mKey;
+ private final Context mContext;
+
+ public LockButtonFactory(Context context, String key) {
+ mContext = context;
+ mKey = key;
+ }
+
+ @Override
+ public String[] keys() {
+ return new String[]{mKey};
+ }
+
+ @Override
+ public IntentButton create(Map<String, String> settings) {
+ String buttonStr = settings.get(mKey);
+ if (!TextUtils.isEmpty(buttonStr)) {
+ if (buttonStr.contains("::")) {
+ Shortcut shortcut = getShortcutInfo(mContext, buttonStr);
+ if (shortcut != null) {
+ return new ShortcutButton(mContext, shortcut);
+ }
+ } else if (buttonStr.contains("/")) {
+ ActivityInfo info = getActivityinfo(mContext, buttonStr);
+ if (info != null) {
+ return new ActivityButton(mContext, info);
+ }
}
}
+ return null;
}
- // Then default.
- return def;
}
private static class ShortcutButton implements IntentButton {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 6c454e1..c0e7e80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -23,7 +23,6 @@
import android.os.MessageQueue;
import android.support.test.InstrumentationRegistry;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.systemui.Dependency.DependencyKey;
import com.android.systemui.utils.TestableContext;
@@ -32,8 +31,6 @@
import org.junit.After;
import org.junit.Before;
-import java.lang.Thread.UncaughtExceptionHandler;
-
/**
* Base class that does System UI specific setup.
*/
@@ -92,8 +89,10 @@
return null;
}
- public <T> void injectMockDependency(Class<T> cls) {
- injectTestDependency(cls, mock(cls));
+ public <T> T injectMockDependency(Class<T> cls) {
+ final T mock = mock(cls);
+ mDependency.injectTestDependency(cls, mock);
+ return mock;
}
public <T> void injectTestDependency(Class<T> cls, T obj) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index 72f6ca8..b8be4fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith;
@RunWith(SysUIRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
public class NotificationMenuRowTest extends LeakCheckedTest {
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStatusBarHeaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStatusBarHeaderTest.java
new file mode 100644
index 0000000..99cecff
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStatusBarHeaderTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.R.layout;
+import com.android.systemui.SysUIRunner;
+import com.android.systemui.utils.TestableLooper;
+import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import com.android.systemui.utils.ViewUtils;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(SysUIRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class QuickStatusBarHeaderTest extends LeakCheckedTest {
+
+ @Before
+ public void setup() throws NoSuchFieldException, IllegalAccessException {
+ injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ }
+
+ @Test
+ @Ignore("Flaky")
+ public void testRoamingStuck() throws Exception {
+ TestableLooper looper = TestableLooper.get(this);
+ assertEquals(Looper.myLooper(), looper.getLooper());
+ assertEquals(Looper.myLooper(), Looper.getMainLooper());
+ QuickStatusBarHeader header = (QuickStatusBarHeader) LayoutInflater.from(mContext).inflate(
+ layout.quick_status_bar_expanded_header, null);
+ header.setExpanded(true);
+
+ ViewUtils.attachView(header);
+ looper.processMessages(1);
+ TextView emergencyText = (TextView) header.findViewById(
+ R.id.header_emergency_calls_only);
+ int subId = 0;
+ header.setMobileDataIndicators(null, null, 0, 0, false,
+ false, null, null, false, subId, true);
+ looper.processAllMessages();
+ assertEquals(mContext.getString(R.string.accessibility_data_connection_roaming),
+ emergencyText.getText());
+ assertEquals(View.VISIBLE, emergencyText.getVisibility());
+
+ header.setSubs(new ArrayList<>());
+ subId = 1;
+ header.setMobileDataIndicators(null, null, 0, 0, false,
+ false, null, null, false, subId, false);
+ looper.processAllMessages();
+
+ assertNotEquals(View.VISIBLE, emergencyText.getVisibility());
+ assertEquals(Looper.myLooper(), Looper.getMainLooper());
+ ViewUtils.detachView(header);
+ looper.processAllMessages();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
new file mode 100644
index 0000000..e3a5ef0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.statusbar.policy.ExtensionController.Extension;
+import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
+import com.android.systemui.tuner.TunerService;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ExtensionControllerTest extends SysuiTestCase {
+
+ private PluginManager mPluginManager;
+ private TunerService mTunerService;
+ private ExtensionController mExtensionController;
+
+ @Before
+ public void setup() {
+ mPluginManager = injectMockDependency(PluginManager.class);
+ mTunerService = injectMockDependency(TunerService.class);
+ mExtensionController = Dependency.get(ExtensionController.class);
+ }
+
+ @Test
+ public void testPlugin() {
+ Extension ext = mExtensionController.newExtension(OverlayPlugin.class)
+ .withPlugin(OverlayPlugin.class)
+ .build();
+ verify(mPluginManager).addPluginListener(eq(OverlayPlugin.ACTION), any(),
+ eq(OverlayPlugin.class));
+
+ ext.destroy();
+ verify(mPluginManager).removePluginListener(any());
+ }
+
+ @Test
+ public void testTuner() {
+ String[] keys = new String[] { "key1", "key2" };
+ TunerFactory<Object> factory = new ExtensionController.TunerFactory() {
+ @Override
+ public String[] keys() {
+ return keys;
+ }
+
+ @Override
+ public Object create(Map settings) {
+ return null;
+ }
+ };
+ Extension ext = mExtensionController.newExtension(Object.class)
+ .withTunerFactory(factory)
+ .build();
+ verify(mTunerService).addTunable(any(), eq(keys[0]), eq(keys[1]));
+
+ ext.destroy();
+ verify(mTunerService).removeTunable(any());
+ }
+
+ @Test
+ public void testDefault() {
+ Object o = new Object();
+ Extension ext = mExtensionController.newExtension(Object.class)
+ .withDefault(() -> o)
+ .build();
+ assertEquals(o, ext.get());
+ }
+
+ @Test
+ public void testCallback() {
+ Consumer<Object> callback = mock(Consumer.class);
+ final Object o = new Object();
+ mExtensionController.newExtension(Object.class)
+ .withDefault(() -> o)
+ .withCallback(callback)
+ .build();
+ verify(callback).accept(eq(o));
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
index d275973..8902e0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
@@ -244,9 +244,11 @@
mLooper.setAsMainLooper();
}
- mBase.evaluate();
-
- mLooper.destroy();
+ try {
+ mBase.evaluate();
+ } finally {
+ mLooper.destroy();
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java b/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
index 07e5f66..678b9f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
@@ -14,9 +14,11 @@
package com.android.systemui.utils;
+import android.content.pm.ApplicationInfo;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -27,18 +29,19 @@
public class ViewUtils {
public static void attachView(View view) {
+ // Make sure hardware acceleration isn't turned on.
+ view.getContext().getApplicationInfo().flags &=
+ ~(ApplicationInfo.FLAG_HARDWARE_ACCELERATED);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
LayoutParams.TYPE_APPLICATION_OVERLAY,
0, PixelFormat.TRANSLUCENT);
- Handler handler = new Handler(Looper.getMainLooper());
- handler.post(() -> InstrumentationRegistry.getContext()
- .getSystemService(WindowManager.class).addView(view, lp));
+ InstrumentationRegistry.getContext()
+ .getSystemService(WindowManager.class).addView(view, lp);
}
public static void detachView(View view) {
- Handler handler = new Handler(Looper.getMainLooper());
- handler.post(() -> InstrumentationRegistry.getContext()
- .getSystemService(WindowManager.class).removeView(view));
+ InstrumentationRegistry.getContext()
+ .getSystemService(WindowManager.class).removeViewImmediate(view);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
new file mode 100644
index 0000000..c0f5783
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import static org.mockito.Mockito.mock;
+
+import com.android.systemui.statusbar.policy.ExtensionController;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class FakeExtensionController implements ExtensionController {
+
+ private final Tracker mTracker;
+
+ public FakeExtensionController(LeakCheckedTest test) {
+ mTracker = test.getTracker("extension");
+ }
+
+ @Override
+ public <T> ExtensionBuilder<T> newExtension(Class<T> cls) {
+ final Object o = new Object();
+ mTracker.getLeakInfo(o).addAllocation(new Throwable());
+ return new FakeExtensionBuilder(o);
+ }
+
+ private class FakeExtensionBuilder<T> implements ExtensionBuilder<T> {
+ private final Object mAllocation;
+
+ public FakeExtensionBuilder(Object o) {
+ mAllocation = o;
+ }
+
+ @Override
+ public ExtensionBuilder<T> withTunerFactory(TunerFactory<T> factory) {
+ return this;
+ }
+
+ @Override
+ public <P extends T> ExtensionBuilder<T> withPlugin(Class<P> cls) {
+ return this;
+ }
+
+ @Override
+ public <P extends T> ExtensionBuilder<T> withPlugin(Class<P> cls, String action) {
+ return this;
+ }
+
+ @Override
+ public <P> ExtensionBuilder<T> withPlugin(Class<P> cls, String action, PluginConverter<T, P> converter) {
+ return this;
+ }
+
+ @Override
+ public ExtensionBuilder<T> withDefault(Supplier<T> def) {
+ return this;
+ }
+
+ @Override
+ public ExtensionBuilder<T> withCallback(Consumer<T> callback) {
+ return this;
+ }
+
+ @Override
+ public Extension build() {
+ return new FakeExtension(mAllocation);
+ }
+ }
+
+ private class FakeExtension<T> implements Extension<T> {
+ private final Object mAllocation;
+
+ public FakeExtension(Object allocation) {
+ mAllocation = allocation;
+ }
+
+ @Override
+ public T get() {
+ // TODO: Support defaults or things.
+ return null;
+ }
+
+ @Override
+ public void destroy() {
+ mTracker.getLeakInfo(mAllocation).clearAllocations();
+ }
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c81bd1b..c1c385a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3458,6 +3458,20 @@
// OPEN: Settings > Apps > Default Apps > Assist > Default voice input
DEFAULT_VOICE_INPUT_PICKER = 844;
+ // OPEN: Settings > Storage > [Profile]
+ SETTINGS_STORAGE_PROFILE = 845;
+
+ // OPEN: Settings > Security & screen lock -> Encryption & crendentials
+ // CATEGORY: SETTINGS
+ // OS: O
+ ENCRYPTION_AND_CREDENTIAL = 846;
+
+ // ACTION: Settings > About device > Build number
+ ACTION_SETTINGS_BUILD_NUMBER_PREF = 847;
+
+ // FIELD: Whether developer mode has already been enabled when clicking build number preference
+ FIELD_SETTINGS_BUILD_NUMBER_DEVELOPER_MODE_ENABLED = 848;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9416ac1..7eefb7e 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -435,9 +435,28 @@
// Number of occurrences of a soft AP session return code
message SoftApReturnCodeCount {
- // Return code of the soft AP session
- optional int32 return_code = 1;
+
+ enum SoftApStartResult {
+
+ // SoftApManager return code unknown
+ SOFT_AP_RETURN_CODE_UNKNOWN = 0;
+
+ // SoftAp started successfully
+ SOFT_AP_STARTED_SUCCESSFULLY = 1;
+
+ // Catch all for failures with no specific failure reason
+ SOFT_AP_FAILED_GENERAL_ERROR = 2;
+
+ // SoftAp failed to start due to NO_CHANNEL error
+ SOFT_AP_FAILED_NO_CHANNEL = 3;
+ }
+
+ // Historical, no longer used for writing as of 01/2017.
+ optional int32 return_code = 1 [deprecated = true];
// Occurrences of this soft AP return code
optional int32 count = 2;
+
+ // Result of attempt to start SoftAp
+ optional SoftApStartResult start_result = 3;
}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index bf3681b..447a47d 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -19,22 +19,17 @@
LOCAL_STATIC_LIBRARIES :=
-rs_generated_include_dir := $(call intermediates-dir-for,SHARED_LIBRARIES,libRS,,)
-
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
frameworks/rs \
frameworks/base/core/jni \
- frameworks/base/libs/hwui \
- $(rs_generated_include_dir)
+ frameworks/base/libs/hwui
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h)
LOCAL_MODULE:= librs_jni
-LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source)
LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := libRS libRSDriver
+LOCAL_REQUIRED_MODULES := libRS
include $(BUILD_SHARED_LIBRARY)
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index af370ff..2300da3 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -35,8 +35,8 @@
#include "android_runtime/android_util_AssetManager.h"
#include "android/graphics/GraphicsJNI.h"
-#include <rs.h>
#include <rsEnv.h>
+#include <rsApiStubs.h>
#include <gui/Surface.h>
#include <gui/GLConsumer.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
@@ -1134,7 +1134,7 @@
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
assert(dataSize == 5);
- uintptr_t elementData[5];
+ uint32_t elementData[5];
rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize);
for(jint i = 0; i < dataSize; i ++) {
@@ -1157,7 +1157,7 @@
uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t));
const char **names = (const char **)malloc(dataSize * sizeof(const char *));
- uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t));
+ size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t));
rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes,
(uint32_t)dataSize);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 4d2b106..c50623e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -709,7 +709,7 @@
}
private boolean isUserRunningAndUnlocked(@UserIdInt int userId) {
- return mUserManager.isUserRunning(userId) && StorageManager.isUserKeyUnlocked(userId);
+ return mUserManager.isUserUnlockingOrUnlocked(userId);
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
deleted file mode 100644
index 64c6abd..0000000
--- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.DEBUG;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-
-/**
- * A window above the application that is smartly anchored to a rectangular region.
- */
-final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchListener {
- private static final String TAG = "AutoFill";
-
- private static final int NULL_HEIGHT = -1;
-
- private final WindowManager mWm;
- private final IBinder mAppToken;
- private final View mContentView;
-
- private final View mWindowSizeListenerView;
- private final int mMinMargin;
-
- private int mLastHeight = NULL_HEIGHT;
- @Nullable
- private Rect mLastBounds;
- @Nullable
- private Rect mLastDisplayBounds;
-
- /**
- * Constructor.
- *
- * @param wm window manager that draws the content on a window
- * @param appToken token to pass to window manager
- * @param contentView content of the window
- */
- AnchoredWindow(WindowManager wm, IBinder appToken, View contentView) {
- mWm = wm;
- mAppToken = appToken;
- mContentView = contentView;
-
- mContentView.addOnLayoutChangeListener(this);
-
- Context context = contentView.getContext();
-
- mWindowSizeListenerView = new FrameLayout(context);
- mWindowSizeListenerView.addOnLayoutChangeListener(this);
-
- mMinMargin = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.autofill_fill_min_margin);
- }
-
- /**
- * Shows the window.
- *
- * @param bounds the region the window should be anchored to
- */
- void show(Rect bounds) {
- if (DEBUG) Slog.d(TAG, "show bounds=" + bounds);
-
- if (!mWindowSizeListenerView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "adding mWindowSizeListenerView");
- LayoutParams params = createWindowLayoutParams(
- mAppToken,
- LayoutParams.FLAG_NOT_TOUCHABLE); // not touchable
- params.gravity = Gravity.LEFT | Gravity.TOP;
- params.x = 0;
- params.y = 0;
- params.width = LayoutParams.MATCH_PARENT;
- params.height = LayoutParams.MATCH_PARENT;
- mWm.addView(mWindowSizeListenerView, params);
- }
-
- updateBounds(bounds);
- }
-
- /**
- * Hides the window.
- */
- void hide() {
- if (DEBUG) Slog.d(TAG, "hide");
-
- mLastHeight = NULL_HEIGHT;
- mLastBounds = null;
- mLastDisplayBounds = null;
-
- if (mWindowSizeListenerView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "removing mWindowSizeListenerView");
- mWm.removeView(mWindowSizeListenerView);
- }
-
- if (mContentView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "removing mContentView");
- mContentView.setOnTouchListener(null);
- mWm.removeView(mContentView);
- }
- }
-
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (view == mWindowSizeListenerView) {
- if (DEBUG) Slog.d(TAG, "onLayoutChange() for mWindowSizeListenerView");
- // mWindowSizeListenerView layout changed, get the size of the display bounds and updateLocked
- // the window.
- final Rect displayBounds = new Rect();
- view.getBoundsOnScreen(displayBounds);
- updateDisplayBounds(displayBounds);
- } else if (view == mContentView) {
- // mContentView layout changed, updateLocked the window in case its height changed.
- if (DEBUG) Slog.d(TAG, "onLayoutChange() for mContentView");
- updateHeight();
- }
- }
-
- // When the window is touched outside, hide the window.
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- hide();
- return true;
- }
- return false;
- }
-
- private boolean updateHeight() {
- final Rect displayBounds = mLastDisplayBounds;
- if (displayBounds == null) {
- return false;
- }
-
- mContentView.measure(
- MeasureSpec.makeMeasureSpec(displayBounds.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(displayBounds.height(), MeasureSpec.AT_MOST));
- int height = mContentView.getMeasuredHeight();
- if (height != mLastHeight) {
- if (DEBUG) Slog.d(TAG, "updateLocked height=" + height);
- mLastHeight = height;
- update(height, mLastBounds, displayBounds);
- return true;
- } else {
- return false;
- }
- }
-
- private void updateBounds(Rect bounds) {
- if (!bounds.equals(mLastBounds)) {
- if (DEBUG) Slog.d(TAG, "updateLocked bounds=" + bounds);
- mLastBounds = bounds;
-
- update(mLastHeight, bounds, mLastDisplayBounds);
- }
- }
-
- private void updateDisplayBounds(Rect displayBounds) {
- if (!displayBounds.equals(mLastDisplayBounds)) {
- if (DEBUG) Slog.d(TAG, "updateLocked displayBounds=" + displayBounds);
- mLastDisplayBounds = displayBounds;
-
- if (!updateHeight()) {
- update(mLastHeight, mLastBounds, displayBounds);
- }
- }
- }
-
- // Updates the window if height, bounds, and displayBounds are not null.
- // Caller should ensure that something changed before calling.
- private void update(int height, @Nullable Rect bounds, @Nullable Rect displayBounds) {
- if (height == NULL_HEIGHT || bounds == null || displayBounds == null) {
- return;
- }
-
- if (DEBUG) Slog.d(TAG, "updateLocked height=" + height + ", bounds=" + bounds
- + ", displayBounds=" + displayBounds);
-
- final LayoutParams params = createWindowLayoutParams(mAppToken,
- LayoutParams.FLAG_NOT_TOUCH_MODAL // outside touches go to windows behind us
- | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); // outside touches trigger MotionEvent
- params.setTitle("AutoFill Fill"); // used for debugging
- updatePosition(params, height, mMinMargin, bounds, displayBounds);
- if (!mContentView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "adding mContentView");
- mWm.addView(mContentView, params);
- mContentView.setOnTouchListener(this);
- } else {
- if (DEBUG) Slog.d(TAG, "updating mContentView");
- mWm.updateViewLayout(mContentView, params);
- }
- }
-
- /**
- * Updates the position of the window by altering the {@link LayoutParams}.
- *
- * <p>The window can be anchored either above or below the bounds. Anchoring the window below
- * the bounds is preferred, if it fits. Otherwise, anchor the window on the side with more
- * space.
- *
- * @param params the params to updateLocked
- * @param height the requested height of the window
- * @param minMargin the minimum margin between the window and the display bounds
- * @param bounds the region the window should be anchored to
- * @param displayBounds the region in which the window may be displayed
- */
- private static void updatePosition(
- LayoutParams params,
- int height,
- int minMargin,
- Rect bounds,
- Rect displayBounds) {
- boolean below;
- int verticalSpace;
- final int verticalSpaceBelow = displayBounds.bottom - bounds.bottom - minMargin;
- if (height <= verticalSpaceBelow) {
- // Fits below bounds.
- below = true;
- verticalSpace = height;
- } else {
- final int verticalSpaceAbove = bounds.top - displayBounds.top - minMargin;
- if (height <= verticalSpaceAbove) {
- // Fits above bounds.
- below = false;
- verticalSpace = height;
- } else {
- // Pick above/below based on which has the most space.
- if (verticalSpaceBelow >= verticalSpaceAbove) {
- below = true;
- verticalSpace = verticalSpaceBelow;
- } else {
- below = false;
- verticalSpace = verticalSpaceAbove;
- }
- }
- }
-
- int gravity;
- int y;
- if (below) {
- if (DEBUG) Slog.d(TAG, "anchorBelow");
- gravity = Gravity.TOP | Gravity.LEFT;
- y = bounds.bottom - displayBounds.top;
- } else {
- if (DEBUG) Slog.d(TAG, "anchorAbove");
- gravity = Gravity.BOTTOM | Gravity.LEFT;
- y = displayBounds.bottom - bounds.top;
- }
-
- final int x = bounds.left - displayBounds.left;
-
- params.gravity = gravity;
- params.x = x;
- params.y = y;
- params.width = bounds.width();
- params.height = verticalSpace;
- }
-
- private static LayoutParams createWindowLayoutParams(IBinder appToken, int flags) {
- final LayoutParams params = new LayoutParams();
- params.token = appToken;
- params.type = LayoutParams.TYPE_PHONE;
- params.flags =
- flags
- | LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events
- | LayoutParams.FLAG_ALT_FOCUSABLE_IM; // resize for soft input
- params.format = PixelFormat.TRANSLUCENT;
- return params;
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 3257812..7a85d4e 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
-import static com.android.server.autofill.Helper.DEBUG;
import static com.android.server.autofill.Helper.VERBOSE;
import android.Manifest;
@@ -57,6 +56,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.autofill.ui.AutoFillUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -100,14 +100,16 @@
private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
// TODO(b/33197203): set a different max (or disable it) on low-memory devices.
- private final LocalLog mRequestsHistory = new LocalLog(100);
+ private final LocalLog mRequestsHistory = new LocalLog(20);
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
final String reason = intent.getStringExtra("reason");
- if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
+ if (VERBOSE) {
+ Slog.v(TAG, "close system dialogs: " + reason);
+ }
mUi.hideAll();
}
}
@@ -246,7 +248,9 @@
private IBinder getTopActivityForUser() {
final List<IBinder> topActivities = LocalServices
.getService(ActivityManagerInternal.class).getTopVisibleActivities();
- if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
+ if (VERBOSE) {
+ Slog.v(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
+ }
if (topActivities.isEmpty()) {
Slog.w(TAG, "Could not get top activity");
return null;
@@ -271,29 +275,20 @@
}
@Override
- public void startSession(IBinder activityToken, IBinder appCallback, AutoFillId autoFillId,
- Rect bounds, AutoFillValue value, int userId) {
+ public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId) {
// TODO(b/33197203): make sure it's called by resumed / focused activity
- if (VERBOSE) {
- Slog.v(TAG, "startSession: autoFillId=" + autoFillId + ", bounds=" + bounds
- + ", value=" + value);
- }
-
synchronized (mLock) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
- service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value);
+ service.startSessionLocked(activityToken, windowToken, appCallback,
+ autoFillId, bounds, value);
}
}
@Override
public void updateSession(IBinder activityToken, AutoFillId id, Rect bounds,
AutoFillValue value, int flags, int userId) {
- if (DEBUG) {
- Slog.d(TAG, "updateSession: flags=" + flags + ", autoFillId=" + id
- + ", bounds=" + bounds + ", value=" + value);
- }
-
synchronized (mLock) {
final AutoFillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
@@ -305,8 +300,6 @@
@Override
public void finishSession(IBinder activityToken, int userId) {
- if (VERBOSE) Slog.v(TAG, "finishSession(): " + activityToken);
-
synchronized (mLock) {
final AutoFillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index ec3d356..7e9cbca 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -56,6 +56,7 @@
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
+import android.service.autofill.SaveInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -70,6 +71,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.IResultReceiver;
+import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -103,7 +105,7 @@
handleSessionSave((IBinder) msg.obj);
break;
default:
- Slog.d(TAG, "invalid msg: " + msg);
+ Slog.w(TAG, "invalid msg on handler: " + msg);
}
};
@@ -127,17 +129,18 @@
private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
- if (DEBUG) Slog.d(TAG, "resultCode on mAssistReceiver: " + resultCode);
+ if (VERBOSE) {
+ Slog.v(TAG, "resultCode on mAssistReceiver: " + resultCode);
+ }
final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
if (structure == null) {
- Slog.w(TAG, "no assist structure for id " + resultCode);
+ Slog.wtf(TAG, "no assist structure for id " + resultCode);
return;
}
final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS);
if (receiverExtras == null) {
- // Should not happen
Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver");
return;
}
@@ -194,7 +197,7 @@
final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
return pm.getApplicationLabel(info);
} catch (Exception e) {
- Slog.w(TAG, "Could not get label for " + packageName + ": " + e);
+ Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
return packageName;
}
}
@@ -210,7 +213,7 @@
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
} catch (RuntimeException | RemoteException e) {
- Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
+ Slog.e(TAG, "Bad auto-fill service name " + componentName + ": " + e);
return;
}
}
@@ -234,7 +237,7 @@
sendStateToClients();
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
+ Slog.e(TAG, "Bad auto-fill service name " + componentName + ": " + e);
}
}
@@ -272,15 +275,15 @@
}
}
- void startSessionLocked(IBinder activityToken, IBinder appCallbackToken, AutoFillId autoFillId,
- Rect bounds, AutoFillValue value) {
+ void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value) {
if (!hasService()) {
return;
}
- final String historyItem = "s=" + new ComponentName(mInfo.getServiceInfo().packageName,
- mInfo.getServiceInfo().name) + " u=" + mUserId + " a=" + activityToken
- + " i=" + autoFillId + " b=" + bounds + " v=" + value;
+ final String historyItem = "s=" + mInfo.getServiceInfo().packageName
+ + " u=" + mUserId + " a=" + activityToken
+ + " i=" + autoFillId + " b=" + bounds;
mRequestsHistory.log(historyItem);
// TODO(b/33197203): Handle partitioning
@@ -290,7 +293,8 @@
return;
}
- final Session newSession = createSessionByTokenLocked(activityToken, appCallbackToken);
+ final Session newSession = createSessionByTokenLocked(activityToken,
+ windowToken, appCallbackToken);
newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION);
}
@@ -308,8 +312,10 @@
session.showSaveLocked();
}
- private Session createSessionByTokenLocked(IBinder activityToken, IBinder appCallbackToken) {
- final Session newSession = new Session(mContext, activityToken, appCallbackToken);
+ private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
+ IBinder appCallbackToken) {
+ final Session newSession = new Session(mContext, activityToken,
+ windowToken, appCallbackToken);
mSessions.put(activityToken, newSession);
/*
@@ -327,8 +333,6 @@
try {
if (!ActivityManager.getService().requestAutoFillData(mAssistReceiver,
receiverExtras, activityToken)) {
- // TODO(b/33197203): might need a way to warn user (perhaps a new method on
- // AutoFillService).
Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
}
} finally {
@@ -345,7 +349,9 @@
// TODO(b/33197203): add MetricsLogger call
final Session session = mSessions.get(activityToken);
if (session == null) {
- Slog.w(TAG, "updateSessionLocked(): session gone for " + activityToken);
+ if (VERBOSE) {
+ Slog.v(TAG, "updateSessionLocked(): session gone for " + activityToken);
+ }
return;
}
@@ -365,7 +371,9 @@
}
void destroyLocked() {
- if (VERBOSE) Slog.v(TAG, "destroyLocked()");
+ if (VERBOSE) {
+ Slog.v(TAG, "destroyLocked()");
+ }
for (Session session : mSessions.values()) {
session.destroyLocked();
@@ -376,7 +384,7 @@
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
- pw.print(prefix); pw.println("Component:"); pw.println(mInfo != null
+ pw.print(prefix); pw.print("Component:"); pw.println(mInfo != null
? mInfo.getServiceInfo().getComponentName() : null);
if (VERBOSE) {
@@ -522,8 +530,6 @@
@Override
public String toString() {
- if (!DEBUG) return super.toString();
-
return "ViewState: [id=" + mId + ", value=" + mAutoFillValue + ", bounds=" + mBounds
+ ", updated = " + mValueUpdated + "]";
}
@@ -556,6 +562,7 @@
final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
AutoFillUI.AutoFillUiCallback {
private final IBinder mActivityToken;
+ private final IBinder mWindowToken;
@GuardedBy("mLock")
private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
@@ -587,15 +594,19 @@
@GuardedBy("mLock")
private AssistStructure mStructure;
- private Session(Context context, IBinder activityToken, IBinder client) {
+ private Session(Context context, IBinder activityToken, IBinder windowToken,
+ IBinder client) {
mRemoteFillService = new RemoteFillService(context,
mInfo.getServiceInfo().getComponentName(), mUserId, this);
mActivityToken = activityToken;
+ mWindowToken = windowToken;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
try {
client.linkToDeath(() -> {
- if (DEBUG) Slog.d(TAG, "app binder died");
+ if (DEBUG) {
+ Slog.d(TAG, "app binder died");
+ }
removeSelf();
}, 0);
@@ -662,7 +673,9 @@
// AutoFillUiCallback
@Override
public void fill(Dataset dataset) {
- autoFill(dataset);
+ mHandlerCaller.getHandler().post(() -> {
+ autoFill(dataset);
+ });
}
// AutoFillUiCallback
@@ -696,26 +709,30 @@
*/
public void showSaveLocked() {
if (mStructure == null) {
- // Sanity check; should not happen...
Slog.wtf(TAG, "showSaveLocked(): no mStructure");
return;
}
if (mCurrentResponse == null) {
- // Happens when the activity / session was finished before the service replied.
- Slog.d(TAG, "showSaveLocked(): no mCurrentResponse yet");
+ // Happens when the activity / session was finished before the service replied, or
+ // when the service cannot auto-fill it (and returned a null response).
+ if (DEBUG) {
+ Slog.d(TAG, "showSaveLocked(): no mCurrentResponse");
+ }
return;
}
- final ArraySet<AutoFillId> savableIds = mCurrentResponse.getSavableIds();
- if (VERBOSE) Slog.v(TAG, "showSaveLocked(): savableIds=" + savableIds);
+ final SaveInfo saveInfo = mCurrentResponse.getSaveInfo();
+ if (DEBUG) {
+ Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo);
+ }
- if (savableIds.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "showSaveLocked(): service doesn't want to save");
+ if (saveInfo == null || saveInfo.getSavableIds() == null
+ || saveInfo.getSavableIds().isEmpty()) {
return;
}
- final int size = savableIds.size();
+ final int size = saveInfo.getSavableIds().size();
for (int i = 0; i < size; i++) {
- final AutoFillId id = savableIds.valueAt(i);
+ final AutoFillId id = saveInfo.getSavableIds().valueAt(i);
final ViewState state = mViewStates.get(id);
if (state != null && state.mValueUpdated) {
final AutoFillValue filledValue = findValue(mAutoFilledDataset, id);
@@ -726,27 +743,34 @@
Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": "
+ state.mAutoFillValue);
}
- getUiForShowing().showSaveUi();
+ getUiForShowing().showSaveUi(mInfo.getServiceInfo()
+ .loadLabel(mContext.getPackageManager()));
return;
}
}
// Nothing changed...
- if (DEBUG) Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities");
+ if (DEBUG) {
+ Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities");
+ }
}
/**
* Calls service when user requested save.
*/
private void callSaveLocked() {
- if (DEBUG) Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
+ if (DEBUG) {
+ Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
+ }
final Bundle extras = this.mCurrentResponse.getExtras();
for (Entry<AutoFillId, ViewState> entry : mViewStates.entrySet()) {
final AutoFillValue value = entry.getValue().mAutoFillValue;
if (value == null) {
- if (VERBOSE) Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey());
+ if (VERBOSE) {
+ Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey());
+ }
continue;
}
final AutoFillId id = entry.getKey();
@@ -755,7 +779,9 @@
Slog.w(TAG, "callSaveLocked(): did not find node with id " + id);
continue;
}
- if (DEBUG) Slog.d(TAG, "callSaveLocked(): updating " + id + " to " + value);
+ if (VERBOSE) {
+ Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
+ }
node.updateAutoFillValue(value);
}
@@ -771,11 +797,9 @@
}
void updateLocked(AutoFillId id, Rect bounds, AutoFillValue value, int flags) {
- if (DEBUG) Slog.d(TAG, "updateLocked(): id=" + id + ", flags=" + flags);
-
if (mAutoFilledDataset != null && (flags & FLAG_VALUE_CHANGED) == 0) {
// TODO(b/33197203): ignoring because we don't support partitions yet
- if (DEBUG) Slog.d(TAG, "updateLocked(): ignoring " + flags + " after auto-filled");
+ Slog.d(TAG, "updateLocked(): ignoring " + flags + " after app was auto-filled");
return;
}
@@ -810,8 +834,9 @@
viewState.mAutoFillValue = value;
// Update the chooser UI
- mUi.updateFillUi(value.coerceToString());
+ getUiForShowing().filterFillUi(value.coerceToString());
}
+
return;
}
@@ -841,7 +866,7 @@
return;
}
- Slog.w(TAG, "unknown flags " + flags);
+ Slog.w(TAG, "updateLocked(): unknown flags " + flags);
}
@Override
@@ -860,8 +885,10 @@
}
private void processResponseLocked(FillResponse response) {
- if (DEBUG) Slog.d(TAG, "processResponseLocked(authRequired="
- + response.getAuthentication() + "):" + response);
+ if (DEBUG) {
+ Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication()
+ + "):" + response);
+ }
// TODO(b/33197203): add MetricsLogger calls
@@ -945,7 +972,9 @@
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
try {
- if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+ if (DEBUG) {
+ Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+ }
mClient.autoFill(dataset.getFieldIds(), dataset.getFieldValues());
} catch (RemoteException e) {
Slog.w(TAG, "Error auto-filling activity: " + e);
@@ -955,7 +984,7 @@
private AutoFillUI getUiForShowing() {
synchronized (mLock) {
- mUi.setCallbackLocked(this, mActivityToken);
+ mUi.setCallback(this, mWindowToken);
return mUi;
}
}
@@ -995,11 +1024,13 @@
private void destroyLocked() {
mRemoteFillService.destroy();
- mUi.setCallbackLocked(null, null);
+ mUi.setCallback(null, null);
}
private void removeSelf() {
- if (VERBOSE) Slog.v(TAG, "removeSelf()");
+ if (VERBOSE) {
+ Slog.v(TAG, "removeSelf()");
+ }
synchronized (mLock) {
destroyLocked();
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
deleted file mode 100644
index ee485df..0000000
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.DEBUG;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.IntentSender;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.IBinder;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.text.format.DateUtils;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.autofill.AutoFillId;
-import android.widget.Toast;
-
-import com.android.server.UiThread;
-
-import java.io.PrintWriter;
-
-/**
- * Handles all auto-fill related UI tasks.
- */
-// TODO(b/33197203): document exactly what once the auto-fill bar is implemented
-final class AutoFillUI {
- private static final String TAG = "AutoFillUI";
-
- private static final long SNACK_BAR_LIFETIME_MS = 5 * DateUtils.SECOND_IN_MILLIS;
-
- private static final int MSG_HIDE_SNACK_BAR = 1;
-
- private final Handler mHandler = UiThread.getHandler();
-
- private final Context mContext;
-
- private final WindowManager mWm;
-
- private AnchoredWindow mFillWindow;
-
- private DatasetPicker mDatasetPicker;
-
- private AutoFillUiCallback mCallback;
-
- private IBinder mActivityToken;
-
- /**
- * Custom snackbar UI used for saving autofill or other informational messages.
- */
- private View mSnackbar;
-
- AutoFillUI(Context context) {
- mContext = context;
- mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- }
-
- void setCallbackLocked(AutoFillUiCallback callback, IBinder activityToken) {
- mHandler.post(() -> {
- if (callback != mCallback && activityToken != mActivityToken) {
- hideAllUiThread();
- mCallback = callback;
- mActivityToken = activityToken;
- }
- });
- }
-
- /**
- * Displays an error message to the user.
- */
- void showError(CharSequence message) {
- // TODO(b/33197203): proper implementation
- UiThread.getHandler().post(() -> {
- if (!hasCallback()) {
- return;
- }
- hideAllUiThread();
- Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
- });
- }
-
- /**
- * Hides the fill UI.
- */
- void hideFillUi() {
- mHandler.post(() -> hideFillUiUiThread());
- }
-
- @android.annotation.UiThread
- private void hideFillUiUiThread() {
- if (mFillWindow != null) {
- if (DEBUG) Slog.d(TAG, "hideFillUiUiThread(): hide" + mFillWindow);
- mFillWindow.hide();
- }
- mFillWindow = null;
- mDatasetPicker = null;
- }
-
- void updateFillUi(@Nullable String filterText) {
- mHandler.post(() -> {
- if (!hasCallback()) {
- return;
- }
- hideSnackbarUiThread();
- if (mDatasetPicker != null) {
- mDatasetPicker.update(filterText);
- }
- });
- }
-
- /**
- * Shows the fill UI, removing the previous fill UI if the has changed.
- *
- * @param focusedId the currently focused field
- * @param response the current fill response
- * @param bounds bounds of the view to be filled, used if changed
- * @param filterText text of the view to be filled, used if changed
- */
- void showFillUi(AutoFillId focusedId, @Nullable FillResponse response, Rect bounds,
- String filterText) {
- mHandler.post(() -> {
- if (!hasCallback()) {
- return;
- }
- hideSnackbarUiThread();
- final View content;
- if (response.getPresentation() != null) {
- content = response.getPresentation().apply(mContext, null);
- content.setOnClickListener((view) -> {
- if (mCallback != null) {
- mCallback.authenticate(response.getAuthentication());
- }
- hideFillUiUiThread();
- });
- } else {
- mDatasetPicker = new DatasetPicker(mContext, response.getDatasets(),
- focusedId, new DatasetPicker.Listener() {
- @Override
- public void onDatasetPicked(Dataset dataset) {
- if (mCallback != null) {
- mCallback.fill(dataset);
- }
- hideFillUiUiThread();
- }
-
- @Override
- public void onCanceled() {
- hideFillUiUiThread();
- }
- });
- mDatasetPicker.update(filterText);
- content = mDatasetPicker;
- }
-
- mFillWindow = new AnchoredWindow(mWm, mActivityToken, content);
- mFillWindow.show(bounds);
- });
- }
-
- /**
- * Shows the UI asking the user to save for auto-fill.
- */
- void showSaveUi() {
- mHandler.post(() -> {
- if (!hasCallback()) {
- return;
- }
- hideAllUiThread();
- showSnackbarUiThread(new SavePrompt(mContext,
- new SavePrompt.OnSaveListener() {
- @Override
- public void onSaveClick() {
- hideSnackbarUiThread();
- // TODO(b/33197203): add MetricsLogger call
- mCallback.save();
- }
-
- @Override
- public void onCancelClick() {
- // TODO(b/33197203): add MetricsLogger call
- hideSnackbarUiThread();
- }
- }));
- });
- }
-
- /**
- * Hides all UI affordances.
- */
- void hideAll() {
- mHandler.post(() -> hideAllUiThread());
- }
-
- @android.annotation.UiThread
- private void hideAllUiThread() {
- hideSnackbarUiThread();
- hideFillUiUiThread();
- }
-
- void dump(PrintWriter pw) {
- pw.println("AufoFill UI");
- final String prefix = " ";
- pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
- pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
- }
-
- //similar to a snackbar, but can be a bit custom since it is more than just text. This will
- //allow two buttons for saving or not saving the autofill for instance as well.
- @android.annotation.UiThread
- private void showSnackbarUiThread(View snackBar) {
- final LayoutParams params = new LayoutParams();
- params.type = LayoutParams.TYPE_PHONE; // TODO(b/33197203) use app window token
- params.flags =
- LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events,
- | LayoutParams.FLAG_ALT_FOCUSABLE_IM // resize for soft input
- | LayoutParams.FLAG_NOT_TOUCH_MODAL; // outside touches go to windows behind us
- params.softInputMode =
- LayoutParams.SOFT_INPUT_ADJUST_PAN; // pan with soft input
- params.gravity = Gravity.BOTTOM | Gravity.START;
- params.width = LayoutParams.MATCH_PARENT;
- params.height = LayoutParams.WRAP_CONTENT;
-
- mHandler.post(() -> {
- mSnackbar = snackBar;
- mWm.addView(mSnackbar, params);
- });
-
- if (DEBUG) {
- Slog.d(TAG, "showSnackbar(): auto dismissing it in " + SNACK_BAR_LIFETIME_MS + " ms");
- }
- mHandler.sendMessageDelayed(mHandler
- .obtainMessage(MSG_HIDE_SNACK_BAR), SNACK_BAR_LIFETIME_MS);
- }
-
- @android.annotation.UiThread
- private void hideSnackbarUiThread() {
- mHandler.removeMessages(MSG_HIDE_SNACK_BAR);
- if (mSnackbar != null) {
- mWm.removeView(mSnackbar);
- mSnackbar = null;
- }
- }
-
- private boolean hasCallback() {
- return mCallback != null;
- }
-
- interface AutoFillUiCallback {
- void authenticate(IntentSender intent);
- void fill(Dataset dataset);
- void save();
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
deleted file mode 100644
index e25f2ce..0000000
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import android.content.Context;
-import android.service.autofill.Dataset;
-import android.util.ArraySet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.RemoteViews;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class manages the dataset selection UI.
- */
-final class DatasetPicker extends FrameLayout implements OnItemClickListener {
- interface Listener {
- void onDatasetPicked(Dataset dataset);
- void onCanceled();
- }
-
- private final Listener mListener;
-
- private final ArrayAdapter<ViewItem> mAdapter;
-
- DatasetPicker(Context context, ArrayList<Dataset> datasets, AutoFillId filteredViewId,
- Listener listener) {
- super(context);
- mListener = listener;
-
- final List<ViewItem> items = new ArrayList<>(datasets.size());
- for (Dataset dataset : datasets) {
- final int index = dataset.getFieldIds().indexOf(filteredViewId);
- if (index >= 0) {
- AutoFillValue value = dataset.getFieldValues().get(index);
- items.add(new ViewItem(dataset, value.coerceToString()));
- }
- }
-
- mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- RemoteViews presentation = getItem(position).getDataset().getPresentation();
- return presentation.apply(context, parent);
- }
- };
-
- LayoutInflater inflater = LayoutInflater.from(context);
- ListView content = (ListView) inflater.inflate(
- com.android.internal.R.layout.autofill_dataset_picker, this, true)
- .findViewById(com.android.internal.R.id.list);
- content.setAdapter(mAdapter);
- content.setOnItemClickListener(this);
- }
-
- public void update(String prefix) {
- mAdapter.getFilter().filter(prefix, (count) -> {
- if (count <= 0 && mListener != null) {
- mListener.onCanceled();
- }
- });
- }
-
- @Override
- public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
- if (mListener != null) {
- final ViewItem vi = (ViewItem) adapterView.getItemAtPosition(pos);
- mListener.onDatasetPicked(vi.getDataset());
- }
- }
-
- private static class ViewItem {
- private final String mValue;
- private final Dataset mDataset;
-
- ViewItem(Dataset dataset, String value) {
- mDataset = dataset;
- mValue = value;
- }
-
- public Dataset getDataset() {
- return mDataset;
- }
-
- @Override
- public String toString() {
- // used by ArrayAdapter
- return mValue;
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/SavePrompt.java b/services/autofill/java/com/android/server/autofill/SavePrompt.java
deleted file mode 100644
index 85c6d7d..0000000
--- a/services/autofill/java/com/android/server/autofill/SavePrompt.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import android.content.Context;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.R;
-
-/**
- * Autofill Save Prompt
- */
-final class SavePrompt extends RelativeLayout {
- public interface OnSaveListener {
- void onSaveClick();
- void onCancelClick();
- }
-
- private final TextView mNoButton;
- private final TextView mYesButton;
- private final OnSaveListener mListener;
-
- SavePrompt(Context context, OnSaveListener listener) {
- super(context);
- mListener = listener;
- LayoutInflater inflater = LayoutInflater.from(context);
- View view = inflater.inflate(R.layout.autofill_save, this);
-
- mNoButton = (TextView) view.findViewById(R.id.autofill_save_no);
- mNoButton.setOnClickListener((v) -> {
- mListener.onCancelClick();
- });
-
- mYesButton = (TextView) view.findViewById(R.id.autofill_save_yes);
- mYesButton.setOnClickListener((v) -> {
- mListener.onSaveClick();
- });
-
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
new file mode 100644
index 0000000..cc0baa3
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.IntentSender;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.text.TextUtils;
+import android.view.autofill.AutoFillId;
+import android.widget.Toast;
+
+import com.android.server.UiThread;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles all auto-fill related UI tasks. The UI has two components:
+ * fill UI that shows a popup style window anchored at the focused
+ * input field for choosing a dataset to fill or trigger the response
+ * authentication flow; save UI that shows a toast style window for
+ * managing saving of user edits.
+ */
+public final class AutoFillUI {
+ private final Handler mHandler = UiThread.getHandler();
+ private final @NonNull Context mContext;
+
+ private @Nullable FillUi mFillUi;
+ private @Nullable SaveUi mSaveUi;
+
+ private @Nullable AutoFillUiCallback mCallback;
+ private @Nullable IBinder mWindowToken;
+
+ public interface AutoFillUiCallback {
+ void authenticate(@NonNull IntentSender intent);
+ void fill(@NonNull Dataset dataset);
+ void save();
+ }
+
+ public AutoFillUI(@NonNull Context context) {
+ mContext = context;
+ }
+
+ public void setCallback(@Nullable AutoFillUiCallback callback,
+ @Nullable IBinder windowToken) {
+ mHandler.post(() -> {
+ if (mCallback != callback || mWindowToken != windowToken) {
+ hideAllUiThread();
+ mCallback = callback;
+ mWindowToken = windowToken;
+ }
+ });
+ }
+
+ /**
+ * Displays an error message to the user.
+ */
+ public void showError(@Nullable CharSequence message) {
+ mHandler.post(() -> {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAllUiThread();
+ if (!TextUtils.isEmpty(message)) {
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
+ /**
+ * Hides the fill UI.
+ */
+ public void hideFillUi() {
+ mHandler.post(this::hideFillUiUiThread);
+ }
+
+ /**
+ * Filters the options in the fill UI.
+ *
+ * @param filterText The filter prefix.
+ */
+ public void filterFillUi(@Nullable String filterText) {
+ mHandler.post(() -> {
+ if (!hasCallback()) {
+ return;
+ }
+ hideSaveUiUiThread();
+ if (mFillUi != null) {
+ mFillUi.filter(filterText);
+ }
+ });
+ }
+
+ /**
+ * Updates the position of the fill UI.
+ *
+ * @param anchoredBounds The bounds of the anchor view.
+ */
+ public void updateFillUi(@NonNull Rect anchoredBounds) {
+ mHandler.post(() -> {
+ if (!hasCallback()) {
+ return;
+ }
+ hideSaveUiUiThread();
+ if (mFillUi != null) {
+ mFillUi.update(anchoredBounds);
+ }
+ });
+ }
+
+ /**
+ * Shows the fill UI, removing the previous fill UI if the has changed.
+ *
+ * @param focusedId the currently focused field
+ * @param response the current fill response
+ * @param anchorBounds bounds of the focused view
+ * @param filterText text of the view to be filled
+ */
+ public void showFillUi(@NonNull AutoFillId focusedId, @NonNull FillResponse response,
+ @NonNull Rect anchorBounds, @Nullable String filterText) {
+ mHandler.post(() -> {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAllUiThread();
+ mFillUi = new FillUi(mContext, response, focusedId,
+ mWindowToken, anchorBounds, filterText, new FillUi.Callback() {
+ @Override
+ public void onResponsePicked(FillResponse response) {
+ hideFillUiUiThread();
+ if (mCallback != null) {
+ mCallback.authenticate(response.getAuthentication());
+ }
+ }
+
+ @Override
+ public void onDatasetPicked(Dataset dataset) {
+ hideFillUiUiThread();
+ if (mCallback != null) {
+ mCallback.fill(dataset);
+ }
+ // TODO(b/33197203): add MetricsLogger call
+ }
+
+ @Override
+ public void onCanceled() {
+ hideFillUiUiThread();
+ // TODO(b/33197203): add MetricsLogger call
+ }
+ });
+ });
+ }
+
+ /**
+ * Shows the UI asking the user to save for auto-fill.
+ */
+ public void showSaveUi(@NonNull CharSequence providerLabel) {
+ mHandler.post(() -> {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAllUiThread();
+ mSaveUi = new SaveUi(mContext, providerLabel,
+ new SaveUi.OnSaveListener() {
+ @Override
+ public void onSave() {
+ hideSaveUiUiThread();
+ if (mCallback != null) {
+ mCallback.save();
+ }
+ // TODO(b/33197203): add MetricsLogger call
+ }
+
+ @Override
+ public void onCancel() {
+ // TODO(b/33197203): add MetricsLogger call
+ hideSaveUiUiThread();
+ }
+ });
+ });
+ }
+
+ /**
+ * Hides all UI affordances.
+ */
+ public void hideAll() {
+ mHandler.post(this::hideAllUiThread);
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("AufoFill UI");
+ final String prefix = " ";
+ pw.print(prefix); pw.print("showsFillUi: "); pw.println(mFillUi != null);
+ pw.print(prefix); pw.print("showsSaveUi: "); pw.println(mSaveUi != null);
+ }
+
+ @android.annotation.UiThread
+ private void hideFillUiUiThread() {
+ if (mFillUi != null) {
+ mFillUi.destroy();
+ mFillUi = null;
+ }
+ }
+
+ @android.annotation.UiThread
+ private void hideSaveUiUiThread() {
+ if (mSaveUi != null) {
+ mSaveUi.destroy();
+ mSaveUi = null;
+ }
+ }
+
+ @android.annotation.UiThread
+ private void hideAllUiThread() {
+ hideFillUiUiThread();
+ hideSaveUiUiThread();
+ }
+
+ private boolean hasCallback() {
+ return mCallback != null;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
new file mode 100644
index 0000000..0d5fbbe
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import com.android.internal.R;
+import com.android.internal.R;
+import libcore.util.Objects;
+
+import java.util.ArrayList;
+
+final class FillUi {
+ private static final String TAG = "FillUi";
+
+ interface Callback {
+ void onResponsePicked(@NonNull FillResponse response);
+ void onDatasetPicked(@NonNull Dataset dataset);
+ void onCanceled();
+ }
+
+ private final Rect mAnchorBounds = new Rect();
+
+ private final @NonNull AnchoredWindow mWindow;
+
+ private final @NonNull Callback mCallback;
+
+ private final @Nullable ArrayAdapter<ViewItem> mAdapter;
+
+ private @Nullable String mFilterText;
+
+ private int mContentWidth;
+ private int mContentHeight;
+
+ private boolean mDestroyed;
+
+ FillUi(@NonNull Context context, @NonNull FillResponse response,
+ @NonNull AutoFillId focusedViewId, @NonNull IBinder windowToken,
+ @NonNull Rect anchorBounds, @Nullable String filterText,
+ @NonNull Callback callback) {
+ mAnchorBounds.set(anchorBounds);
+ mCallback = callback;
+
+ if (response.getAuthentication() != null) {
+ final View content;
+ try {
+ content = response.getPresentation().apply(context, null);
+ } catch (RuntimeException e) {
+ callback.onCanceled();
+ Slog.e(TAG, "Error inflating remote views", e);
+ mWindow = null;
+ mAdapter = null;
+ return;
+ }
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ content.measure(widthMeasureSpec, heightMeasureSpec);
+ content.setOnClickListener(v -> mCallback.onResponsePicked(response));
+ mContentWidth = content.getMeasuredWidth();
+ mContentHeight = content.getMeasuredHeight();
+ mAdapter = null;
+
+ mWindow = new AnchoredWindow(windowToken, content);
+ mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+ } else {
+ final int datasetCount = response.getDatasets().size();
+ final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
+ for (int i = 0; i < datasetCount; i++) {
+ final Dataset dataset = response.getDatasets().get(i);
+ final int index = dataset.getFieldIds().indexOf(focusedViewId);
+ if (index >= 0) {
+ AutoFillValue value = dataset.getFieldValues().get(index);
+ final View view;
+ try {
+ view = dataset.getPresentation().apply(context, null);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Error inflating remote views", e);
+ continue;
+ }
+ items.add(new ViewItem(dataset, value.coerceToString()
+ .toLowerCase(), view));
+ }
+ }
+
+ mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getItem(position).getView();
+ }
+ };
+
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ final ListView listView = (ListView) inflater.inflate(
+ com.android.internal.R.layout.autofill_dataset_picker, null);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener((adapter, view, position, id) -> {
+ final ViewItem vi = mAdapter.getItem(position);
+ mCallback.onDatasetPicked(vi.getDataset());
+ });
+
+ filter(filterText);
+ mWindow = new AnchoredWindow(windowToken, listView);
+ }
+ }
+
+ public void update(@NonNull Rect anchorBounds) {
+ throwIfDestroyed();
+ if (!mAnchorBounds.equals(anchorBounds)) {
+ mAnchorBounds.set(anchorBounds);
+ mWindow.update(mContentWidth, mContentHeight, anchorBounds);
+ }
+ }
+
+ public void filter(@Nullable String filterText) {
+ throwIfDestroyed();
+ if (mAdapter == null) {
+ return;
+ }
+ if (Objects.equal(mFilterText, filterText)) {
+ return;
+ }
+ mFilterText = filterText;
+ mAdapter.getFilter().filter(filterText, (count) -> {
+ if (mDestroyed) {
+ return;
+ }
+ if (count <= 0) {
+ mCallback.onCanceled();
+ } else {
+ if (updateContentSize()) {
+ mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+ }
+ }
+ });
+ }
+
+ public void destroy() {
+ throwIfDestroyed();
+ mWindow.destroy();
+ mDestroyed = true;
+ }
+
+ private boolean updateContentSize() {
+ if (mAdapter == null) {
+ return false;
+ }
+ boolean changed = false;
+ if (mAdapter.getCount() <= 0) {
+ if (mContentWidth != 0) {
+ mContentWidth = 0;
+ changed = true;
+ }
+ if (mContentHeight != 0) {
+ mContentHeight = 0;
+ changed = true;
+ }
+ return changed;
+ }
+
+ mContentWidth = 0;
+ mContentHeight = 0;
+
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ final int itemCount = mAdapter.getCount();
+ for (int i = 0; i < itemCount; i++) {
+ View view = mAdapter.getItem(i).getView();
+ view.measure(widthMeasureSpec, heightMeasureSpec);
+ final int newContentWidth = Math.max(mContentWidth, view.getMeasuredWidth());
+ if (newContentWidth != mContentWidth) {
+ mContentWidth = newContentWidth;
+ changed = true;
+ }
+ final int newContentHeight = mContentHeight + view.getMeasuredHeight();
+ if (newContentHeight != mContentHeight) {
+ mContentHeight = newContentHeight;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("cannot interact with a destroyed instance");
+ }
+ }
+
+ private static class ViewItem {
+ private final String mValue;
+ private final Dataset mDataset;
+ private final View mView;
+
+ ViewItem(Dataset dataset, String value, View view) {
+ mDataset = dataset;
+ mValue = value.toLowerCase();
+ mView = view;
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ public Dataset getDataset() {
+ return mDataset;
+ }
+
+ @Override
+ public String toString() {
+ // Used for filtering in the adapter
+ return mValue;
+ }
+ }
+
+ final class AnchoredWindow implements View.OnTouchListener {
+ private final Point mTempPoint = new Point();
+
+ private final WindowManager mWm;
+
+ private final IBinder mActivityToken;
+ private final View mContentView;
+
+ /**
+ * Constructor.
+ *
+ * @param activityToken token to pass to window manager
+ * @param contentView content of the window
+ */
+ AnchoredWindow(IBinder activityToken, View contentView) {
+ mWm = contentView.getContext().getSystemService(WindowManager.class);
+ mActivityToken = activityToken;
+ mContentView = contentView;
+ }
+
+ /**
+ * Hides the window.
+ */
+ void destroy() {
+ if (mContentView.isAttachedToWindow()) {
+ mContentView.setOnTouchListener(null);
+ mWm.removeView(mContentView);
+ }
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ // When the window is touched outside, hide the window.
+ if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mCallback.onCanceled();
+ return true;
+ }
+ return false;
+ }
+
+ public void update(int desiredWidth, int desiredHeight, Rect anchorBounds) {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.setTitle("FillUi");
+ params.token = mActivityToken;
+ params.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ params.format = PixelFormat.TRANSLUCENT;
+
+ mWm.getDefaultDisplay().getRealSize(mTempPoint);
+ final int screenWidth = mTempPoint.x;
+ final int screenHeight = mTempPoint.y;
+
+ // Try to place the window at the start of the anchor view if
+ // there is space to fit the content, otherwise fit as much of
+ // the window as possible moving it to the left using all available
+ // screen width.
+ params.x = Math.min(anchorBounds.left, Math.max(screenWidth - desiredWidth, 0));
+ params.width = Math.min(screenWidth, desiredWidth);
+
+ // Try to fit below using all available space with top-start gravity
+ // and if that fails try to fit above using all available space with
+ // bottom-start gravity.
+ final int verticalSpaceBelow = screenHeight - anchorBounds.bottom;
+ if (desiredHeight <= verticalSpaceBelow) {
+ // Fits below bounds.
+ params.height = desiredHeight;
+ params.gravity = Gravity.TOP | Gravity.START;
+ params.y = anchorBounds.bottom;
+ } else {
+ final int verticalSpaceAbove = anchorBounds.top;
+ if (desiredHeight <= verticalSpaceAbove) {
+ // Fits above bounds.
+ params.height = desiredHeight;
+ params.gravity = Gravity.BOTTOM | Gravity.START;
+ params.y = anchorBounds.top + desiredHeight;
+ } else {
+ // Pick above/below based on which has the most space.
+ if (verticalSpaceBelow >= verticalSpaceAbove) {
+ params.height = verticalSpaceBelow;
+ params.gravity = Gravity.TOP | Gravity.START;
+ params.y = anchorBounds.bottom;
+ } else {
+ params.height = verticalSpaceAbove;
+ params.gravity = Gravity.BOTTOM | Gravity.START;
+ params.y = anchorBounds.top + desiredHeight;
+ }
+ }
+ }
+
+ if (!mContentView.isAttachedToWindow()) {
+ mWm.addView(mContentView, params);
+ mContentView.setOnTouchListener(this);
+ } else {
+ mWm.updateViewLayout(mContentView, params);
+ }
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
new file mode 100644
index 0000000..b7215d6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.server.UiThread;
+
+/**
+ * Autofill Save Prompt
+ */
+final class SaveUi {
+ public interface OnSaveListener {
+ void onSave();
+ void onCancel();
+ }
+
+ private static final long LIFETIME_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+
+ private final Handler mHandler = UiThread.getHandler();
+
+ private final @NonNull Dialog mDialog;
+
+ private final @NonNull OnSaveListener mListener;
+
+ private boolean mDestroyed;
+
+ SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel,
+ @NonNull OnSaveListener listener) {
+ mListener = listener;
+
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ final View view = inflater.inflate(R.layout.autofill_save, null);
+
+ final TextView title = (TextView) view.findViewById(R.id.autofill_save_title);
+ title.setText(context.getString(R.string.autofill_save_title, providerLabel));
+
+ final View noButton = view.findViewById(R.id.autofill_save_no);
+ noButton.setOnClickListener((v) -> mListener.onCancel());
+
+ final View yesButton = view.findViewById(R.id.autofill_save_yes);
+ yesButton.setOnClickListener((v) -> mListener.onSave());
+
+ final View closeButton = view.findViewById(R.id.autofill_save_close);
+ closeButton.setOnClickListener((v) -> mListener.onCancel());
+
+ mDialog = new Dialog(context, R.style.Theme_Material_Panel);
+ mDialog.setContentView(view);
+
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
+ window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
+ window.setCloseOnTouchOutside(true);
+ window.getAttributes().width = WindowManager.LayoutParams.MATCH_PARENT;
+
+ mDialog.show();
+
+ mHandler.postDelayed(() -> mListener.onCancel(), LIFETIME_MILLIS);
+ }
+
+ void destroy() {
+ throwIfDestroyed();
+ mHandler.removeCallbacksAndMessages(mListener);
+ mDialog.dismiss();
+ mDestroyed = true;
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("cannot interact with a destroyed instance");
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b459f74..4f8b8af 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -38,15 +38,15 @@
import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
import android.app.backup.SelectBackupTransportCallback;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -136,6 +136,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -146,6 +147,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
@@ -327,6 +329,11 @@
final Object mClearDataLock = new Object();
volatile boolean mClearingData;
+ @GuardedBy("mPendingRestores")
+ private boolean mIsRestoreInProgress;
+ @GuardedBy("mPendingRestores")
+ private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
+
ActiveRestoreSession mActiveRestoreSession;
// Watch the device provisioning operation during setup
@@ -909,11 +916,28 @@
{
RestoreParams params = (RestoreParams)msg.obj;
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
- BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
+
+ PerformUnifiedRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
params.observer, params.monitor, params.token, params.pkgInfo,
params.pmToken, params.isSystemRestore, params.filterSet);
- Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
- sendMessage(restoreMsg);
+
+ synchronized (mPendingRestores) {
+ if (mIsRestoreInProgress) {
+ if (DEBUG) {
+ Slog.d(TAG, "Restore in progress, queueing.");
+ }
+ mPendingRestores.add(task);
+ // This task will be picked up and executed when the the currently running
+ // restore task finishes.
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting restore.");
+ }
+ mIsRestoreInProgress = true;
+ Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
+ sendMessage(restoreMsg);
+ }
+ }
break;
}
@@ -2658,19 +2682,32 @@
mStateDir = new File(mBaseStateDir, dirName);
mCurrentOpToken = generateToken();
- mCurrentState = BackupState.INITIAL;
mFinished = false;
- CountDownLatch latch = new CountDownLatch(1);
- String[] fullBackups =
- mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
- mFullBackupTask =
- new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
- fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, latch,
- mObserver, mMonitor,mUserInitiated);
+ synchronized (mCurrentOpLock) {
+ if (isBackupOperationInProgress()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Skipping backup since one is already in progress.");
+ }
+ mCancelAll = true;
+ mFullBackupTask = null;
+ mCurrentState = BackupState.FINAL;
+ addBackupTrace("Skipped. Backup already in progress.");
+ } else {
+ mCurrentState = BackupState.INITIAL;
+ CountDownLatch latch = new CountDownLatch(1);
+ String[] fullBackups =
+ mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
+ mFullBackupTask =
+ new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
+ fullBackups, /*updateSchedule*/ false, /*runningJob*/ null,
+ latch,
+ mObserver, mMonitor, mUserInitiated);
- registerTask();
- addBackupTrace("STATE => INITIAL");
+ registerTask();
+ addBackupTrace("STATE => INITIAL");
+ }
+ }
}
/**
@@ -3053,7 +3090,9 @@
mWakelock.acquire();
(new Thread(mFullBackupTask, "full-transport-requested")).start();
} else if (mCancelAll) {
- mFullBackupTask.unregisterTask();
+ if (mFullBackupTask != null) {
+ mFullBackupTask.unregisterTask();
+ }
sendBackupFinished(mObserver, BackupManager.ERROR_BACKUP_CANCELLED);
} else {
mFullBackupTask.unregisterTask();
@@ -3537,6 +3576,18 @@
}
}
+ private boolean isBackupOperationInProgress() {
+ synchronized (mCurrentOpLock) {
+ for (int i = 0; i < mCurrentOperations.size(); i++) {
+ Operation op = mCurrentOperations.valueAt(i);
+ if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
// ----- Full backup/restore to a file/socket -----
@@ -4519,6 +4570,14 @@
mCurrentOpToken = generateToken();
mBackupRunnerOpToken = generateToken();
+ if (isBackupOperationInProgress()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Skipping full backup. A backup is already in progress.");
+ }
+ mCancelAll = true;
+ return;
+ }
+
registerTask();
for (String pkg : whichPackages) {
@@ -9148,6 +9207,24 @@
// done; we can finally release the wakelock and be legitimately done.
Slog.i(TAG, "Restore complete.");
+
+ synchronized (mPendingRestores) {
+ if (mPendingRestores.size() > 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting next pending restore.");
+ }
+ PerformUnifiedRestoreTask task = mPendingRestores.remove();
+ mBackupHandler.sendMessage(
+ mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, task));
+
+ } else {
+ mIsRestoreInProgress = false;
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "No pending restores.");
+ }
+ }
+ }
+
mWakelock.release();
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index ef7780c..979096e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.annotation.NonNull;
import android.content.pm.PackageManagerInternal;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
@@ -104,6 +105,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -225,6 +227,12 @@
private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
+ private final ArrayMap<IGnssMeasurementsListener, Identity> mGnssMeasurementsListeners =
+ new ArrayMap<>();
+
+ private final ArrayMap<IGnssNavigationMessageListener, Identity>
+ mGnssNavigationMessageListeners = new ArrayMap<>();
+
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
@@ -315,17 +323,17 @@
boolean foreground = isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
synchronized (mLock) {
- for (Map.Entry<String, ArrayList<UpdateRecord>> entry
+ for (Entry<String, ArrayList<UpdateRecord>> entry
: mRecordsByProvider.entrySet()) {
String provider = entry.getKey();
for (UpdateRecord record : entry.getValue()) {
- if (record.mReceiver.mUid == uid
+ if (record.mReceiver.mIdentity.mUid == uid
&& record.mIsForegroundUid != foreground) {
if (D) Log.d(TAG, "request from uid " + uid + " is now "
+ (foreground ? "foreground" : "background)"));
record.mIsForegroundUid = foreground;
- if (!isThrottlingExemptLocked(record.mReceiver)) {
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
affectedProviders.add(provider);
}
}
@@ -334,6 +342,33 @@
for (String provider : affectedProviders) {
applyRequirementsLocked(provider);
}
+
+ for (Entry<IGnssMeasurementsListener, Identity> entry
+ : mGnssMeasurementsListeners.entrySet()) {
+ if (entry.getValue().mUid == uid) {
+ if (D) Log.d(TAG, "gnss measurements listener from uid " + uid
+ + " is now " + (foreground ? "foreground" : "background)"));
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssMeasurementsProvider.addListener(entry.getKey());
+ } else {
+ mGnssMeasurementsProvider.removeListener(entry.getKey());
+ }
+ }
+ }
+
+ for (Entry<IGnssNavigationMessageListener, Identity> entry
+ : mGnssNavigationMessageListeners.entrySet()) {
+ if (entry.getValue().mUid == uid) {
+ if (D) Log.d(TAG, "gnss navigation message listener from uid "
+ + uid + " is now "
+ + (foreground ? "foreground" : "background)"));
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssNavigationMessageProvider.addListener(entry.getKey());
+ } else {
+ mGnssNavigationMessageProvider.removeListener(entry.getKey());
+ }
+ }
+ }
}
}
@@ -344,7 +379,7 @@
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
updateUserProfiles(mCurrentUserId);
- updateThrottlingWhitelistLocked();
+ updateBackgroundThrottlingWhitelistLocked();
// prepare providers
loadProvidersLocked();
@@ -381,7 +416,7 @@
@Override
public void onChange(boolean selfChange) {
synchronized (mLock) {
- updateThrottlingWhitelistLocked();
+ updateBackgroundThrottlingWhitelistLocked();
updateProvidersLocked();
}
}
@@ -721,14 +756,24 @@
}
}
+ private static final class Identity {
+ final int mUid;
+ final int mPid;
+ final String mPackageName;
+
+ Identity(int uid, int pid, String packageName) {
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
+ }
+ }
+
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
- final int mUid; // uid of receiver
- final int mPid; // pid of receiver
- final String mPackageName; // package name of receiver
+ final Identity mIdentity;
final int mAllowedResolutionLevel; // resolution level allowed to receiver
final ILocationListener mListener;
@@ -756,9 +801,7 @@
mKey = intent;
}
mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
- mUid = uid;
- mPid = pid;
- mPackageName = packageName;
+ mIdentity = new Identity(uid, pid, packageName);
if (workSource != null && workSource.size() <= 0) {
workSource = null;
}
@@ -770,7 +813,7 @@
// construct/configure wakelock
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
if (workSource == null) {
- workSource = new WorkSource(mUid, mPackageName);
+ workSource = new WorkSource(mIdentity.mUid, mIdentity.mPackageName);
}
mWakeLock.setWorkSource(workSource);
}
@@ -865,13 +908,14 @@
int op) {
if (!currentlyMonitoring) {
if (allowMonitoring) {
- return mAppOps.startOpNoThrow(op, mUid, mPackageName)
+ return mAppOps.startOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
== AppOpsManager.MODE_ALLOWED;
}
} else {
- if (!allowMonitoring || mAppOps.checkOpNoThrow(op, mUid, mPackageName)
+ if (!allowMonitoring
+ || mAppOps.checkOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
!= AppOpsManager.MODE_ALLOWED) {
- mAppOps.finishOp(op, mUid, mPackageName);
+ mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
return false;
}
}
@@ -1628,7 +1672,7 @@
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
for (UpdateRecord record : records) {
- if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
@@ -1673,16 +1717,16 @@
if (records != null) {
for (UpdateRecord record : records) {
- if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
- record.mReceiver.mPid,
- record.mReceiver.mUid,
- record.mReceiver.mPackageName,
+ record.mReceiver.mIdentity.mPid,
+ record.mReceiver.mIdentity.mUid,
+ record.mReceiver.mIdentity.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
LocationRequest locationRequest = record.mRequest;
long interval = locationRequest.getInterval();
- if (!isThrottlingExemptLocked(record.mReceiver)) {
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
@@ -1709,7 +1753,7 @@
// under that threshold.
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
- if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
LocationRequest locationRequest = record.mRequest;
// Don't assign battery blame for update records whose
@@ -1728,8 +1772,8 @@
} else {
// Assign blame to caller.
worksource.add(
- record.mReceiver.mUid,
- record.mReceiver.mPackageName);
+ record.mReceiver.mIdentity.mUid,
+ record.mReceiver.mIdentity.mPackageName);
}
}
}
@@ -1741,7 +1785,15 @@
p.setRequest(providerRequest, worksource);
}
- private void updateThrottlingWhitelistLocked() {
+ @Override
+ public String[] getBackgroundThrottlingWhitelist() {
+ synchronized (mLock) {
+ return mBackgroundThrottlePackageWhitelist.toArray(
+ new String[mBackgroundThrottlePackageWhitelist.size()]);
+ }
+ }
+
+ private void updateBackgroundThrottlingWhitelistLocked() {
String setting = Settings.Global.getString(
mContext.getContentResolver(),
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -1756,17 +1808,17 @@
Arrays.asList(setting.split(",")));
}
- private boolean isThrottlingExemptLocked(Receiver receiver) {
- if (receiver.mUid == Process.SYSTEM_UID) {
+ private boolean isThrottlingExemptLocked(Identity identity) {
+ if (identity.mUid == Process.SYSTEM_UID) {
return true;
}
- if (mBackgroundThrottlePackageWhitelist.contains(receiver.mPackageName)) {
+ if (mBackgroundThrottlePackageWhitelist.contains(identity.mPackageName)) {
return true;
}
for (LocationProviderProxy provider : mProxyProviders) {
- if (receiver.mPackageName.equals(provider.getConnectedPackageName())) {
+ if (identity.mPackageName.equals(provider.getConnectedPackageName())) {
return true;
}
}
@@ -1790,7 +1842,7 @@
mRequest = request;
mReceiver = receiver;
mIsForegroundUid = isImportanceForeground(
- mActivityManager.getPackageImportance(mReceiver.mPackageName));
+ mActivityManager.getPackageImportance(mReceiver.mIdentity.mPackageName));
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -1803,14 +1855,14 @@
// Update statistics for historical location requests by package/provider
mRequestStatistics.startRequesting(
- mReceiver.mPackageName, provider, request.getInterval());
+ mReceiver.mIdentity.mPackageName, provider, request.getInterval());
}
/**
* Method to be called when a record will no longer be used.
*/
void disposeLocked(boolean removeReceiver) {
- mRequestStatistics.stopRequesting(mReceiver.mPackageName, mProvider);
+ mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
// remove from mRecordsByProvider
ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
@@ -1834,8 +1886,8 @@
@Override
public String toString() {
- return "UpdateRecord[" + mProvider + " " + mReceiver.mPackageName
- + "(" + mReceiver.mUid + (mIsForegroundUid ? " foreground" : " background")
+ return "UpdateRecord[" + mProvider + " " + mReceiver.mIdentity.mPackageName
+ + "(" + mReceiver.mIdentity.mUid + (mIsForegroundUid ? " foreground" : " background")
+ ")" + " " + mRequest + "]";
}
}
@@ -1994,7 +2046,8 @@
if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ " " + name + " " + request + " from " + packageName + "(" + uid + " "
+ (record.mIsForegroundUid ? "foreground" : "background")
- + (isThrottlingExemptLocked(receiver) ? " [whitelisted]" : "") + ")");
+ + (isThrottlingExemptLocked(receiver.mIdentity)
+ ? " [whitelisted]" : "") + ")");
UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
if (oldRecord != null) {
@@ -2227,13 +2280,33 @@
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
- return mGnssMeasurementsProvider.addListener(listener);
+
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ mGnssMeasurementsListeners.put(listener, callerIdentity);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (isThrottlingExemptLocked(callerIdentity)
+ || isImportanceForeground(
+ mActivityManager.getPackageImportance(packageName))) {
+ return mGnssMeasurementsProvider.addListener(listener);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return true;
+ }
}
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
if (mGnssMeasurementsProvider != null) {
- mGnssMeasurementsProvider.removeListener(listener);
+ synchronized (mLock) {
+ mGnssMeasurementsListeners.remove(listener);
+ mGnssMeasurementsProvider.removeListener(listener);
+ }
}
}
@@ -2244,13 +2317,33 @@
if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
return false;
}
- return mGnssNavigationMessageProvider.addListener(listener);
+
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ mGnssNavigationMessageListeners.put(listener, callerIdentity);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (isThrottlingExemptLocked(callerIdentity)
+ || isImportanceForeground(
+ mActivityManager.getPackageImportance(packageName))) {
+ return mGnssNavigationMessageProvider.addListener(listener);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return true;
+ }
}
@Override
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
if (mGnssNavigationMessageProvider != null) {
- mGnssNavigationMessageProvider.removeListener(listener);
+ synchronized (mLock) {
+ mGnssNavigationMessageListeners.remove(listener);
+ mGnssNavigationMessageProvider.removeListener(listener);
+ }
}
}
@@ -2529,26 +2622,30 @@
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- int receiverUserId = UserHandle.getUserId(receiver.mUid);
- if (!isCurrentProfile(receiverUserId) && !isUidALocationProvider(receiver.mUid)) {
+ int receiverUserId = UserHandle.getUserId(receiver.mIdentity.mUid);
+ if (!isCurrentProfile(receiverUserId)
+ && !isUidALocationProvider(receiver.mIdentity.mUid)) {
if (D) {
Log.d(TAG, "skipping loc update for background user " + receiverUserId +
" (current user: " + mCurrentUserId + ", app: " +
- receiver.mPackageName + ")");
+ receiver.mIdentity.mPackageName + ")");
}
continue;
}
- if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
+ if (mBlacklist.isBlacklisted(receiver.mIdentity.mPackageName)) {
if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
- receiver.mPackageName);
+ receiver.mIdentity.mPackageName);
continue;
}
- if (!reportLocationAccessNoThrow(receiver.mPid, receiver.mUid, receiver.mPackageName,
+ if (!reportLocationAccessNoThrow(
+ receiver.mIdentity.mPid,
+ receiver.mIdentity.mUid,
+ receiver.mIdentity.mPackageName,
receiver.mAllowedResolutionLevel)) {
if (D) Log.d(TAG, "skipping loc update for no op app: " +
- receiver.mPackageName);
+ receiver.mIdentity.mPackageName);
continue;
}
@@ -2671,7 +2768,7 @@
ArrayList<Receiver> deadReceivers = null;
for (Receiver receiver : mReceivers.values()) {
- if (receiver.mPackageName.equals(packageName)) {
+ if (receiver.mIdentity.mPackageName.equals(packageName)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b33538cb..d54ebaa 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -35,8 +35,7 @@
import android.net.INetworkScoreService;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
-import android.net.NetworkScorerAppManager;
-import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.NetworkScorerAppData;
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
@@ -132,10 +131,10 @@
* manages the service connection.
*/
private class NetworkScorerPackageMonitor extends PackageMonitor {
- final List<String> mPackagesToWatch;
+ final String mPackageToWatch;
- private NetworkScorerPackageMonitor(List<String> packagesToWatch) {
- mPackagesToWatch = packagesToWatch;
+ private NetworkScorerPackageMonitor(String packageToWatch) {
+ mPackageToWatch = packageToWatch;
}
@Override
@@ -168,37 +167,27 @@
evaluateBinding(packageName, true /* forceUnbind */);
}
- private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
- if (!mPackagesToWatch.contains(scorerPackageName)) {
+ private void evaluateBinding(String changedPackageName, boolean forceUnbind) {
+ if (!mPackageToWatch.equals(changedPackageName)) {
// Early exit when we don't care about the package that has changed.
return;
}
if (DBG) {
- Log.d(TAG, "Evaluating binding for: " + scorerPackageName
+ Log.d(TAG, "Evaluating binding for: " + changedPackageName
+ ", forceUnbind=" + forceUnbind);
}
+
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
if (activeScorer == null) {
// Package change has invalidated a scorer, this will also unbind any service
// connection.
if (DBG) Log.d(TAG, "No active scorers available.");
- unbindFromScoringServiceIfNeeded();
- } else if (activeScorer.getRecommendationServicePackageName().equals(scorerPackageName))
- {
- // The active scoring service changed in some way.
- if (DBG) {
- Log.d(TAG, "Possible change to the active scorer: "
- + activeScorer.getRecommendationServicePackageName());
- }
+ refreshBinding();
+ } else { // The scoring service changed in some way.
if (forceUnbind) {
unbindFromScoringServiceIfNeeded();
}
- bindToScoringServiceIfNeeded(activeScorer);
- } else {
- // One of the scoring apps on the device has changed and we may no longer be
- // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
- // will sort that out to leave us bound to the most recent active scorer.
if (DBG) {
Log.d(TAG, "Binding to " + activeScorer.getRecommendationServiceComponent()
+ " if needed.");
@@ -272,60 +261,71 @@
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
if (DBG) Log.d(TAG, "systemReady");
- registerPackageMonitorIfNeeded();
registerRecommendationSettingsObserver();
- refreshRecommendationRequestTimeoutMs();
}
/** Called when the system is ready for us to start third-party code. */
void systemRunning() {
if (DBG) Log.d(TAG, "systemRunning");
- bindToScoringServiceIfNeeded();
}
- private void onUserUnlocked(int userId) {
+ @VisibleForTesting
+ void onUserUnlocked(int userId) {
+ if (DBG) Log.d(TAG, "onUserUnlocked(" + userId + ")");
+ refreshBinding();
+ }
+
+ private void refreshBinding() {
+ if (DBG) Log.d(TAG, "refreshBinding()");
+ // Apply the default package name if the Setting isn't set.
+ mNetworkScorerAppManager.revertToDefaultIfNoActive();
registerPackageMonitorIfNeeded();
bindToScoringServiceIfNeeded();
}
private void registerRecommendationSettingsObserver() {
- final List<String> providerPackages =
- mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
- if (!providerPackages.isEmpty()) {
- final Uri enabledUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
- mContentObserver.observe(enabledUri,
- ServiceHandler.MSG_RECOMMENDATIONS_ENABLED_CHANGED);
- }
+ final Uri packageNameUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_PACKAGE);
+ mContentObserver.observe(packageNameUri,
+ ServiceHandler.MSG_RECOMMENDATIONS_PACKAGE_CHANGED);
final Uri timeoutUri = Global.getUriFor(Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
mContentObserver.observe(timeoutUri,
ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED);
}
+ /**
+ * Ensures the package manager is registered to monitor the current active scorer.
+ * If a discrepancy is found any previous monitor will be cleaned up
+ * and a new monitor will be created.
+ *
+ * This method is idempotent.
+ */
private void registerPackageMonitorIfNeeded() {
- if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
- final List<String> providerPackages =
- mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
+ if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded()");
+ final NetworkScorerAppData appData = mNetworkScorerAppManager.getActiveScorer();
synchronized (mPackageMonitorLock) {
// Unregister the current monitor if needed.
- if (mPackageMonitor != null) {
+ if (mPackageMonitor != null && (appData == null
+ || !appData.getRecommendationServicePackageName().equals(
+ mPackageMonitor.mPackageToWatch))) {
if (DBG) {
Log.d(TAG, "Unregistering package monitor for "
- + mPackageMonitor.mPackagesToWatch);
+ + mPackageMonitor.mPackageToWatch);
}
mPackageMonitor.unregister();
mPackageMonitor = null;
}
- // Create and register the monitor if there are packages that could be providers.
- if (!providerPackages.isEmpty()) {
- mPackageMonitor = new NetworkScorerPackageMonitor(providerPackages);
+ // Create and register the monitor if a scorer is active.
+ if (appData != null && mPackageMonitor == null) {
+ mPackageMonitor = new NetworkScorerPackageMonitor(
+ appData.getRecommendationServicePackageName());
// TODO: Need to update when we support per-user scorers. http://b/23422763
mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
false /* externalStorage */);
if (DBG) {
Log.d(TAG, "Registered package monitor for "
- + mPackageMonitor.mPackagesToWatch);
+ + mPackageMonitor.mPackageToWatch);
}
}
}
@@ -337,6 +337,13 @@
bindToScoringServiceIfNeeded(scorerData);
}
+ /**
+ * Ensures the service connection is bound to the current active scorer.
+ * If a discrepancy is found any previous connection will be cleaned up
+ * and a new connection will be created.
+ *
+ * This method is idempotent.
+ */
private void bindToScoringServiceIfNeeded(NetworkScorerAppData appData) {
if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + appData + ")");
if (appData != null) {
@@ -365,6 +372,8 @@
synchronized (mServiceConnectionLock) {
if (mServiceConnection != null) {
mServiceConnection.disconnect(mContext);
+ if (DBG) Log.d(TAG, "Disconnected from: "
+ + mServiceConnection.mAppData.getRecommendationServiceComponent());
}
mServiceConnection = null;
}
@@ -653,17 +662,13 @@
@Override
public boolean setActiveScorer(String packageName) {
- // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
- // to directly set the scorer app rather than having to use the consent dialog. The
- // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
- // do the right thing and not enable this feature without explaining it to the user.
- // In the future, should this API be opened to 3p apps, we will need to lock this down and
- // figure out another way to streamline the UX.
-
- mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
-
- // Scorers (recommendation providers) are selected and no longer set.
- return false;
+ // Only the system can set the active scorer
+ if (isCallerSystemProcess(getCallingUid()) || callerCanRequestScores()) {
+ return mNetworkScorerAppManager.setActiveScorer(packageName);
+ } else {
+ throw new SecurityException(
+ "Caller is neither the system process nor a score requester.");
+ }
}
/**
@@ -700,7 +705,6 @@
return null;
}
-
/**
* Returns metadata about the active scorer or <code>null</code> if there is no active scorer.
*/
@@ -727,7 +731,13 @@
*/
@Override
public List<NetworkScorerAppData> getAllValidScorers() {
- return mNetworkScorerAppManager.getAllValidScorers();
+ // Only the system can access this data.
+ if (isCallerSystemProcess(getCallingUid()) || callerCanRequestScores()) {
+ return mNetworkScorerAppManager.getAllValidScorers();
+ } else {
+ throw new SecurityException(
+ "Caller is neither the system process nor a score requester.");
+ }
}
@Override
@@ -1159,7 +1169,7 @@
@VisibleForTesting
public final class ServiceHandler extends Handler {
public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT = 1;
- public static final int MSG_RECOMMENDATIONS_ENABLED_CHANGED = 2;
+ public static final int MSG_RECOMMENDATIONS_PACKAGE_CHANGED = 2;
public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED = 3;
public ServiceHandler(Looper looper) {
@@ -1181,8 +1191,8 @@
sendDefaultRecommendationResponse(request, remoteCallback);
break;
- case MSG_RECOMMENDATIONS_ENABLED_CHANGED:
- bindToScoringServiceIfNeeded();
+ case MSG_RECOMMENDATIONS_PACKAGE_CHANGED:
+ refreshBinding();
break;
case MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED:
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
new file mode 100644
index 0000000..2f4485a
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.Manifest.permission;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.NetworkScoreManager;
+import android.net.NetworkScorerAppData;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Internal class for discovering and managing the network scorer/recommendation application.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class NetworkScorerAppManager {
+ private static final String TAG = "NetworkScorerAppManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private final Context mContext;
+ private final SettingsFacade mSettingsFacade;
+
+ public NetworkScorerAppManager(Context context) {
+ this(context, new SettingsFacade());
+ }
+
+ @VisibleForTesting
+ public NetworkScorerAppManager(Context context, SettingsFacade settingsFacade) {
+ mContext = context;
+ mSettingsFacade = settingsFacade;
+ }
+
+ /**
+ * Returns the list of available scorer apps. The list will be empty if there are
+ * no valid scorers.
+ */
+ @VisibleForTesting
+ public List<NetworkScorerAppData> getAllValidScorers() {
+ if (VERBOSE) Log.v(TAG, "getAllValidScorers()");
+ final PackageManager pm = mContext.getPackageManager();
+ final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
+ final List<ResolveInfo> resolveInfos =
+ pm.queryIntentServices(serviceIntent, PackageManager.GET_META_DATA);
+ if (resolveInfos == null || resolveInfos.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "Found 0 Services able to handle " + serviceIntent);
+ return Collections.emptyList();
+ }
+
+ List<NetworkScorerAppData> appDataList = new ArrayList<>();
+ for (int i = 0; i < resolveInfos.size(); i++) {
+ final ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
+ if (hasPermissions(serviceInfo.packageName)) {
+ if (VERBOSE) {
+ Log.v(TAG, serviceInfo.packageName + " is a valid scorer/recommender.");
+ }
+ final ComponentName serviceComponentName =
+ new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ final ComponentName useOpenWifiNetworksActivity =
+ findUseOpenWifiNetworksActivity(serviceInfo);
+ appDataList.add(
+ new NetworkScorerAppData(serviceInfo.applicationInfo.uid,
+ serviceComponentName, useOpenWifiNetworksActivity));
+ } else {
+ if (VERBOSE) Log.v(TAG, serviceInfo.packageName
+ + " is NOT a valid scorer/recommender.");
+ }
+ }
+
+ return appDataList;
+ }
+
+ @Nullable
+ private ComponentName findUseOpenWifiNetworksActivity(ServiceInfo serviceInfo) {
+ if (serviceInfo.metaData == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No metadata found on " + serviceInfo.getComponentName());
+ }
+ return null;
+ }
+ final String useOpenWifiPackage = serviceInfo.metaData
+ .getString(NetworkScoreManager.USE_OPEN_WIFI_PACKAGE_META_DATA);
+ if (TextUtils.isEmpty(useOpenWifiPackage)) {
+ if (DEBUG) {
+ Log.d(TAG, "No use_open_wifi_package metadata found on "
+ + serviceInfo.getComponentName());
+ }
+ return null;
+ }
+ final Intent enableUseOpenWifiIntent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE)
+ .setPackage(useOpenWifiPackage);
+ final ResolveInfo resolveActivityInfo = mContext.getPackageManager()
+ .resolveActivity(enableUseOpenWifiIntent, 0 /* flags */);
+ if (VERBOSE) {
+ Log.d(TAG, "Resolved " + enableUseOpenWifiIntent + " to " + resolveActivityInfo);
+ }
+
+ if (resolveActivityInfo != null && resolveActivityInfo.activityInfo != null) {
+ return resolveActivityInfo.activityInfo.getComponentName();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the application to use for scoring networks.
+ *
+ * @return the scorer app info or null if scoring is disabled (including if no scorer was ever
+ * selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
+ * it was disabled or uninstalled).
+ */
+ @Nullable
+ @VisibleForTesting
+ public NetworkScorerAppData getActiveScorer() {
+ return getScorer(getNetworkRecommendationsPackage());
+ }
+
+ private NetworkScorerAppData getScorer(String packageName) {
+ if (TextUtils.isEmpty(packageName)) {
+ return null;
+ }
+
+ // Otherwise return the recommendation provider (which may be null).
+ List<NetworkScorerAppData> apps = getAllValidScorers();
+ for (int i = 0; i < apps.size(); i++) {
+ NetworkScorerAppData app = apps.get(i);
+ if (app.getRecommendationServicePackageName().equals(packageName)) {
+ return app;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean hasPermissions(String packageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ return pm.checkPermission(permission.SCORE_NETWORKS, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Set the specified package as the default scorer application.
+ *
+ * <p>The caller must have permission to write to {@link Settings.Global}.
+ *
+ * @param packageName the packageName of the new scorer to use. If null, the scoring app will
+ * revert back to the configured default. Otherwise, the scorer will only
+ * be set if it is a valid scorer application.
+ * @return true if the scorer was changed, or false if the package is not a valid scorer or
+ * a valid network recommendation provider exists.
+ */
+ @VisibleForTesting
+ public boolean setActiveScorer(String packageName) {
+ String oldPackageName = getNetworkRecommendationsPackage();
+ if (TextUtils.equals(oldPackageName, packageName)) {
+ // No change.
+ return true;
+ }
+
+ Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
+
+ if (packageName == null) {
+ // revert to the default setting.
+ setNetworkRecommendationsPackage(getDefaultPackageSetting());
+ return true;
+ } else {
+ // We only make the change if the new package is valid.
+ if (getScorer(packageName) != null) {
+ setNetworkRecommendationsPackage(packageName);
+ return true;
+ } else {
+ Log.w(TAG, "Requested network scorer is not valid: " + packageName);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * If the active scorer is null then revert to the default scorer.
+ */
+ @VisibleForTesting
+ public void revertToDefaultIfNoActive() {
+ if (getActiveScorer() == null) {
+ final String defaultPackage = getDefaultPackageSetting();
+ setNetworkRecommendationsPackage(defaultPackage);
+ Log.i(TAG, "Defaulted the network recommendations app to: " + defaultPackage);
+ }
+ }
+
+ private String getDefaultPackageSetting() {
+ return mContext.getResources().getString(
+ R.string.config_defaultNetworkRecommendationProviderPackage);
+ }
+
+ private String getNetworkRecommendationsPackage() {
+ return mSettingsFacade.getString(mContext, Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE);
+ }
+
+ private void setNetworkRecommendationsPackage(String packageName) {
+ mSettingsFacade.putString(mContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, packageName);
+ }
+
+ /**
+ * Wrapper around Settings to make testing easier.
+ */
+ public static class SettingsFacade {
+ public boolean putString(Context context, String name, String value) {
+ return Settings.Global.putString(context.getContentResolver(), name, value);
+ }
+
+ public String getString(Context context, String name) {
+ return Settings.Global.getString(context.getContentResolver(), name);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 33351ff..480b08a 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -19,13 +19,9 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.BatteryManager;
-import android.os.BatteryProperties;
import android.os.Build;
-import android.os.IBatteryPropertiesListener;
-import android.os.IBatteryPropertiesRegistrar;
+import android.os.FileUtils;
import android.os.RecoverySystem;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -34,14 +30,12 @@
import android.text.format.DateUtils;
import android.util.ExceptionUtils;
import android.util.MathUtils;
-import android.util.MutableBoolean;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.ArrayUtils;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.io.File;
/**
* Utilities to help rescue the system from crash loops. Callers are expected to
@@ -325,30 +319,13 @@
/**
* Hacky test to check if the device has an active USB connection, which is
- * a good proxy for someone doing local development work. It uses a low
- * level call since we may not have started {@link BatteryManager} yet.
+ * a good proxy for someone doing local development work.
*/
private static boolean isUsbActive() {
- final MutableBoolean res = new MutableBoolean(false);
- final CountDownLatch latch = new CountDownLatch(1);
- final IBatteryPropertiesListener listener = new IBatteryPropertiesListener.Stub() {
- @Override
- public void batteryPropertiesChanged(BatteryProperties props) {
- res.value = props.chargerUsbOnline;
- latch.countDown();
- }
- };
-
try {
- final IBatteryPropertiesRegistrar bpr = IBatteryPropertiesRegistrar.Stub
- .asInterface(ServiceManager.getService("batteryproperties"));
- bpr.registerListener(listener);
- try {
- latch.await(5, TimeUnit.SECONDS);
- } finally {
- bpr.unregisterListener(listener);
- }
- return res.value;
+ final String state = FileUtils
+ .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
+ return "CONFIGURED".equals(state.trim());
} catch (Throwable t) {
Slog.w(TAG, "Failed to determine if device was on USB", t);
return false;
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index ce3166d..421d5a6 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.UserManager;
/**
* The base class for services running in the system process. Override and implement
@@ -133,9 +134,16 @@
public void onStartUser(int userHandle) {}
/**
- * Called when an existing user is unlocked. This means the
- * credential-encrypted storage for that user is now available, and
- * encryption-aware component filtering is no longer in effect.
+ * Called when an existing user is in the process of being unlocked. This
+ * means the credential-encrypted storage for that user is now available,
+ * and encryption-aware component filtering is no longer in effect.
+ * <p>
+ * While dispatching this event to services, the user is in the
+ * {@code STATE_RUNNING_UNLOCKING} state, and once dispatching is finished
+ * the user will transition into the {@code STATE_RUNNING_UNLOCKED} state.
+ * Code written inside system services should use
+ * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
+ * these states.
*
* @param userHandle The identifier of the user.
*/
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 3f97d4f..cb13a3d 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.Trace;
import android.util.Slog;
@@ -105,22 +106,25 @@
+ ": service constructor threw an exception", ex);
}
- // Register it.
- mServices.add(service);
-
- // Start it.
- try {
- service.onStart();
- } catch (RuntimeException ex) {
- throw new RuntimeException("Failed to start service " + name
- + ": onStart threw an exception", ex);
- }
+ startService(service);
return service;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
+ public void startService(@NonNull final SystemService service) {
+ // Register it.
+ mServices.add(service);
+ // Start it.
+ try {
+ service.onStart();
+ } catch (RuntimeException ex) {
+ throw new RuntimeException("Failed to start service " + service.getClass().getName()
+ + ": onStart threw an exception", ex);
+ }
+ }
+
/**
* Starts the specified boot phase for all system services that have been started up to
* this point.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a3326b..d287d85 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7694,7 +7694,7 @@
aspectRatio);
mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
bounds, true /* moveHomeStackToFront */);
- final ActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+ final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -7748,9 +7748,9 @@
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(args);
- final ActivityStack stack = r.getStack();
- if (stack.getStackId() == PINNED_STACK_ID) {
+ if (r.getStack().getStackId() == PINNED_STACK_ID) {
// If the activity is already in picture-in-picture, update the pinned stack now
+ final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio());
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
@@ -10312,7 +10312,7 @@
synchronized (this) {
if (animate) {
if (stackId == PINNED_STACK_ID) {
- final ActivityStack pinnedStack =
+ final PinnedActivityStack pinnedStack =
mStackSupervisor.getStack(PINNED_STACK_ID);
pinnedStack.animateResizePinnedStack(bounds, animationDuration);
} else {
@@ -10708,10 +10708,7 @@
} catch (RemoteException ignored) {
}
if (cpi == null) {
- // TODO: make this an outright failure in a future platform release;
- // until then anonymous content notifications are unprotected
- //return "Failed to find provider " + authority + " for user " + userId;
- return null;
+ return "Failed to find provider " + authority + " for user " + userId;
}
ProcessRecord r = null;
@@ -23165,10 +23162,11 @@
}
void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
+ final PackageManagerInternal packageManager = getPackageManagerInternalLocked();
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mLruProcesses.get(i);
- if (app.thread == null) {
+ if (app.thread == null || app.pid == Process.myPid()) {
continue;
}
@@ -23181,7 +23179,7 @@
final String packageName = app.pkgList.keyAt(j);
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
- final ApplicationInfo ai = mPackageManagerInt.getApplicationInfo(
+ final ApplicationInfo ai = packageManager.getApplicationInfo(
packageName, app.userId);
if (ai != null) {
app.thread.scheduleApplicationInfoChanged(ai);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6f89cf2..aef429e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -880,8 +880,8 @@
/**
* @return Stack value from current task, null if there is no task.
*/
- ActivityStack getStack() {
- return task != null ? task.getStack() : null;
+ <T extends ActivityStack> T getStack() {
+ return task != null ? (T) task.getStack() : null;
}
boolean changeWindowTranslucency(boolean toOpaque) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b948c15..7f7caff 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -84,7 +84,6 @@
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
-import android.app.RemoteAction;
import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.Intent;
@@ -135,7 +134,8 @@
/**
* State and management of a single stack of activities.
*/
-final class ActivityStack extends ConfigurationContainer implements StackWindowListener {
+class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
+ implements StackWindowListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -247,7 +247,7 @@
final ActivityManagerService mService;
private final WindowManagerService mWindowManager;
- private StackWindowController mWindowContainerController;
+ T mWindowContainerController;
private final RecentTasks mRecentTasks;
/**
@@ -462,14 +462,18 @@
? new LaunchingTaskPositioner() : null;
final ActivityStackSupervisor.ActivityDisplay display = mActivityContainer.mActivityDisplay;
mTmpRect2.setEmpty();
- mWindowContainerController = new StackWindowController(mStackId, this,
- display.mDisplayId, onTop, mTmpRect2);
+ mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
+ mTmpRect2);
activityContainer.mStack = this;
mStackSupervisor.mActivityContainers.put(mStackId, activityContainer);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}
- StackWindowController getWindowContainerController() {
+ T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+ return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+ }
+
+ T getWindowContainerController() {
return mWindowContainerController;
}
@@ -540,18 +544,6 @@
mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
}
- void animateResizePinnedStack(Rect bounds, int animationDuration) {
- mWindowContainerController.animateResizePinnedStack(bounds, animationDuration);
- }
-
- void setPictureInPictureAspectRatio(float aspectRatio) {
- mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
- }
-
- void setPictureInPictureActions(List<RemoteAction> actions) {
- mWindowContainerController.setPictureInPictureActions(actions);
- }
-
void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
boolean ignoreVisibility) {
mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7ceeff8..6f79bc27 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -120,7 +120,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -153,7 +152,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Slog;
@@ -1198,13 +1196,11 @@
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
- try {
- return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
+ synchronized (mService) {
+ return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
| ActivityManagerService.STOCK_PM_FLAGS, userId);
- } catch (RemoteException e) {
}
- return null;
}
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
@@ -2037,19 +2033,20 @@
|| mService.mSupportsFreeformWindowManagement;
}
- ActivityStack getStack(int stackId) {
+ protected <T extends ActivityStack> T getStack(int stackId) {
return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
}
- ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
+ protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
+ boolean createOnTop) {
final ActivityContainer activityContainer = mActivityContainers.get(stackId);
if (activityContainer != null) {
- return activityContainer.mStack;
+ return (T) activityContainer.mStack;
}
if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
return null;
}
- return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+ return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
}
/**
@@ -2856,7 +2853,7 @@
mWindowManager.deferSurfaceLayout();
// Need to make sure the pinned stack exist so we can resize it below...
- final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
try {
final TaskRecord task = r.task;
@@ -2906,7 +2903,7 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- stack.animateResizePinnedStack(bounds, -1);
+ stack.animateResizePinnedStack(bounds, -1 /* animationDuration */);
mService.mTaskChangeNotificationController.notifyActivityPinned();
}
@@ -4367,7 +4364,14 @@
synchronized (mService) {
mStackId = stackId;
mActivityDisplay = activityDisplay;
- new ActivityStack(this, mRecentTasks, onTop);
+ switch (mStackId) {
+ case PINNED_STACK_ID:
+ new PinnedActivityStack(this, mRecentTasks, onTop);
+ break;
+ default:
+ new ActivityStack(this, mRecentTasks, onTop);
+ break;
+ }
mIdString = "ActivtyContainer{" + mStackId + "}";
if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7605a1e..83d43db 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -82,6 +82,7 @@
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
@@ -98,7 +99,9 @@
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
@@ -456,22 +459,9 @@
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.ephemeralResponse != null) {
- final String packageName =
- rInfo.ephemeralResponse.resolveInfo.getPackageName();
- final String splitName = rInfo.ephemeralResponse.splitName;
- final boolean needsPhaseTwo = rInfo.ephemeralResponse.needsPhase2;
- final String token = rInfo.ephemeralResponse.token;
- final int versionCode = rInfo.ephemeralResponse.resolveInfo.getVersionCode();
- if (needsPhaseTwo) {
- // request phase two resolution
- mService.getPackageManagerInternalLocked().requestEphemeralResolutionPhaseTwo(
- rInfo.ephemeralResponse, ephemeralIntent, resolvedType, intent,
- callingPackage, userId);
- }
- intent = EphemeralResolver.buildEphemeralInstallerIntent(intent, ephemeralIntent,
- callingPackage, resolvedType, userId, packageName, splitName, versionCode,
- token, needsPhaseTwo);
+ if (rInfo != null && rInfo.auxiliaryInfo != null) {
+ intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
+ callingPackage, resolvedType, userId);
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
@@ -530,6 +520,21 @@
return err;
}
+ /** Creates a launch intent for the given auxiliary resolution data. */
+ private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse,
+ Intent originalIntent, String callingPackage,
+ String resolvedType, int userId) {
+ if (auxiliaryResponse.needsPhaseTwo) {
+ // request phase two resolution
+ mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
+ auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId);
+ }
+ return EphemeralResolver.buildEphemeralInstallerIntent(originalIntent,
+ callingPackage, resolvedType, userId, auxiliaryResponse.packageName,
+ auxiliaryResponse.splitName, auxiliaryResponse.versionCode,
+ auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
+ }
+
void postStartActivityUncheckedProcessing(
ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
ActivityStack targetStack) {
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
new file mode 100644
index 0000000..aa7ab15
--- /dev/null
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.StackWindowController;
+
+import java.util.List;
+
+/**
+ * State and management of the pinned stack of activities.
+ */
+class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
+
+ PinnedActivityStack(ActivityContainer activityContainer,
+ RecentTasks recentTasks, boolean onTop) {
+ super(activityContainer, recentTasks, onTop);
+ }
+
+ @Override
+ PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
+ Rect outBounds) {
+ return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+ }
+
+ void animateResizePinnedStack(Rect bounds, int animationDuration) {
+ getWindowContainerController().animateResizePinnedStack(bounds, animationDuration);
+ }
+
+ void setPictureInPictureAspectRatio(float aspectRatio) {
+ getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
+ }
+
+ void setPictureInPictureActions(List<RemoteAction> actions) {
+ getWindowContainerController().setPictureInPictureActions(actions);
+ }
+}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e8ff510..d648dd82 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -51,14 +51,36 @@
private final static boolean DEBUG = false;
private final static int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
- private ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
+ private final VolumeShaper.Configuration DUCK_VSHAPE =
+ new VolumeShaper.Configuration.Builder()
+ .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
+ .setCurve(new float[] { 0.f, 1.f } /* times */,
+ new float[] { 1.f, 0.2f } /* volumes */)
+ .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
+ .setDurationMs(MediaFocusControl.getFocusRampTimeMs(
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
+ .build()))
+ .build();
+ private final VolumeShaper.Configuration DUCK_ID =
+ new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID);
+ private final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
+ new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
+ .createIfNeeded()
+ .build();
+ private final VolumeShaper.Operation TERMINATE =
+ new VolumeShaper.Operation.Builder()
+ .terminate()
+ .build();
+
+ private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
// a public client is one that needs an anonymized version of the playback configurations, we
// keep track of whether there is at least one to know when we need to create the list of
// playback configurations that do not contain uid/pid/package name information.
private boolean mHasPublicClients = false;
private final Object mPlayerLock = new Object();
- private HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
+ private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
new HashMap<Integer, AudioPlaybackConfiguration>();
PlaybackActivityMonitor() {
@@ -130,12 +152,10 @@
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_ID,
+ TERMINATE);
mPlayers.remove(new Integer(piid));
- final VolumeShaper vs = mDuckVolumeShapers.get(new Integer(piid));
- if (vs != null) {
- vs.release();
- mDuckVolumeShapers.remove(new Integer(piid));
- }
} else {
Log.e(TAG, "Error releasing player " + piid);
}
@@ -252,20 +272,6 @@
private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
- private final VolumeShaper.Configuration DUCK_VSHAPE =
- new VolumeShaper.Configuration.Builder()
- .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
- .setCurve(new float[] { 0.f, 1.f } /* times */,
- new float[] { 1.f, 0.2f } /* volumes */)
- .setDurationMs(MediaFocusControl.getFocusRampTimeMs(
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
- new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
- .build()))
- .build();
-
- private final HashMap<Integer, VolumeShaper> mDuckVolumeShapers =
- new HashMap<Integer, VolumeShaper>();
-
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
if (DEBUG) {
@@ -302,17 +308,9 @@
} else {
try {
if (DEBUG) { Log.v(TAG, "ducking player " + piid); }
- final VolumeShaper ducker;
- if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
- ducker = mDuckVolumeShapers.get(new Integer(piid));
- } else {
- ducker = new VolumeShaper(
- DUCK_VSHAPE,
- apc.getPlayerProxy(),
- true /* keepReference */);
- mDuckVolumeShapers.put(new Integer(piid), ducker);
- }
- ducker.apply(VolumeShaper.Operation.PLAY); // duck
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_VSHAPE,
+ PLAY_CREATE_IF_NEEDED);
mDuckedPlayers.add(piid);
} catch (Exception e) {
Log.e(TAG, "Error ducking player " + piid, e);
@@ -341,10 +339,9 @@
try {
if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
mDuckedPlayers.remove(new Integer(piid));
- if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
- final VolumeShaper ducker = mDuckVolumeShapers.get(new Integer(piid));
- ducker.apply(VolumeShaper.Operation.REVERSE); // unduck
- }
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_ID,
+ VolumeShaper.Operation.REVERSE);
} catch (Exception e) {
Log.e(TAG, "Error unducking player " + piid, e);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 63024db..6106093 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -20,6 +20,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
@@ -72,6 +74,7 @@
private final Context mContext;
private final StateMachine mTarget;
+ private final Handler mHandler;
private final int mWhat;
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
private ConnectivityManager mCM;
@@ -84,6 +87,7 @@
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
mContext = ctx;
mTarget = tgt;
+ mHandler = mTarget.getHandler();
mWhat = what;
}
@@ -99,10 +103,10 @@
final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
.clearCapabilities().build();
mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
- cm().registerNetworkCallback(listenAllRequest, mListenAllCallback);
+ cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
- cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
}
public void stop() {
@@ -154,7 +158,7 @@
// Additionally, we log a message to aid in any subsequent debugging.
Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
- cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType);
+ cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
}
public void releaseMobileNetworkRequest() {
@@ -185,11 +189,13 @@
case CALLBACK_TRACK_DEFAULT:
if (mDefaultNetworkCallback == null) {
// The callback was unregistered in the interval between
- // ConnectivityService calling onAvailable() and our
- // handling of it here on the mTarget.getHandler() thread.
+ // ConnectivityService enqueueing onAvailable() and our
+ // handling of it here on the mHandler thread.
+ //
// Clean-up of this network entry is deferred to the
// handling of onLost() by other callbacks.
- // TODO: change to Log.wtf() after oag/331764 is merged.
+ //
+ // These request*() calls can be deleted post oag/339444.
return;
}
@@ -200,11 +206,13 @@
case CALLBACK_MOBILE_REQUEST:
if (mMobileNetworkCallback == null) {
// The callback was unregistered in the interval between
- // ConnectivityService calling onAvailable() and our
- // handling of it here on the mTarget.getHandler() thread.
+ // ConnectivityService enqueueing onAvailable() and our
+ // handling of it here on the mHandler thread.
+ //
// Clean-up of this network entry is deferred to the
// handling of onLost() by other callbacks.
- // TODO: change to Log.wtf() after oag/331764 is merged.
+ //
+ // These request*() calls can be deleted post oag/339444.
return;
}
@@ -312,8 +320,9 @@
}
/**
- * A NetworkCallback class that relays information of interest to the
- * tethering master state machine thread for subsequent processing.
+ * A NetworkCallback class that handles information of interest directly
+ * in the thread on which it is invoked. To avoid locking, this MUST be
+ * run on the same thread as the target state machine's handler.
*/
private class UpstreamNetworkCallback extends NetworkCallback {
private final int mCallbackType;
@@ -324,22 +333,35 @@
@Override
public void onAvailable(Network network) {
- mTarget.getHandler().post(() -> handleAvailable(mCallbackType, network));
+ checkExpectedThread();
+ handleAvailable(mCallbackType, network);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- mTarget.getHandler().post(() -> handleNetCap(network, newNc));
+ checkExpectedThread();
+ handleNetCap(network, newNc);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- mTarget.getHandler().post(() -> handleLinkProp(network, newLp));
+ checkExpectedThread();
+ handleLinkProp(network, newLp);
}
+ // TODO: Handle onNetworkSuspended();
+ // TODO: Handle onNetworkResumed();
+
@Override
public void onLost(Network network) {
- mTarget.getHandler().post(() -> handleLost(mCallbackType, network));
+ checkExpectedThread();
+ handleLost(mCallbackType, network);
+ }
+
+ private void checkExpectedThread() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ Log.wtf(TAG, "Handling callback in unexpected thread.");
+ }
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 886c97f..4b34eba56 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -43,6 +43,7 @@
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.FactoryTest;
import android.os.IBinder;
@@ -287,7 +288,7 @@
*/
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle) {
+ IContentObserver observer, int userHandle, int targetSdkVersion) {
if (observer == null || uri == null) {
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
@@ -301,8 +302,17 @@
final String msg = LocalServices.getService(ActivityManagerInternal.class)
.checkContentProviderAccess(uri.getAuthority(), userHandle);
if (msg != null) {
- Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
- return;
+ if (targetSdkVersion >= Build.VERSION_CODES.O) {
+ throw new SecurityException(msg);
+ } else {
+ if (msg.startsWith("Failed to find provider")) {
+ // Sigh, we need to quietly let apps targeting older API
+ // levels notify on non-existent providers.
+ } else {
+ Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
+ return;
+ }
+ }
}
synchronized (mRootNode) {
@@ -316,7 +326,7 @@
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
IContentObserver observer) {
registerContentObserver(uri, notifyForDescendants, observer,
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT);
}
@Override
@@ -340,8 +350,8 @@
*/
@Override
public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, int flags,
- int userHandle) {
+ boolean observerWantsSelfNotifications, int flags, int userHandle,
+ int targetSdkVersion) {
if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
+ " from observer " + observer + ", flags " + Integer.toHexString(flags));
@@ -359,8 +369,17 @@
final String msg = LocalServices.getService(ActivityManagerInternal.class)
.checkContentProviderAccess(uri.getAuthority(), userHandle);
if (msg != null) {
- Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg);
- return;
+ if (targetSdkVersion >= Build.VERSION_CODES.O) {
+ throw new SecurityException(msg);
+ } else {
+ if (msg.startsWith("Failed to find provider")) {
+ // Sigh, we need to quietly let apps targeting older API
+ // levels notify on non-existent providers.
+ } else {
+ Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg);
+ return;
+ }
+ }
}
// This makes it so that future permission checks will be in the context of this
@@ -427,7 +446,7 @@
boolean observerWantsSelfNotifications, boolean syncToNetwork) {
notifyChange(uri, observer, observerWantsSelfNotifications,
syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT);
}
/**
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 74e025d..5149933 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -149,13 +149,7 @@
}
private void handleBinderDiedLocked(IBinder appToken) {
- VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
- if (device != null) {
- Slog.i(TAG, "Virtual display device released because application token died: "
- + device.mOwnerPackageName);
- device.destroyLocked(false);
- sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
- }
+ mVirtualDisplayDevices.remove(appToken);
}
private void handleMediaProjectionStoppedLocked(IBinder appToken) {
@@ -216,6 +210,10 @@
public void binderDied() {
synchronized (getSyncRoot()) {
handleBinderDiedLocked(mAppToken);
+ Slog.i(TAG, "Virtual display device released because application token died: "
+ + mOwnerPackageName);
+ destroyLocked(false);
+ sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e83b228..0f29942 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -1180,7 +1180,7 @@
// Statistics about secure fingerprint transactions (e.g. to unlock password
// storage, make secure purchases, etc.)
- final PerformanceStats crypto = mPerformanceMap.get(userId);
+ final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
if (crypto != null) {
final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
proto.write(FingerprintActionStatsProto.ACCEPT, crypto.accept);
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index 4795fbf..e6edaf1 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -46,7 +46,9 @@
if (!appCanShowBadge) {
record.setShowBadge(false);
} else {
- record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+ record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
+ record.sbn.getUid(), record.getChannel().getId(), false).canShowBadge()
+ && appCanShowBadge);
}
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8f4ad00..45ff20b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2029,11 +2029,11 @@
*/
@Override
public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
- long snoozeUntil) {
+ long duration) {
long identity = Binder.clearCallingIdentity();
try {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- snoozeNotificationInt(key, snoozeUntil, null, info);
+ snoozeNotificationInt(key, duration, null, info);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3409,7 +3409,7 @@
@VisibleForTesting
void scheduleTimeoutLocked(NotificationRecord record) {
- if (record.getNotification().getTimeout() > System.currentTimeMillis()) {
+ if (record.getNotification().getTimeout() > 0) {
final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
REQUEST_CODE_TIMEOUT,
new Intent(ACTION_NOTIFICATION_TIMEOUT)
@@ -3418,8 +3418,8 @@
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_KEY, record.getKey()),
PendingIntent.FLAG_UPDATE_CURRENT);
- mAlarmManager.setExactAndAllowWhileIdle(
- AlarmManager.RTC_WAKEUP, record.getNotification().getTimeout(), pi);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + record.getNotification().getTimeout(), pi);
}
}
@@ -4185,16 +4185,16 @@
}
}
- void snoozeNotificationInt(String key, long until, String snoozeCriterionId,
+ void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
ManagedServiceInfo listener) {
String listenerName = listener == null ? null : listener.component.toShortString();
- if (until < System.currentTimeMillis() && snoozeCriterionId == null) {
+ if (duration <= 0 && snoozeCriterionId == null) {
return;
}
if (DBG) {
- Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, until, snoozeCriterionId,
- listenerName));
+ Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
+ snoozeCriterionId, listenerName));
}
// Needs to post so that it can cancel notifications not yet enqueued.
mHandler.post(new Runnable() {
@@ -4215,7 +4215,7 @@
snoozeCriterionId);
mSnoozeHelper.snooze(r);
} else {
- mSnoozeHelper.snooze(r, until);
+ mSnoozeHelper.snooze(r, duration);
}
savePolicyFile();
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 0cd8cea..913f636 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -32,6 +32,7 @@
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Binder;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
@@ -125,9 +126,9 @@
/**
* Snoozes a notification and schedules an alarm to repost at that time.
*/
- protected void snooze(NotificationRecord record, long until) {
+ protected void snooze(NotificationRecord record, long duration) {
snooze(record);
- scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), until);
+ scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
}
/**
@@ -291,13 +292,14 @@
PendingIntent.FLAG_UPDATE_CURRENT);
}
- private void scheduleRepost(String pkg, String key, int userId, long time) {
+ private void scheduleRepost(String pkg, String key, int userId, long duration) {
long identity = Binder.clearCallingIdentity();
try {
final PendingIntent pi = createPendingIntent(pkg, key, userId);
mAm.cancel(pi);
+ long time = SystemClock.elapsedRealtime() + duration;
if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
- mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
+ mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index cc709ce..ba4d46a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -26,7 +26,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -42,7 +41,6 @@
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -646,7 +644,7 @@
Slog.d(TAG, String.format("send broadcast %s", intent));
}
try {
- ActivityManagerNative.getDefault().broadcastIntent(null, intent, null, null, 0,
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
userId);
} catch (RemoteException e) {
@@ -664,7 +662,38 @@
}
private void updateAssets(final int userId, List<String> targetPackageNames) {
- // TODO: implement when we integrate OMS properly
+ final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ final boolean updateFrameworkRes = targetPackageNames.contains("android");
+ if (updateFrameworkRes) {
+ targetPackageNames = pm.getTargetPackageNames(userId);
+ }
+
+ final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
+ synchronized (mLock) {
+ final int N = targetPackageNames.size();
+ for (int i = 0; i < N; i++) {
+ final String targetPackageName = targetPackageNames.get(i);
+ pendingChanges.put(targetPackageName,
+ mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ }
+ }
+
+ final int N = targetPackageNames.size();
+ for (int i = 0; i < N; i++) {
+ final String targetPackageName = targetPackageNames.get(i);
+ if (!pm.setEnabledOverlayPackages(
+ userId, targetPackageName, pendingChanges.get(targetPackageName))) {
+ Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+ targetPackageName, userId));
+ }
+ }
+
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
}
private void schedulePersistSettings() {
diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java
index d99a1b6..7bc65f9 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolver.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolver.java
@@ -16,8 +16,9 @@
package com.android.server.pm;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -29,7 +30,7 @@
import android.content.pm.EphemeralIntentFilter;
import android.content.pm.EphemeralRequest;
import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.EphemeralResponse;
+import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
import android.os.Binder;
import android.os.Handler;
@@ -46,7 +47,7 @@
/** @hide */
public abstract class EphemeralResolver {
- public static EphemeralResponse doEphemeralResolutionPhaseOne(Context context,
+ public static AuxiliaryResolveInfo doEphemeralResolutionPhaseOne(Context context,
EphemeralResolverConnection connection, EphemeralRequest requestObj) {
final Intent intent = requestObj.origIntent;
final EphemeralDigest digest =
@@ -83,7 +84,7 @@
final ArrayList<EphemeralResolveInfo> ephemeralResolveInfoList =
new ArrayList<EphemeralResolveInfo>(1);
ephemeralResolveInfoList.add(ephemeralResolveInfo);
- final EphemeralResponse ephemeralIntentInfo =
+ final AuxiliaryResolveInfo ephemeralIntentInfo =
EphemeralResolver.filterEphemeralIntent(
ephemeralResolveInfoList, intent, null /*resolvedType*/,
0 /*userId*/, intent.getPackage(), digest,
@@ -104,7 +105,6 @@
versionCode = -1;
}
final Intent installerIntent = buildEphemeralInstallerIntent(
- requestObj.launchIntent,
requestObj.origIntent,
requestObj.callingPackage,
requestObj.resolvedType,
@@ -126,35 +126,41 @@
/**
* Builds and returns an intent to launch the ephemeral installer.
*/
- public static Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
- String callingPackage, String resolvedType, int userId, String ephemeralPackageName,
- String ephemeralSplitName, int versionCode, String token, boolean needsPhaseTwo) {
+ public static Intent buildEphemeralInstallerIntent(@NonNull Intent origIntent,
+ @NonNull String callingPackage,
+ @NonNull String resolvedType,
+ int userId,
+ @NonNull String ephemeralPackageName,
+ @Nullable String ephemeralSplitName,
+ int versionCode,
+ @Nullable String token,
+ boolean needsPhaseTwo) {
// Construct the intent that launches the ephemeral installer
- int flags = launchIntent.getFlags();
+ int flags = origIntent.getFlags();
final Intent intent = new Intent();
intent.setFlags(flags
| Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- // TODO: Remove when the platform has fully implemented ephemeral apps
- intent.setData(origIntent.getData().buildUpon().clearQuery().build());
- intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
- intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
+ if (token != null) {
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
+ }
+ if (origIntent.getData() != null) {
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
+ }
+ // We have all of the data we need; just start the installer without a second phase
if (!needsPhaseTwo) {
- // We have all of the data we need; just start the installer without a second phase
- final Intent nonEphemeralIntent = new Intent(origIntent);
- nonEphemeralIntent.setFlags(
- nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
- // Intent that is launched if the ephemeral package couldn't be installed
- // for any reason.
+ // Intent that is launched if the package couldn't be installed for any reason.
+ final Intent failureIntent = new Intent(origIntent);
+ failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
try {
- final IIntentSender failureIntentTarget = ActivityManagerNative.getDefault()
+ final IIntentSender failureIntentTarget = ActivityManager.getService()
.getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
- new Intent[] { nonEphemeralIntent },
+ new Intent[] { failureIntent },
new String[] { resolvedType },
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE,
@@ -163,19 +169,14 @@
new IntentSender(failureIntentTarget));
} catch (RemoteException ignore) { /* ignore; same process */ }
- // Success intent goes back to the installer
- final Intent ephemeralIntent = new Intent(launchIntent)
- .setComponent(null)
- .setPackage(ephemeralPackageName);
- // Intent that is eventually launched if the ephemeral package was
- // installed successfully. This will actually be launched by a platform
- // broadcast receiver.
+ // Intent that is launched if the package was installed successfully.
+ final Intent successIntent = new Intent(origIntent);
try {
- final IIntentSender successIntentTarget = ActivityManagerNative.getDefault()
+ final IIntentSender successIntentTarget = ActivityManager.getService()
.getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
null /*token*/, null /*resultWho*/, 0 /*requestCode*/,
- new Intent[] { ephemeralIntent },
+ new Intent[] { successIntent },
new String[] { resolvedType },
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE,
@@ -192,7 +193,7 @@
return intent;
}
- private static EphemeralResponse filterEphemeralIntent(
+ private static AuxiliaryResolveInfo filterEphemeralIntent(
List<EphemeralResolveInfo> ephemeralResolveInfoList,
Intent intent, String resolvedType, int userId, String packageName,
EphemeralDigest digest, String token) {
@@ -212,7 +213,7 @@
ephemeralInfo.getIntentFilters();
// No filters; we need to start phase two
if (ephemeralFilters == null || ephemeralFilters.isEmpty()) {
- return new EphemeralResponse(ephemeralInfo,
+ return new AuxiliaryResolveInfo(ephemeralInfo,
new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/,
null /*splitName*/, token, true /*needsPhase2*/);
}
@@ -226,14 +227,14 @@
continue;
}
for (int k = splitFilters.size() - 1; k >= 0; --k) {
- final EphemeralResponse intentInfo =
- new EphemeralResponse(ephemeralInfo,
+ final AuxiliaryResolveInfo intentInfo =
+ new AuxiliaryResolveInfo(ephemeralInfo,
splitFilters.get(k), ephemeralFilter.getSplitName(),
token, false /*needsPhase2*/);
ephemeralResolver.addFilter(intentInfo);
}
}
- List<EphemeralResponse> matchedResolveInfoList = ephemeralResolver.queryIntent(
+ List<AuxiliaryResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
intent, resolvedType, false /*defaultOnly*/, userId);
if (!matchedResolveInfoList.isEmpty()) {
return matchedResolveInfoList.get(0);
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 2b6ce10..7f5973e 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.EphemeralResponse;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 96a2577..c11131a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -242,6 +242,12 @@
try {
UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
if (callingUserInfo.isManagedProfile()) {
+
+ // STOPSHIP Remove the whitelist.
+ if ("com.google.android.talk".equals(callingPackage)
+ || "com.google.android.quicksearchbox".equals(callingPackage)) {
+ return false;
+ }
Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile "
+ targetUserId + " from " + callingUserId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 53765f2..a7a1683 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -607,6 +607,12 @@
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
+ if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
+ || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ throw new IllegalArgumentException(
+ "New installs into ASEC containers no longer supported");
+ }
+
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 975ae06..17045b4b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -128,10 +128,9 @@
import android.content.pm.AppsQueryHelper;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
-import android.content.pm.InstantAppInfo;
import android.content.pm.EphemeralRequest;
import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.EphemeralResponse;
+import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
@@ -143,6 +142,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
@@ -204,15 +204,16 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
-import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
+import android.service.pm.PackageServiceDumpProto;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -235,6 +236,7 @@
import android.util.SparseIntArray;
import android.util.Xml;
import android.util.jar.StrictJarFile;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.R;
@@ -316,8 +318,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -661,9 +663,12 @@
final ArrayMap<String, Set<String>> mKnownCodebase =
new ArrayMap<String, Set<String>>();
- // Tracks available target package names -> overlay package paths.
- final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays =
- new ArrayMap<String, ArrayMap<String, PackageParser.Package>>();
+ // List of APK paths to load for each user and package. This data is never
+ // persisted by the package manager. Instead, the overlay manager will
+ // ensure the data is up-to-date in runtime.
+ @GuardedBy("mPackages")
+ final SparseArray<ArrayMap<String, ArrayList<String>>> mEnabledOverlayPaths =
+ new SparseArray<ArrayMap<String, ArrayList<String>>>();
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -837,12 +842,12 @@
private int mIntentFilterVerificationToken = 0;
/** The service connection to the ephemeral resolver */
- final EphemeralResolverConnection mEphemeralResolverConnection;
+ final EphemeralResolverConnection mInstantAppResolverConnection;
/** Component used to install ephemeral applications */
- ComponentName mEphemeralInstallerComponent;
- final ActivityInfo mEphemeralInstallerActivity = new ActivityInfo();
- final ResolveInfo mEphemeralInstallerInfo = new ResolveInfo();
+ ComponentName mInstantAppInstallerComponent;
+ final ActivityInfo mInstantAppInstallerActivity = new ActivityInfo();
+ final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
@@ -1164,7 +1169,7 @@
static final int START_INTENT_FILTER_VERIFICATIONS = 17;
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
- static final int EPHEMERAL_RESOLUTION_PHASE_TWO = 20;
+ static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1735,11 +1740,11 @@
break;
}
- case EPHEMERAL_RESOLUTION_PHASE_TWO: {
+ case INSTANT_APP_RESOLUTION_PHASE_TWO: {
EphemeralResolver.doEphemeralResolutionPhaseTwo(mContext,
- mEphemeralResolverConnection,
+ mInstantAppResolverConnection,
(EphemeralRequest) msg.obj,
- mEphemeralInstallerActivity,
+ mInstantAppInstallerActivity,
mHandler);
}
}
@@ -2890,17 +2895,17 @@
if (DEBUG_EPHEMERAL) {
Slog.i(TAG, "Ephemeral resolver: " + ephemeralResolverComponent);
}
- mEphemeralResolverConnection =
+ mInstantAppResolverConnection =
new EphemeralResolverConnection(mContext, ephemeralResolverComponent);
} else {
- mEphemeralResolverConnection = null;
+ mInstantAppResolverConnection = null;
}
- mEphemeralInstallerComponent = getEphemeralInstallerLPr();
- if (mEphemeralInstallerComponent != null) {
+ mInstantAppInstallerComponent = getEphemeralInstallerLPr();
+ if (mInstantAppInstallerComponent != null) {
if (DEBUG_EPHEMERAL) {
- Slog.i(TAG, "Ephemeral installer: " + mEphemeralInstallerComponent);
+ Slog.i(TAG, "Ephemeral installer: " + mInstantAppInstallerComponent);
}
- setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
+ setUpInstantAppInstallerActivityLP(mInstantAppInstallerComponent);
}
// Read and update the usage of dex files.
@@ -3734,6 +3739,7 @@
ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
ps.readUserState(userId), userId);
if (ai != null) {
+ rebaseEnabledOverlays(ai, userId);
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
return ai;
@@ -3768,6 +3774,7 @@
ApplicationInfo ai = PackageParser.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId);
if (ai != null) {
+ rebaseEnabledOverlays(ai, userId);
ai.packageName = resolveExternalPackageNameLPr(p);
}
return ai;
@@ -3784,6 +3791,26 @@
return null;
}
+ private void rebaseEnabledOverlays(@NonNull ApplicationInfo ai, int userId) {
+ List<String> paths = new ArrayList<>();
+ ArrayMap<String, ArrayList<String>> userSpecificOverlays =
+ mEnabledOverlayPaths.get(userId);
+ if (userSpecificOverlays != null) {
+ if (!"android".equals(ai.packageName)) {
+ ArrayList<String> frameworkOverlays = userSpecificOverlays.get("android");
+ if (frameworkOverlays != null) {
+ paths.addAll(frameworkOverlays);
+ }
+ }
+
+ ArrayList<String> appOverlays = userSpecificOverlays.get(ai.packageName);
+ if (appOverlays != null) {
+ paths.addAll(appOverlays);
+ }
+ }
+ ai.resourceDirs = paths.size() > 0 ? paths.toArray(new String[paths.size()]) : null;
+ }
+
private String normalizePackageNameLPr(String packageName) {
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
return normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -4012,8 +4039,17 @@
/**
* Update given flags when being used to request {@link ResolveInfo}.
+ * <p>Instant apps are resolved specially, depending upon context. Minimally,
+ * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
+ * flag set. However, this flag is only honoured in three circumstances:
+ * <ul>
+ * <li>when called from a system process</li>
+ * <li>when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS}</li>
+ * <li>when resolution occurs to start an activity with a {@code android.intent.action.VIEW}
+ * action and a {@code android.intent.category.BROWSABLE} category</li>
+ * </ul>
*/
- int updateFlagsForResolve(int flags, int userId, Object cookie) {
+ int updateFlagsForResolve(int flags, int userId, Intent intent, boolean includeInstantApp) {
// Safe mode means we shouldn't match any third-party components
if (mSafeMode) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
@@ -4025,13 +4061,24 @@
flags |= PackageManager.MATCH_INSTANT;
} else {
// Otherwise, prevent leaking ephemeral components
+ final boolean isSpecialProcess =
+ callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID
+ || callingUid == 0;
+ final boolean allowMatchInstant =
+ (includeInstantApp
+ && Intent.ACTION_VIEW.equals(intent.getAction())
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && hasWebURI(intent))
+ || isSpecialProcess
+ || mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED;
flags &= ~PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
- // Unless called from the system process
+ if (!allowMatchInstant) {
flags &= ~PackageManager.MATCH_INSTANT;
}
}
- return updateFlagsForComponent(flags, userId, cookie);
+ return updateFlagsForComponent(flags, userId, intent /*cookie*/);
}
@Override
@@ -5556,17 +5603,23 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
+ return resolveIntentInternal(
+ intent, resolvedType, flags, userId, false /*includeInstantApp*/);
+ }
+
+ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+ int flags, int userId, boolean includeInstantApp) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
if (!sUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
- flags, userId);
+ flags, userId, includeInstantApp);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final ResolveInfo bestChoice =
@@ -5588,7 +5641,7 @@
}
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
- final int flags = updateFlagsForResolve(0, userId, intent);
+ final int flags = updateFlagsForResolve(0, userId, intent, false);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mPackages) {
@@ -5659,10 +5712,10 @@
if (callingUser != UserHandle.USER_SYSTEM) {
return false;
}
- if (mEphemeralResolverConnection == null) {
+ if (mInstantAppResolverConnection == null) {
return false;
}
- if (mEphemeralInstallerComponent == null) {
+ if (mInstantAppInstallerComponent == null) {
return false;
}
if (intent.getComponent() != null) {
@@ -5679,6 +5732,7 @@
return false;
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
+ // Or if there's already an ephemeral app installed that handles the action
synchronized (mPackages) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
@@ -5697,6 +5751,9 @@
}
return false;
}
+ if (ps.getInstantApp(userId)) {
+ return false;
+ }
}
}
}
@@ -5704,11 +5761,11 @@
return true;
}
- private void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
- Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage,
+ private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
+ Intent origIntent, String resolvedType, String callingPackage,
int userId) {
- final Message msg = mHandler.obtainMessage(EPHEMERAL_RESOLUTION_PHASE_TWO,
- new EphemeralRequest(responseObj, origIntent, resolvedType, launchIntent,
+ final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
+ new EphemeralRequest(responseObj, origIntent, resolvedType,
callingPackage, userId));
mHandler.sendMessage(msg);
}
@@ -5743,6 +5800,13 @@
if (ri != null) {
return ri;
}
+ // If we have an ephemeral app, use it
+ for (int i = 0; i < N; i++) {
+ ri = query.get(i);
+ if (ri.activityInfo.applicationInfo.isInstantApp()) {
+ return ri;
+ }
+ }
ri = new ResolveInfo(mResolveInfo);
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
@@ -5861,7 +5925,7 @@
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
intent = updateIntentForResolve(intent);
// writer
synchronized (mPackages) {
@@ -6029,7 +6093,7 @@
// cross-profile app linking works only towards the parent.
final UserInfo parent = getProfileParent(sourceUserId);
synchronized(mPackages) {
- int flags = updateFlagsForResolve(0, parent.id, intent);
+ int flags = updateFlagsForResolve(0, parent.id, intent, false);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -6088,9 +6152,14 @@
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
+ return queryIntentActivitiesInternal(intent, resolvedType, flags, userId, false);
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+ String resolvedType, int flags, int userId, boolean includeInstantApp) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, includeInstantApp);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
@@ -6134,7 +6203,7 @@
list.add(ri);
}
}
- return list;
+ return applyPostResolutionFilter(list, instantAppPkgName);
}
// reader
@@ -6152,7 +6221,7 @@
if (xpResolveInfo != null) {
List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
xpResult.add(xpResolveInfo);
- return filterForEphemeral(
+ return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName);
}
@@ -6193,13 +6262,13 @@
// And we are not going to add emphemeral app, so we can return the
// result straight away.
result.add(xpDomainInfo.resolveInfo);
- return filterForEphemeral(result, instantAppPkgName);
+ return applyPostResolutionFilter(result, instantAppPkgName);
}
} else if (result.size() <= 1 && !addEphemeral) {
// No result in parent user and <= 1 result in current profile, and we
// are not going to add emphemeral app, so we can return the result without
// further processing.
- return filterForEphemeral(result, instantAppPkgName);
+ return applyPostResolutionFilter(result, instantAppPkgName);
}
// We have more than one candidate (combining results from current and parent
// profile), so we need filtering and sorting.
@@ -6210,7 +6279,7 @@
} else {
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- result = filterForEphemeral(filterIfNotSystemUser(
+ result = applyPostResolutionFilter(filterIfNotSystemUser(
mActivities.queryIntentForPackage(
intent, resolvedType, flags, pkg.activities, userId),
userId), instantAppPkgName);
@@ -6227,15 +6296,16 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
final EphemeralRequest requestObject = new EphemeralRequest(
null /*responseObj*/, intent /*origIntent*/, resolvedType,
- null /*launchIntent*/, null /*callingPackage*/, userId);
- final EphemeralResponse intentInfo = EphemeralResolver.doEphemeralResolutionPhaseOne(
- mContext, mEphemeralResolverConnection, requestObject);
- if (intentInfo != null) {
+ null /*callingPackage*/, userId);
+ final AuxiliaryResolveInfo auxiliaryResponse =
+ EphemeralResolver.doEphemeralResolutionPhaseOne(
+ mContext, mInstantAppResolverConnection, requestObject);
+ if (auxiliaryResponse != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
- final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
- ephemeralInstaller.ephemeralResponse = intentInfo;
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
+ ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -6251,7 +6321,7 @@
if (sortResult) {
Collections.sort(result, mResolvePrioritySorter);
}
- return filterForEphemeral(result, instantAppPkgName);
+ return applyPostResolutionFilter(result, instantAppPkgName);
}
private static class CrossProfileDomainInfo {
@@ -6359,16 +6429,40 @@
* is performed.
* @return A filtered list of resolved activities.
*/
- private List<ResolveInfo> filterForEphemeral(List<ResolveInfo> resolveInfos,
+ private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
String ephemeralPkgName) {
+ // TODO: When adding on-demand split support for non-instant apps, remove this check
+ // and always apply post filtering
if (ephemeralPkgName == null) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
- ResolveInfo info = resolveInfos.get(i);
+ final ResolveInfo info = resolveInfos.get(i);
final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
// allow activities that are defined in the provided package
if (isEphemeralApp && ephemeralPkgName.equals(info.activityInfo.packageName)) {
+ if (info.activityInfo.splitName != null
+ && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
+ info.activityInfo.splitName)) {
+ // requested activity is defined in a split that hasn't been installed yet.
+ // add the installer to the resolve list
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ info.activityInfo.packageName, info.activityInfo.splitName,
+ info.activityInfo.applicationInfo.versionCode);
+ // make sure this resolver is the default
+ installerInfo.isDefault = true;
+ installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ installerInfo.filter = new IntentFilter();
+ // load resources from the correct package
+ installerInfo.resolvePackageName = info.getComponentInfo().packageName;
+ resolveInfos.set(i, installerInfo);
+ }
continue;
}
// allow activities that have been explicitly exposed to ephemeral apps
@@ -6692,7 +6786,7 @@
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activity options");
@@ -6872,7 +6966,7 @@
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -6909,7 +7003,7 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
List<ResolveInfo> query = queryIntentServicesInternal(intent, resolvedType, flags, userId);
if (query != null) {
if (query.size() >= 1) {
@@ -6931,7 +7025,7 @@
private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -6975,7 +7069,7 @@
private @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = updateFlagsForResolve(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent, false);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7147,6 +7241,7 @@
ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
ps.readUserState(userId), userId);
if (ai != null) {
+ rebaseEnabledOverlays(ai, userId);
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
} else {
@@ -7170,6 +7265,7 @@
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
+ rebaseEnabledOverlays(ai, userId);
ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
}
@@ -7313,6 +7409,7 @@
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
+ rebaseEnabledOverlays(ai, userId);
finalList.add(ai);
}
}
@@ -7448,60 +7545,6 @@
return finalList;
}
- private void createIdmapsForPackageLI(PackageParser.Package pkg) {
- ArrayMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName);
- if (overlays == null) {
- Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages");
- return;
- }
- for (PackageParser.Package opkg : overlays.values()) {
- // Not much to do if idmap fails: we already logged the error
- // and we certainly don't want to abort installation of pkg simply
- // because an overlay didn't fit properly. For these reasons,
- // ignore the return value of createIdmapForPackagePairLI.
- createIdmapForPackagePairLI(pkg, opkg);
- }
- }
-
- private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
- PackageParser.Package opkg) {
- if (!opkg.mTrustedOverlay) {
- Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
- opkg.baseCodePath + ": overlay not trusted");
- return false;
- }
- ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
- if (overlaySet == null) {
- Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
- opkg.baseCodePath + " but target package has no known overlays");
- return false;
- }
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- // TODO: generate idmap for split APKs
- try {
- mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid);
- } catch (InstallerException e) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
- + opkg.baseCodePath);
- return false;
- }
- PackageParser.Package[] overlayArray =
- overlaySet.values().toArray(new PackageParser.Package[0]);
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Arrays.sort(overlayArray, cmp);
-
- pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
- int i = 0;
- for (PackageParser.Package p : overlayArray) {
- pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
- }
- return true;
- }
-
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
try {
@@ -9990,7 +10033,6 @@
// writer
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
- boolean createIdmapFailed = false;
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
@@ -10331,36 +10373,9 @@
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
-
- // Create idmap files for pairs of (packages, overlay packages).
- // Note: "android", ie framework-res.apk, is handled by native layers.
- if (pkg.mOverlayTarget != null) {
- // This is an overlay package.
- if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
- if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
- mOverlays.put(pkg.mOverlayTarget,
- new ArrayMap<String, PackageParser.Package>());
- }
- ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
- map.put(pkg.packageName, pkg);
- PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
- if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
- createIdmapFailed = true;
- }
- }
- } else if (mOverlays.containsKey(pkg.packageName) &&
- !pkg.packageName.equals("android")) {
- // This is a regular package, with one or more known overlay packages.
- createIdmapsForPackageLI(pkg);
- }
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- if (createIdmapFailed) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "scanPackageLI failed to createIdmap");
- }
}
private static void maybeRenameForeignDexMarkers(PackageParser.Package existing,
@@ -10699,12 +10714,12 @@
}
}
- private void setUpEphemeralInstallerActivityLP(ComponentName installerComponent) {
+ private void setUpInstantAppInstallerActivityLP(ComponentName installerComponent) {
if (installerComponent == null) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Clear ephemeral installer activity");
}
- mEphemeralInstallerActivity.applicationInfo = null;
+ mInstantAppInstallerActivity.applicationInfo = null;
return;
}
@@ -10713,21 +10728,21 @@
}
final PackageParser.Package pkg = mPackages.get(installerComponent.getPackageName());
// Set up information for ephemeral installer activity
- mEphemeralInstallerActivity.applicationInfo = pkg.applicationInfo;
- mEphemeralInstallerActivity.name = installerComponent.getClassName();
- mEphemeralInstallerActivity.packageName = pkg.applicationInfo.packageName;
- mEphemeralInstallerActivity.processName = pkg.applicationInfo.packageName;
- mEphemeralInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+ mInstantAppInstallerActivity.applicationInfo = pkg.applicationInfo;
+ mInstantAppInstallerActivity.name = installerComponent.getClassName();
+ mInstantAppInstallerActivity.packageName = pkg.applicationInfo.packageName;
+ mInstantAppInstallerActivity.processName = pkg.applicationInfo.packageName;
+ mInstantAppInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ mInstantAppInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
| ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
- mEphemeralInstallerActivity.theme = 0;
- mEphemeralInstallerActivity.exported = true;
- mEphemeralInstallerActivity.enabled = true;
- mEphemeralInstallerInfo.activityInfo = mEphemeralInstallerActivity;
- mEphemeralInstallerInfo.priority = 0;
- mEphemeralInstallerInfo.preferredOrder = 1;
- mEphemeralInstallerInfo.isDefault = true;
- mEphemeralInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ mInstantAppInstallerActivity.theme = 0;
+ mInstantAppInstallerActivity.exported = true;
+ mInstantAppInstallerActivity.enabled = true;
+ mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity;
+ mInstantAppInstallerInfo.priority = 0;
+ mInstantAppInstallerInfo.preferredOrder = 1;
+ mInstantAppInstallerInfo.isDefault = true;
+ mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
}
@@ -12769,7 +12784,7 @@
}
static final class EphemeralIntentResolver
- extends IntentResolver<EphemeralResponse, EphemeralResponse> {
+ extends IntentResolver<AuxiliaryResolveInfo, AuxiliaryResolveInfo> {
/**
* The result that has the highest defined order. Ordering applies on a
* per-package basis. Mapping is from package name to Pair of order and
@@ -12784,17 +12799,17 @@
final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
@Override
- protected EphemeralResponse[] newArray(int size) {
- return new EphemeralResponse[size];
+ protected AuxiliaryResolveInfo[] newArray(int size) {
+ return new AuxiliaryResolveInfo[size];
}
@Override
- protected boolean isPackageForFilter(String packageName, EphemeralResponse responseObj) {
+ protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) {
return true;
}
@Override
- protected EphemeralResponse newResult(EphemeralResponse responseObj, int match,
+ protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
@@ -12816,7 +12831,7 @@
}
@Override
- protected void filterResults(List<EphemeralResponse> results) {
+ protected void filterResults(List<AuxiliaryResolveInfo> results) {
// only do work if ordering is enabled [most of the time it won't be]
if (mOrderResult.size() == 0) {
return;
@@ -13055,6 +13070,12 @@
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
+ if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
+ || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ throw new IllegalArgumentException(
+ "New installs into ASEC containers no longer supported");
+ }
+
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
@@ -18689,7 +18710,7 @@
Slog.w(TAG, "Shame on you for calling a hidden API. Shame!");
try {
observer.onGetStatsCompleted(null, false);
- } catch (RemoteException ignored) {
+ } catch (Throwable ignored) {
}
}
@@ -20159,6 +20180,7 @@
public static final int DUMP_FROZEN = 1 << 19;
public static final int DUMP_DEXOPT = 1 << 20;
public static final int DUMP_COMPILER_STATS = 1 << 21;
+ public static final int DUMP_ENABLED_OVERLAYS = 1 << 22;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -20278,12 +20300,16 @@
pw.println(" check-permission <permission> <package> [<user>]: does pkg hold perm?");
pw.println(" dexopt: dump dexopt state");
pw.println(" compiler-stats: dump compiler statistics");
+ pw.println(" enabled-overlays: dump list of enabled overlay packages");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
checkin = true;
} else if ("-f".equals(opt)) {
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
+ } else if ("--proto".equals(opt)) {
+ dumpProto(fd);
+ return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
@@ -20406,6 +20432,8 @@
dumpState.setDump(DumpState.DUMP_DEXOPT);
} else if ("compiler-stats".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
+ } else if ("enabled-overlays".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_ENABLED_OVERLAYS);
} else if ("write".equals(cmd)) {
synchronized (mPackages) {
mSettings.writeLPr();
@@ -20776,6 +20804,11 @@
dumpCompilerStatsLPr(pw, packageName);
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ENABLED_OVERLAYS)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dumpEnabledOverlaysLPr(pw);
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
@@ -20814,6 +20847,98 @@
}
}
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mPackages) {
+ final long requiredVerifierPackageToken =
+ proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
+ proto.write(PackageServiceDumpProto.PackageShortProto.NAME, mRequiredVerifierPackage);
+ proto.write(
+ PackageServiceDumpProto.PackageShortProto.UID,
+ getPackageUid(
+ mRequiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ proto.end(requiredVerifierPackageToken);
+
+ if (mIntentFilterVerifierComponent != null) {
+ String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+ final long verifierPackageToken =
+ proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
+ proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
+ proto.write(
+ PackageServiceDumpProto.PackageShortProto.UID,
+ getPackageUid(
+ verifierPackageName,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ proto.end(verifierPackageToken);
+ }
+
+ dumpSharedLibrariesProto(proto);
+ dumpFeaturesProto(proto);
+ mSettings.dumpPackagesProto(proto);
+ mSettings.dumpSharedUsersProto(proto);
+ dumpMessagesProto(proto);
+ }
+ proto.flush();
+ }
+
+ private void dumpMessagesProto(ProtoOutputStream proto) {
+ BufferedReader in = null;
+ String line = null;
+ try {
+ in = new BufferedReader(new FileReader(getSettingsProblemFile()));
+ while ((line = in.readLine()) != null) {
+ if (line.contains("ignored: updated version")) continue;
+ proto.write(PackageServiceDumpProto.MESSAGES, line);
+ }
+ } catch (IOException ignored) {
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void dumpFeaturesProto(ProtoOutputStream proto) {
+ synchronized (mAvailableFeatures) {
+ final int count = mAvailableFeatures.size();
+ for (int i = 0; i < count; i++) {
+ final FeatureInfo feat = mAvailableFeatures.valueAt(i);
+ final long featureToken = proto.start(PackageServiceDumpProto.FEATURES);
+ proto.write(PackageServiceDumpProto.FeatureProto.NAME, feat.name);
+ proto.write(PackageServiceDumpProto.FeatureProto.VERSION, feat.version);
+ proto.end(featureToken);
+ }
+ }
+ }
+
+ private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
+ final int count = mSharedLibraries.size();
+ for (int i = 0; i < count; i++) {
+ final String libName = mSharedLibraries.keyAt(i);
+ SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int j = 0; j < versionCount; j++) {
+ final SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+ final long sharedLibraryToken =
+ proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libEntry.info.getName());
+ final boolean isJar = (libEntry.path != null);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
+ if (isJar) {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH, libEntry.path);
+ } else {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.APK, libEntry.apk);
+ }
+ proto.end(sharedLibraryToken);
+ }
+ }
+ }
+
private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
@@ -20872,6 +20997,23 @@
}
}
+ private void dumpEnabledOverlaysLPr(PrintWriter pw) {
+ pw.println("Enabled overlay paths:");
+ final int N = mEnabledOverlayPaths.size();
+ for (int i = 0; i < N; i++) {
+ final int userId = mEnabledOverlayPaths.keyAt(i);
+ pw.println(String.format(" User %d:", userId));
+ final ArrayMap<String, ArrayList<String>> userSpecificOverlays =
+ mEnabledOverlayPaths.valueAt(i);
+ final int M = userSpecificOverlays.size();
+ for (int j = 0; j < M; j++) {
+ final String targetPackageName = userSpecificOverlays.keyAt(j);
+ final ArrayList<String> overlayPackagePaths = userSpecificOverlays.valueAt(j);
+ pw.println(String.format(" %s: %s", targetPackageName, overlayPackagePaths));
+ }
+ }
+ }
+
private String dumpDomainString(String packageName) {
List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
.getList();
@@ -22898,11 +23040,10 @@
}
@Override
- public void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
- Intent origIntent, String resolvedType, Intent launchIntent,
- String callingPackage, int userId) {
- PackageManagerService.this.requestEphemeralResolutionPhaseTwo(
- responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId);
+ public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
+ Intent origIntent, String resolvedType, String callingPackage, int userId) {
+ PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
+ responseObj, origIntent, resolvedType, callingPackage, userId);
}
@Override
@@ -22973,12 +23114,50 @@
return targetPackages;
}
-
@Override
- public boolean setEnabledOverlayPackages(int userId, String targetPackageName,
- List<String> overlayPackageNames) {
- // TODO: implement when we integrate OMS properly
- return false;
+ public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
+ @Nullable List<String> overlayPackageNames) {
+ synchronized (mPackages) {
+ if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
+ Slog.e(TAG, "failed to find package " + targetPackageName);
+ return false;
+ }
+
+ ArrayList<String> paths = null;
+ if (overlayPackageNames != null) {
+ final int N = overlayPackageNames.size();
+ paths = new ArrayList<String>(N);
+ for (int i = 0; i < N; i++) {
+ final String packageName = overlayPackageNames.get(i);
+ final PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ Slog.e(TAG, "failed to find package " + packageName);
+ return false;
+ }
+ paths.add(pkg.baseCodePath);
+ }
+ }
+
+ ArrayMap<String, ArrayList<String>> userSpecificOverlays =
+ mEnabledOverlayPaths.get(userId);
+ if (userSpecificOverlays == null) {
+ userSpecificOverlays = new ArrayMap<String, ArrayList<String>>();
+ mEnabledOverlayPaths.put(userId, userSpecificOverlays);
+ }
+
+ if (paths != null && paths.size() > 0) {
+ userSpecificOverlays.put(targetPackageName, paths);
+ } else {
+ userSpecificOverlays.remove(targetPackageName);
+ }
+ return true;
+ }
+ }
+
+ public ResolveInfo resolveIntent(Intent intent, String resolvedType,
+ int flags, int userId) {
+ return resolveIntentInternal(
+ intent, resolvedType, flags, userId, true /*includeInstantApp*/);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 5f348ab..b4bba88 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -19,6 +19,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
+import android.service.pm.PackageProto;
+import android.util.proto.ProtoOutputStream;
import java.io.File;
import java.util.List;
@@ -128,4 +131,32 @@
}
return true;
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
+ final long packageToken = proto.start(fieldId);
+ proto.write(PackageProto.NAME, (realName != null ? realName : name));
+ proto.write(PackageProto.UID, appId);
+ proto.write(PackageProto.VERSION_CODE, versionCode);
+ proto.write(PackageProto.VERSION_STRING, pkg.mVersionName);
+ proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime);
+ proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime);
+ proto.write(PackageProto.INSTALLER_NAME, installerPackageName);
+
+ if (pkg != null) {
+ long splitToken = proto.start(PackageProto.SPLITS);
+ proto.write(PackageProto.SplitProto.NAME, "base");
+ proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.baseRevisionCode);
+ proto.end(splitToken);
+ if (pkg.splitNames != null) {
+ for (int i = 0; i < pkg.splitNames.length; i++) {
+ splitToken = proto.start(PackageProto.SPLITS);
+ proto.write(PackageProto.SplitProto.NAME, pkg.splitNames[i]);
+ proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.splitRevisionCodes[i]);
+ proto.end(splitToken);
+ }
+ }
+ }
+ writeUsersInfoToProto(proto, PackageProto.USERS);
+ proto.end(packageToken);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 601377d6..b9c43da 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -25,8 +25,10 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.os.storage.VolumeInfo;
+import android.service.pm.PackageProto;
import android.util.ArraySet;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -564,4 +566,32 @@
modifyUserState(userId).domainVerificationStatus =
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
+
+ protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
+ int count = userState.size();
+ for (int i = 0; i < count; i++) {
+ final long userToken = proto.start(fieldId);
+ final int userId = userState.keyAt(i);
+ final PackageUserState state = userState.valueAt(i);
+ proto.write(PackageProto.UserInfoProto.ID, userId);
+ final int installType;
+ if (state.instantApp) {
+ installType = PackageProto.UserInfoProto.INSTANT_APP_INSTALL;
+ } else if (state.installed) {
+ installType = PackageProto.UserInfoProto.FULL_APP_INSTALL;
+ } else {
+ installType = PackageProto.UserInfoProto.NOT_INSTALLED_FOR_USER;
+ }
+ proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
+ proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
+ proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
+ proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
+ proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
+ proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
+ proto.write(
+ PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
+ state.lastDisableAppCaller);
+ proto.end(userToken);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a8a5ff0..570b31f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -65,6 +65,7 @@
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.service.pm.PackageServiceDumpProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -77,6 +78,7 @@
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
@@ -887,9 +889,9 @@
}
if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + p.name + " could not be assigned a valid uid");
+ "Package " + p.name + " could not be assigned a valid UID");
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Creating application package " + p.name + " failed");
+ "Package " + p.name + " could not be assigned a valid UID");
}
}
@@ -3370,7 +3372,7 @@
private void applyDefaultPreferredActivityLPw(PackageManagerService service,
Intent intent, int flags, ComponentName cn, String scheme, PatternMatcher ssp,
IntentFilter.AuthorityEntry auth, PatternMatcher path, int userId) {
- flags = service.updateFlagsForResolve(flags, userId, intent);
+ flags = service.updateFlagsForResolve(flags, userId, intent, false);
List<ResolveInfo> ri = service.mActivities.queryIntent(intent,
intent.getType(), flags, 0);
if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Queried " + intent
@@ -4876,6 +4878,16 @@
}
}
+ void dumpPackagesProto(ProtoOutputStream proto) {
+ List<UserInfo> users = getAllUsers(UserManagerService.getInstance());
+
+ final int count = mPackages.size();
+ for (int i = 0; i < count; i++) {
+ final PackageSetting ps = mPackages.valueAt(i);
+ ps.writeToProto(proto, PackageServiceDumpProto.PACKAGES, users);
+ }
+ }
+
void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
boolean printedSomething = false;
@@ -4966,6 +4978,17 @@
}
}
+ void dumpSharedUsersProto(ProtoOutputStream proto) {
+ final int count = mSharedUsers.size();
+ for (int i = 0; i < count; i++) {
+ final SharedUserSetting su = mSharedUsers.valueAt(i);
+ final long sharedUserToken = proto.start(PackageServiceDumpProto.SHARED_USERS);
+ proto.write(PackageServiceDumpProto.SharedUserProto.USER_ID, su.userId);
+ proto.write(PackageServiceDumpProto.SharedUserProto.NAME, su.name);
+ proto.end(sharedUserToken);
+ }
+ }
+
void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) {
pw.println("Settings parse messages:");
pw.print(mReadMessages.toString());
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 6eac5e3..b0689b8 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -48,7 +48,7 @@
/**
* Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
*/
- private static class PinItemRequestInner extends IPinItemRequest.Stub {
+ private abstract static class PinItemRequestInner extends IPinItemRequest.Stub {
protected final ShortcutRequestPinProcessor mProcessor;
private final IntentSender mResultIntent;
private final int mLauncherUid;
@@ -63,6 +63,14 @@
mLauncherUid = launcherUid;
}
+ public ShortcutInfo getShortcutInfo() {
+ return null;
+ }
+
+ public AppWidgetProviderInfo getAppWidgetProviderInfo() {
+ return null;
+ }
+
/**
* Returns true if the caller is same as the default launcher app when this request
* object was created.
@@ -126,6 +134,26 @@
/**
* Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
*/
+ private static class PinAppWidgetRequestInner extends PinItemRequestInner {
+ final AppWidgetProviderInfo mAppWidgetProviderInfo;
+
+ private PinAppWidgetRequestInner(ShortcutRequestPinProcessor processor,
+ IntentSender resultIntent, int launcherUid,
+ AppWidgetProviderInfo appWidgetProviderInfo) {
+ super(processor, resultIntent, launcherUid);
+
+ mAppWidgetProviderInfo = appWidgetProviderInfo;
+ }
+
+ @Override
+ public AppWidgetProviderInfo getAppWidgetProviderInfo() {
+ return mAppWidgetProviderInfo;
+ }
+ }
+
+ /**
+ * Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
+ */
private static class PinShortcutRequestInner extends PinItemRequestInner {
/** Original shortcut passed by the app. */
public final ShortcutInfo shortcutOriginal;
@@ -153,6 +181,11 @@
}
@Override
+ public ShortcutInfo getShortcutInfo() {
+ return shortcutForLauncher;
+ }
+
+ @Override
protected boolean tryAccept() {
if (DEBUG) {
Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId()
@@ -208,8 +241,9 @@
} else {
int launcherUid = mService.injectGetPackageUid(
confirmActivity.first.getPackageName(), launcherUserId);
- request = new PinItemRequest(inAppWidget,
- new PinItemRequestInner(this, resultIntent, launcherUid));
+ request = new PinItemRequest(
+ new PinAppWidgetRequestInner(this, resultIntent, launcherUid, inAppWidget),
+ PinItemRequest.REQUEST_TYPE_APPWIDGET);
}
return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request,
requestType);
@@ -319,7 +353,7 @@
mService.injectGetPackageUid(launcherPackage, launcherUserId),
existsAlready);
- return new PinItemRequest(shortcutForLauncher, inner);
+ return new PinItemRequest(inner, PinItemRequest.REQUEST_TYPE_SHORTCUT);
}
private void validateExistingShortcut(ShortcutInfo shortcutInfo) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b76a249..d3931fb 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -51,21 +51,23 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
-import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
+import android.service.power.PowerServiceDumpProto;
+import android.service.power.PowerServiceSettingsAndConfigurationDumpProto;
+import android.service.power.SuspendBlockerProto;
+import android.service.power.WakeLockProto;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
import android.util.KeyValueListParser;
-import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.WindowManagerPolicy;
-
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -78,7 +80,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
-import com.android.server.vr.VrManagerService;
+
import libcore.util.Objects;
import java.io.FileDescriptor;
@@ -575,6 +577,13 @@
pw.print(" "); pw.print(KEY_NO_CACHED_WAKE_LOCKS); pw.print("=");
pw.println(NO_CACHED_WAKE_LOCKS);
}
+
+ void dumpProto(ProtoOutputStream proto) {
+ final long constantsToken = proto.start(PowerServiceDumpProto.CONSTANTS);
+ proto.write(PowerServiceDumpProto.ConstantsProto.IS_NO_CACHED_WAKE_LOCKS,
+ NO_CACHED_WAKE_LOCKS);
+ proto.end(constantsToken);
+ }
}
final Constants mConstants;
@@ -3244,6 +3253,354 @@
}
}
+ private void dumpProto(FileDescriptor fd) {
+ final WirelessChargerDetector wcd;
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mLock) {
+ mConstants.dumpProto(proto);
+ proto.write(PowerServiceDumpProto.DIRTY, mDirty);
+ proto.write(PowerServiceDumpProto.WAKEFULNESS, mWakefulness);
+ proto.write(PowerServiceDumpProto.IS_WAKEFULNESS_CHANGING, mWakefulnessChanging);
+ proto.write(PowerServiceDumpProto.IS_POWERED, mIsPowered);
+ proto.write(PowerServiceDumpProto.PLUG_TYPE, mPlugType);
+ proto.write(PowerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
+ proto.write(
+ PowerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
+ mBatteryLevelWhenDreamStarted);
+ proto.write(PowerServiceDumpProto.DOCK_STATE, mDockState);
+ proto.write(PowerServiceDumpProto.IS_STAY_ON, mStayOn);
+ proto.write(PowerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
+ proto.write(PowerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
+ proto.write(PowerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
+ proto.write(
+ PowerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
+ mHalAutoSuspendModeEnabled);
+ proto.write(
+ PowerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
+ mHalInteractiveModeEnabled);
+
+ final long activeWakeLocksToken = proto.start(PowerServiceDumpProto.ACTIVE_WAKE_LOCKS);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
+ (mWakeLockSummary & WAKE_LOCK_CPU) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_BRIGHT,
+ (mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_DIM,
+ (mWakeLockSummary & WAKE_LOCK_SCREEN_DIM) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_BUTTON_BRIGHT,
+ (mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_PROXIMITY_SCREEN_OFF,
+ (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_STAY_AWAKE,
+ (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_DOZE,
+ (mWakeLockSummary & WAKE_LOCK_DOZE) != 0);
+ proto.write(
+ PowerServiceDumpProto.ActiveWakeLocksProto.IS_DRAW,
+ (mWakeLockSummary & WAKE_LOCK_DRAW) != 0);
+ proto.end(activeWakeLocksToken);
+
+ proto.write(PowerServiceDumpProto.NOTIFY_LONG_SCHEDULED_MS, mNotifyLongScheduled);
+ proto.write(PowerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
+ proto.write(PowerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
+
+ final long userActivityToken = proto.start(PowerServiceDumpProto.USER_ACTIVITY);
+ proto.write(
+ PowerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
+ (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
+ proto.write(
+ PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
+ (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
+ proto.write(
+ PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
+ (mUserActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
+ proto.end(userActivityToken);
+
+ proto.write(
+ PowerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
+ mRequestWaitForNegativeProximity);
+ proto.write(PowerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
+ proto.write(PowerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
+ proto.write(PowerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
+ proto.write(PowerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
+ proto.write(PowerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
+ proto.write(PowerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
+
+ for (int id : mDeviceIdleWhitelist) {
+ proto.write(PowerServiceDumpProto.DEVICE_IDLE_WHITELIST, id);
+ }
+ for (int id : mDeviceIdleTempWhitelist) {
+ proto.write(PowerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
+ }
+
+ proto.write(PowerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
+ proto.write(PowerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
+ proto.write(PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
+ proto.write(
+ PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
+ mLastUserActivityTimeNoChangeLights);
+ proto.write(
+ PowerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
+ mLastInteractivePowerHintTime);
+ proto.write(
+ PowerServiceDumpProto.LAST_SCREEN_BRIGHTNESS_BOOST_TIME_MS,
+ mLastScreenBrightnessBoostTime);
+ proto.write(
+ PowerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
+ mScreenBrightnessBoostInProgress);
+ proto.write(PowerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
+ proto.write(
+ PowerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
+ mHoldingWakeLockSuspendBlocker);
+ proto.write(
+ PowerServiceDumpProto.IS_HOLDING_DISPLAY_SUSPEND_BLOCKER,
+ mHoldingDisplaySuspendBlocker);
+
+ final long settingsAndConfigurationToken =
+ proto.start(PowerServiceDumpProto.SETTINGS_AND_CONFIGURATION);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_DECOUPLE_HAL_AUTO_SUSPEND_MODE_FROM_DISPLAY_CONFIG,
+ mDecoupleHalAutoSuspendModeFromDisplayConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_DECOUPLE_HAL_INTERACTIVE_MODE_FROM_DISPLAY_CONFIG,
+ mDecoupleHalInteractiveModeFromDisplayConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_WAKE_UP_WHEN_PLUGGED_OR_UNPLUGGED_CONFIG,
+ mWakeUpWhenPluggedOrUnpluggedConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_WAKE_UP_WHEN_PLUGGED_OR_UNPLUGGED_IN_THEATER_MODE_CONFIG,
+ mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_THEATER_MODE_ENABLED,
+ mTheaterModeEnabled);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_SUSPEND_WHEN_SCREEN_OFF_DUE_TO_PROXIMITY_CONFIG,
+ mSuspendWhenScreenOffDueToProximityConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ARE_DREAMS_SUPPORTED_CONFIG,
+ mDreamsSupportedConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ENABLED_BY_DEFAULT_CONFIG,
+ mDreamsEnabledByDefaultConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ACTIVATED_ON_SLEEP_BY_DEFAULT_CONFIG,
+ mDreamsActivatedOnSleepByDefaultConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ACTIVATED_ON_DOCK_BY_DEFAULT_CONFIG,
+ mDreamsActivatedOnDockByDefaultConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ENABLED_ON_BATTERY_CONFIG,
+ mDreamsEnabledOnBatteryConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .DREAMS_BATTERY_LEVEL_MINIMUM_WHEN_POWERED_CONFIG,
+ mDreamsBatteryLevelMinimumWhenPoweredConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .DREAMS_BATTERY_LEVEL_MINIMUM_WHEN_NOT_POWERED_CONFIG,
+ mDreamsBatteryLevelMinimumWhenNotPoweredConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .DREAMS_BATTERY_LEVEL_DRAIN_CUTOFF_CONFIG,
+ mDreamsBatteryLevelDrainCutoffConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ARE_DREAMS_ENABLED_SETTING,
+ mDreamsEnabledSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ACTIVATE_ON_SLEEP_SETTING,
+ mDreamsActivateOnSleepSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .ARE_DREAMS_ACTIVATE_ON_DOCK_SETTING,
+ mDreamsActivateOnDockSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_DOZE_AFTER_SCREEN_OFF_CONFIG,
+ mDozeAfterScreenOffConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_LOW_POWER_MODE_SETTING,
+ mLowPowerModeSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_AUTO_LOW_POWER_MODE_CONFIGURED,
+ mAutoLowPowerModeConfigured);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_AUTO_LOW_POWER_MODE_SNOOZING,
+ mAutoLowPowerModeSnoozing);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .MINIMUM_SCREEN_OFF_TIMEOUT_CONFIG_MS,
+ mMinimumScreenOffTimeoutConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .MAXIMUM_SCREEN_DIM_DURATION_CONFIG_MS,
+ mMaximumScreenDimDurationConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.MAXIMUM_SCREEN_DIM_RATIO_CONFIG,
+ mMaximumScreenDimRatioConfig);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.SCREEN_OFF_TIMEOUT_SETTING_MS,
+ mScreenOffTimeoutSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.SLEEP_TIMEOUT_SETTING_MS,
+ mSleepTimeoutSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .MAXIMUM_SCREEN_OFF_TIMEOUT_FROM_DEVICE_ADMIN_MS,
+ mMaximumScreenOffTimeoutFromDeviceAdmin);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_MAXIMUM_SCREEN_OFF_TIMEOUT_FROM_DEVICE_ADMIN_ENFORCED_LOCKED,
+ isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked());
+
+ final long stayOnWhilePluggedInToken =
+ proto.start(
+ PowerServiceSettingsAndConfigurationDumpProto.STAY_ON_WHILE_PLUGGED_IN);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.StayOnWhilePluggedInProto
+ .IS_STAY_ON_WHILE_PLUGGED_IN_AC,
+ ((mStayOnWhilePluggedInSetting & BatteryManager.BATTERY_PLUGGED_AC) != 0));
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.StayOnWhilePluggedInProto
+ .IS_STAY_ON_WHILE_PLUGGED_IN_USB,
+ ((mStayOnWhilePluggedInSetting & BatteryManager.BATTERY_PLUGGED_USB) != 0));
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.StayOnWhilePluggedInProto
+ .IS_STAY_ON_WHILE_PLUGGED_IN_WIRELESS,
+ ((mStayOnWhilePluggedInSetting & BatteryManager.BATTERY_PLUGGED_WIRELESS)
+ != 0));
+ proto.end(stayOnWhilePluggedInToken);
+
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.SCREEN_BRIGHTNESS_SETTING,
+ mScreenBrightnessSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_SETTING,
+ mScreenAutoBrightnessAdjustmentSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.SCREEN_BRIGHTNESS_MODE_SETTING,
+ mScreenBrightnessModeSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER,
+ mScreenBrightnessOverrideFromWindowManager);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS,
+ mUserActivityTimeoutOverrideFromWindowManager);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .IS_USER_INACTIVE_OVERRIDE_FROM_WINDOW_MANAGER,
+ mUserInactiveOverrideFromWindowManager);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .TEMPORARY_SCREEN_BRIGHTNESS_SETTING_OVERRIDE,
+ mTemporaryScreenBrightnessSettingOverride);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .TEMPORARY_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_SETTING_OVERRIDE,
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .DOZE_SCREEN_STATE_OVERRIDE_FROM_DREAM_MANAGER,
+ mDozeScreenStateOverrideFromDreamManager);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .DOZED_SCREEN_BRIGHTNESS_OVERRIDE_FROM_DREAM_MANAGER,
+ mDozeScreenBrightnessOverrideFromDreamManager);
+
+ final long screenBrightnessSettingLimitsToken =
+ proto.start(
+ PowerServiceSettingsAndConfigurationDumpProto
+ .SCREEN_BRIGHTNESS_SETTING_LIMITS);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
+ .SETTING_MINIMUM,
+ mScreenBrightnessSettingMinimum);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
+ .SETTING_MAXIMUM,
+ mScreenBrightnessSettingMaximum);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
+ .SETTING_DEFAULT,
+ mScreenBrightnessSettingDefault);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
+ .SETTING_FOR_VR_DEFAULT,
+ mScreenBrightnessForVrSettingDefault);
+ proto.end(screenBrightnessSettingLimitsToken);
+
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.SCREEN_BRIGHTNESS_FOR_VR_SETTING,
+ mScreenBrightnessForVrSetting);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_DOUBLE_TAP_WAKE_ENABLED,
+ mDoubleTapWakeEnabled);
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.IS_VR_MODE_ENABLED,
+ mIsVrModeEnabled);
+ proto.end(settingsAndConfigurationToken);
+
+ final int sleepTimeout = getSleepTimeoutLocked();
+ final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
+ final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ proto.write(PowerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
+ proto.write(PowerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
+ proto.write(PowerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
+ proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGING, mUidsChanging);
+ proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGED, mUidsChanged);
+
+ for (int i = 0; i < mUidState.size(); i++) {
+ final UidState state = mUidState.valueAt(i);
+ final long uIDToken = proto.start(PowerServiceDumpProto.UIDS);
+ final int uid = mUidState.keyAt(i);
+ proto.write(PowerServiceDumpProto.UidProto.UID, uid);
+ proto.write(PowerServiceDumpProto.UidProto.UID_STRING, UserHandle.formatUid(uid));
+ proto.write(PowerServiceDumpProto.UidProto.IS_ACTIVE, state.mActive);
+ proto.write(PowerServiceDumpProto.UidProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
+ if (state.mProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
+ proto.write(PowerServiceDumpProto.UidProto.IS_PROCESS_STATE_UNKNOWN, true);
+ } else {
+ proto.write(PowerServiceDumpProto.UidProto.PROCESS_STATE, state.mProcState);
+ }
+ proto.end(uIDToken);
+ }
+
+ mHandler.getLooper().writeToProto(proto, PowerServiceDumpProto.LOOPER);
+
+ for (WakeLock wl : mWakeLocks) {
+ wl.writeToProto(proto, PowerServiceDumpProto.WAKE_LOCKS);
+ }
+
+ for (SuspendBlocker sb : mSuspendBlockers) {
+ sb.writeToProto(proto, PowerServiceDumpProto.SUSPEND_BLOCKERS);
+ }
+ wcd = mWirelessChargerDetector;
+ }
+
+ if (wcd != null) {
+ wcd.writeToProto(proto, PowerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
+ }
+ proto.flush();
+ }
+
private SuspendBlocker createSuspendBlockerLocked(String name) {
SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
mSuspendBlockers.add(suspendBlocker);
@@ -3471,6 +3828,32 @@
return sb.toString();
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long wakeLockToken = proto.start(fieldId);
+ proto.write(WakeLockProto.LOCK_LEVEL, (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK));
+ proto.write(WakeLockProto.TAG, mTag);
+
+ final long wakeLockFlagsToken = proto.start(WakeLockProto.FLAGS);
+ proto.write(WakeLockProto.WakeLockFlagsProto.IS_ACQUIRE_CAUSES_WAKEUP,
+ (mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP)!=0);
+ proto.write(WakeLockProto.WakeLockFlagsProto.IS_ON_AFTER_RELEASE,
+ (mFlags & PowerManager.ON_AFTER_RELEASE)!=0);
+ proto.end(wakeLockFlagsToken);
+
+ proto.write(WakeLockProto.IS_DISABLED, mDisabled);
+ if (mNotifiedAcquired) {
+ proto.write(WakeLockProto.ACQ_MS, mAcquireTime);
+ }
+ proto.write(WakeLockProto.IS_NOTIFIED_LONG, mNotifiedLong);
+ proto.write(WakeLockProto.UID, mOwnerUid);
+ proto.write(WakeLockProto.PID, mOwnerPid);
+
+ if (mWorkSource != null) {
+ mWorkSource.writeToProto(proto, WakeLockProto.WORK_SOURCE);
+ }
+ proto.end(wakeLockToken);
+ }
+
@SuppressWarnings("deprecation")
private String getLockLevelString() {
switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
@@ -3568,6 +3951,15 @@
return mName + ": ref count=" + mReferenceCount;
}
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long sbToken = proto.start(fieldId);
+ synchronized (this) {
+ proto.write(SuspendBlockerProto.NAME, mName);
+ proto.write(SuspendBlockerProto.REFERENCE_COUNT, mReferenceCount);
+ }
+ proto.end(sbToken);
+ }
}
static final class UidState {
@@ -4055,8 +4447,19 @@
}
final long ident = Binder.clearCallingIdentity();
+
+ boolean isDumpProto = false;
+ for (String arg : args) {
+ if (arg.equals("--proto")) {
+ isDumpProto = true;
+ }
+ }
try {
- dumpInternal(pw);
+ if (isDumpProto) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/power/SuspendBlocker.java b/services/core/java/com/android/server/power/SuspendBlocker.java
index 70b278a..30b35f0 100644
--- a/services/core/java/com/android/server/power/SuspendBlocker.java
+++ b/services/core/java/com/android/server/power/SuspendBlocker.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import android.util.proto.ProtoOutputStream;
+
/**
* Low-level suspend blocker mechanism equivalent to holding a partial wake lock.
*
@@ -40,4 +42,6 @@
* The system may crash.
*/
void release();
+
+ void writeToProto(ProtoOutputStream proto, long fieldId);
}
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 38f5d77..6ee9dcd3 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -24,8 +24,10 @@
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.service.power.WirelessChargerDetectorProto;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
@@ -170,6 +172,44 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long wcdToken = proto.start(fieldId);
+ synchronized (mLock) {
+ proto.write(WirelessChargerDetectorProto.IS_POWERED_WIRELESSLY, mPoweredWirelessly);
+ proto.write(WirelessChargerDetectorProto.IS_AT_REST, mAtRest);
+
+ final long restVectorToken = proto.start(WirelessChargerDetectorProto.REST);
+ proto.write(WirelessChargerDetectorProto.VectorProto.X, mRestX);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Y, mRestY);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Z, mRestZ);
+ proto.end(restVectorToken);
+
+ proto.write(
+ WirelessChargerDetectorProto.IS_DETECTION_IN_PROGRESS, mDetectionInProgress);
+ proto.write(WirelessChargerDetectorProto.DETECTION_START_TIME_MS, mDetectionStartTime);
+ proto.write(
+ WirelessChargerDetectorProto.IS_MUST_UPDATE_REST_POSITION,
+ mMustUpdateRestPosition);
+ proto.write(WirelessChargerDetectorProto.TOTAL_SAMPLES, mTotalSamples);
+ proto.write(WirelessChargerDetectorProto.MOVING_SAMPLES, mMovingSamples);
+
+ final long firstSampleVectorToken =
+ proto.start(WirelessChargerDetectorProto.FIRST_SAMPLE);
+ proto.write(WirelessChargerDetectorProto.VectorProto.X, mFirstSampleX);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Y, mFirstSampleY);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Z, mFirstSampleZ);
+ proto.end(firstSampleVectorToken);
+
+ final long lastSampleVectorToken =
+ proto.start(WirelessChargerDetectorProto.LAST_SAMPLE);
+ proto.write(WirelessChargerDetectorProto.VectorProto.X, mLastSampleX);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Y, mLastSampleY);
+ proto.write(WirelessChargerDetectorProto.VectorProto.Z, mLastSampleZ);
+ proto.end(lastSampleVectorToken);
+ }
+ proto.end(wcdToken);
+ }
+
/**
* Updates the charging state and returns true if docking was detected.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index d179ea7..3df4d24 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -680,10 +680,10 @@
}
}
- mCurrentVrModeComponent = calling;
if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {
sendUpdatedCaller = true;
}
+ mCurrentVrModeComponent = calling;
if (mCurrentVrModeUser != userId) {
mCurrentVrModeUser = userId;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f7a9e41..b7479da 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -226,8 +226,8 @@
Matrix outMatrix) {
sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
- sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
- sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
+ sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
+ sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
sTempFloats[Matrix.MPERSP_0] = 0;
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 01bd86d..837b69e 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -272,18 +272,10 @@
void onAnimationEnd();
void moveToFullscreen();
-
- void getFullScreenBounds(Rect bounds);
}
- void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration) {
- boolean moveToFullscreen = false;
- if (to == null) {
- to = new Rect();
- target.getFullScreenBounds(to);
- moveToFullscreen = true;
- }
-
+ void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration,
+ boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index cfeb198..5f41187 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -230,28 +230,6 @@
}
/**
- * @return the movement bounds for the given {@param stackBounds} and the current state of the
- * controller.
- */
- private Rect getMovementBounds(Rect stackBounds) {
- return getMovementBounds(stackBounds, true /* adjustForIme */);
- }
-
- /**
- * @return the movement bounds for the given {@param stackBounds} and the current state of the
- * controller.
- */
- private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
- final Rect movementBounds = new Rect();
- getInsetBounds(movementBounds);
-
- // Apply the movement bounds adjustments based on the current state
- mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
- (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
- return movementBounds;
- }
-
- /**
* @param preChangeTargetBounds The final bounds of the stack if it is currently animating
* @return the repositioned PIP bounds given it's pre-change bounds, and the new display
* content.
@@ -387,6 +365,28 @@
}
/**
+ * @return the movement bounds for the given {@param stackBounds} and the current state of the
+ * controller.
+ */
+ private Rect getMovementBounds(Rect stackBounds) {
+ return getMovementBounds(stackBounds, true /* adjustForIme */);
+ }
+
+ /**
+ * @return the movement bounds for the given {@param stackBounds} and the current state of the
+ * controller.
+ */
+ private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+ final Rect movementBounds = new Rect();
+ getInsetBounds(movementBounds);
+
+ // Apply the movement bounds adjustments based on the current state
+ mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+ (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+ return movementBounds;
+ }
+
+ /**
* Applies the minimized offsets to the given stack bounds.
*/
private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
new file mode 100644
index 0000000..71f88de
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.UiThread;
+
+import java.util.List;
+
+/**
+ * Controller for the pinned stack container. See {@link StackWindowController}.
+ */
+public class PinnedStackWindowController extends StackWindowController {
+
+ private Rect mTmpBoundsRect = new Rect();
+
+ public PinnedStackWindowController(int stackId, StackWindowListener listener, int displayId,
+ boolean onTop, Rect outBounds) {
+ super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+ }
+
+ /**
+ * Animates the pinned stack.
+ */
+ public void animateResizePinnedStack(Rect bounds, int animationDuration) {
+ synchronized (mWindowMap) {
+ if (mContainer == null) {
+ throw new IllegalArgumentException("Pinned stack container not found :(");
+ }
+
+ // Get non-null fullscreen bounds if the bounds are null
+ final boolean moveToFullscreen = bounds == null;
+ bounds = getPinnedStackAnimationBounds(bounds);
+
+ // If the bounds are truly null, then there was no fullscreen stack at this time, so
+ // animate this to the full display bounds
+ final Rect toBounds;
+ if (bounds == null) {
+ toBounds = new Rect();
+ mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
+ } else {
+ toBounds = bounds;
+ }
+
+ final Rect originalBounds = new Rect();
+ mContainer.getBounds(originalBounds);
+ mContainer.setAnimatingBounds(toBounds);
+ UiThread.getHandler().post(() -> {
+ mService.mBoundsAnimationController.animateBounds(mContainer, originalBounds,
+ toBounds, animationDuration, moveToFullscreen);
+ });
+ }
+ }
+
+ /**
+ * Sets the current picture-in-picture aspect ratio.
+ */
+ public void setPictureInPictureAspectRatio(float aspectRatio) {
+ synchronized (mWindowMap) {
+ if (!mService.mSupportsPictureInPicture || mContainer == null) {
+ return;
+ }
+
+ final int displayId = mContainer.getDisplayContent().getDisplayId();
+ final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+ final Rect targetBounds = new Rect();
+ mContainer.getAnimatingBounds(targetBounds);
+ if (!toBounds.equals(targetBounds)) {
+ animateResizePinnedStack(toBounds, -1 /* duration */);
+ }
+
+ final PinnedStackController pinnedStackController =
+ mContainer.getDisplayContent().getPinnedStackController();
+ pinnedStackController.setAspectRatio(
+ pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+ ? aspectRatio : -1f);
+ }
+ }
+
+ /**
+ * Sets the current picture-in-picture actions.
+ */
+ public void setPictureInPictureActions(List<RemoteAction> actions) {
+ synchronized (mWindowMap) {
+ if (!mService.mSupportsPictureInPicture || mContainer == null) {
+ return;
+ }
+
+ mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
+ }
+ }
+
+ /**
+ * Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack
+ * animation if necessary.
+ */
+ private Rect getPinnedStackAnimationBounds(Rect bounds) {
+ mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
+ if (bounds == null && !mTmpBoundsRect.isEmpty()) {
+ bounds = new Rect(mTmpBoundsRect);
+ }
+ return bounds;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 142f69a..b0e115b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -199,59 +199,6 @@
}
}
- // TODO: This and similar methods should be separated into PinnedStackWindowContainerController
- public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
- synchronized (mWindowMap) {
- if (mContainer == null) {
- throw new IllegalArgumentException("Pinned stack container not found :(");
- }
- final Rect originalBounds = new Rect();
- mContainer.getBounds(originalBounds);
- mContainer.setAnimatingBounds(bounds);
- UiThread.getHandler().post(new Runnable() {
- @Override
- public void run() {
- mService.mBoundsAnimationController.animateBounds(
- mContainer, originalBounds, bounds, animationDuration);
- }
- });
- }
- }
-
- /** Sets the current picture-in-picture aspect ratio. */
- public void setPictureInPictureAspectRatio(float aspectRatio) {
- synchronized (mWindowMap) {
- if (!mService.mSupportsPictureInPicture || mContainer == null) {
- return;
- }
-
- final int displayId = mContainer.getDisplayContent().getDisplayId();
- final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
- final Rect targetBounds = new Rect();
- mContainer.getAnimatingBounds(targetBounds);
- if (!toBounds.equals(targetBounds)) {
- animateResizePinnedStack(toBounds, -1 /* duration */);
- }
-
- final PinnedStackController pinnedStackController =
- mContainer.getDisplayContent().getPinnedStackController();
- pinnedStackController.setAspectRatio(
- pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
- ? aspectRatio : -1f);
- }
- }
-
- /** Sets the current picture-in-picture actions. */
- public void setPictureInPictureActions(List<RemoteAction> actions) {
- synchronized (mWindowMap) {
- if (!mService.mSupportsPictureInPicture || mContainer == null) {
- return;
- }
-
- mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
- }
- }
-
public void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds,
Rect outTempInsetBounds, boolean ignoreVisibility) {
synchronized (mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 2b74f84..5041138 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.GraphicBuffer;
@@ -65,26 +66,22 @@
}
void onTransitionStarting() {
- if (!ENABLE_TASK_SNAPSHOTS) {
- return;
- }
handleClosingApps(mService.mClosingApps);
}
-
/**
* Called when the visibility of an app changes outside of the regular app transition flow.
*/
void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
- if (!ENABLE_TASK_SNAPSHOTS) {
- return;
- }
if (!visible) {
handleClosingApps(Sets.newArraySet(appWindowToken));
}
}
private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
+ if (!ENABLE_TASK_SNAPSHOTS || ActivityManager.isLowRamDeviceStatic()) {
+ return;
+ }
// We need to take a snapshot of the task if and only if all activities of the task are
// either closing or hidden.
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5f8b694..c7532ac 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -1476,14 +1477,6 @@
}
}
- @Override
- public void getFullScreenBounds(Rect bounds) {
- // This is currently only used for the pinned stack animation when leaving PiP
- // (see {@link BoundsAnimationController}), and in that case we need to animate this back
- // to the full bounds to match the fullscreen stack
- getDisplayContent().getLogicalDisplayRect(bounds);
- }
-
public boolean hasMovementAnimations() {
return StackId.hasMovementAnimations(mStackId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9397c9e..474610b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7267,10 +7267,6 @@
return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
}
- private DisplayInfo getDefaultDisplayInfoLocked() {
- return getDefaultDisplayContentLocked().getDisplayInfo();
- }
-
public void onDisplayAdded(int displayId) {
mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c0929cb..98598e1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -172,7 +172,7 @@
private boolean mAnimateMove = false;
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
- float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
+ private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
boolean mHaveMatrix;
@@ -945,8 +945,8 @@
tmpMatrix.getValues(tmpFloats);
mDsDx = tmpFloats[Matrix.MSCALE_X];
mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDsDy = tmpFloats[Matrix.MSKEW_X];
- mDtDy = tmpFloats[Matrix.MSCALE_Y];
+ mDtDy = tmpFloats[Matrix.MSKEW_X];
+ mDsDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
mWin.mShownPosition.set(Math.round(x), Math.round(y));
@@ -959,7 +959,7 @@
mShownAlpha = mAlpha;
if (!mService.mLimitedAlphaCompositing
|| (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
- || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
+ || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
&& x == frame.left && y == frame.top))) {
//Slog.i(TAG_WM, "Applying alpha transform");
if (selfTransformation) {
@@ -1038,8 +1038,8 @@
mHaveMatrix = true;
mDsDx = tmpFloats[Matrix.MSCALE_X];
mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDsDy = tmpFloats[Matrix.MSKEW_X];
- mDtDy = tmpFloats[Matrix.MSCALE_Y];
+ mDtDy = tmpFloats[Matrix.MSKEW_X];
+ mDsDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
mWin.mShownPosition.set(Math.round(x), Math.round(y));
@@ -1054,8 +1054,8 @@
mHaveMatrix = false;
mDsDx = mWin.mGlobalScale;
mDtDx = 0;
- mDsDy = 0;
- mDtDy = mWin.mGlobalScale;
+ mDtDy = 0;
+ mDsDy = mWin.mGlobalScale;
}
}
@@ -1384,8 +1384,8 @@
applyCrop(clipRect, finalClipRect, recoveringMemory);
mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
- mDsDy * w.mHScale * mExtraHScale,
- mDtDy * w.mVScale * mExtraVScale, recoveringMemory);
+ mDtDy * w.mHScale * mExtraHScale,
+ mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
}
if (mSurfaceResized) {
@@ -1467,15 +1467,15 @@
"alpha=" + mShownAlpha + " layer=" + mAnimLayer
+ " matrix=[" + mDsDx + "*" + w.mHScale
+ "," + mDtDx + "*" + w.mVScale
- + "][" + mDsDy + "*" + w.mHScale
- + "," + mDtDy + "*" + w.mVScale + "]", false);
+ + "][" + mDtDy + "*" + w.mHScale
+ + "," + mDsDy + "*" + w.mVScale + "]", false);
boolean prepared =
mSurfaceController.prepareToShowInTransaction(mShownAlpha, mAnimLayer,
mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
- mDsDy * w.mHScale * mExtraHScale,
- mDtDy * w.mVScale * mExtraVScale,
+ mDtDy * w.mHScale * mExtraHScale,
+ mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
if (prepared && mLastHidden && mDrawState == HAS_DRAWN) {
@@ -1777,8 +1777,8 @@
pw.print(prefix); pw.print("mGlobalScale="); pw.print(mWin.mGlobalScale);
pw.print(" mDsDx="); pw.print(mDsDx);
pw.print(" mDtDx="); pw.print(mDtDx);
- pw.print(" mDsDy="); pw.print(mDsDy);
- pw.print(" mDtDy="); pw.println(mDtDy);
+ pw.print(" mDtDy="); pw.print(mDtDy);
+ pw.print(" mDsDy="); pw.println(mDsDy);
}
if (mAnimationStartDelayed) {
pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
@@ -1926,15 +1926,15 @@
float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
float DtDx = mService.mTmpFloats[Matrix.MSKEW_Y];
- float DsDy = mService.mTmpFloats[Matrix.MSKEW_X];
- float DtDy = mService.mTmpFloats[Matrix.MSCALE_Y];
+ float DtDy = mService.mTmpFloats[Matrix.MSKEW_X];
+ float DsDy = mService.mTmpFloats[Matrix.MSCALE_Y];
float nx = mService.mTmpFloats[Matrix.MTRANS_X];
float ny = mService.mTmpFloats[Matrix.MTRANS_Y];
mSurfaceController.setPositionInTransaction(nx, ny, false);
mSurfaceController.setMatrixInTransaction(DsDx * w.mHScale,
DtDx * w.mVScale,
- DsDy * w.mHScale,
- DtDy * w.mVScale, false);
+ DtDy * w.mHScale,
+ DsDy * w.mVScale, false);
}
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4c89705..20b70a6 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1036,7 +1036,7 @@
}
// TODO(b/31632518)
- gnssHal = IGnss::getService("gnss");
+ gnssHal = IGnss::getService();
if (gnssHal != nullptr) {
auto gnssXtra = gnssHal->getExtensionXtra();
if (!gnssXtra.isOk()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 620d441..7b6b941 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -41,6 +41,7 @@
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
@@ -332,15 +333,6 @@
}
/**
- * Keyguard features that when set on a managed profile that doesn't have its own challenge will
- * affect the profile's parent user. These can also be set on the managed profile's parent DPM
- * instance.
- */
- private static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
- DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
- | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
-
- /**
* Keyguard features that when set on a profile affect the profile content or challenge only.
* These cannot be set on the managed profile's parent DPM instance
*/
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a5f1945..e586482 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -82,6 +82,7 @@
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.notification.NotificationManagerService;
+import com.android.server.om.OverlayManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.pm.Installer;
@@ -592,6 +593,11 @@
mActivityManagerService.setSystemProcess();
traceEnd();
+ // Manages Overlay packages
+ traceBeginAndSlog("StartOverlayManagerService");
+ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
+ traceEnd();
+
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
index b26bac3..0cf4994 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -89,6 +89,7 @@
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
channel.setShowBadge(false);
NotificationRecord r = getNotificationRecord(channel);
@@ -107,6 +108,7 @@
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(true);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
NotificationRecord r = getNotificationRecord(channel);
@@ -124,6 +126,7 @@
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
channel.setShowBadge(true);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
NotificationRecord r = getNotificationRecord(channel);
@@ -141,6 +144,7 @@
NotificationChannel channel =
new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
channel.setShowBadge(false);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
NotificationRecord r = getNotificationRecord(channel);
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index 69724f4..c5abba8 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -27,6 +28,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
@@ -71,8 +73,11 @@
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
+ ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
verify(mAm, times(1)).setExactAndAllowWhileIdle(
- anyInt(), eq((long) 1000), any(PendingIntent.class));
+ anyInt(), captor.capture(), any(PendingIntent.class));
+ long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
+ assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 2);
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 665f01f..4141f2f 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -56,8 +56,7 @@
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
-import android.net.NetworkScorerAppManager;
-import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.NetworkScorerAppData;
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
@@ -201,10 +200,10 @@
}
@Test
- public void testSystemRunning() {
+ public void testOnUserUnlocked() {
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
@@ -549,9 +548,9 @@
}
@Test
- public void testSetActiveScorer_noScoreNetworksPermission() {
- doThrow(new SecurityException()).when(mContext)
- .enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
+ public void testSetActiveScorer_noRequestNetworkScoresPermission() {
+ when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
try {
mNetworkScoreService.setActiveScorer(null);
@@ -630,7 +629,7 @@
@Test
public void testIsCallerActiveScorer_noBoundService() throws Exception {
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()));
}
@@ -651,7 +650,7 @@
@Test
public void testGetActiveScorerPackage_notActive() throws Exception {
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
assertNull(mNetworkScoreService.getActiveScorerPackage());
}
@@ -659,7 +658,7 @@
@Test
public void testGetActiveScorerPackage_active() throws Exception {
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
assertEquals(NEW_SCORER.getRecommendationServicePackageName(),
mNetworkScoreService.getActiveScorerPackage());
@@ -960,7 +959,7 @@
return true;
}
});
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
}
private void bindToScorer(boolean callerIsScorer) {
@@ -974,7 +973,7 @@
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(appData);
when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
isA(UserHandle.class))).thenReturn(true);
- mNetworkScoreService.systemRunning();
+ mNetworkScoreService.onUserUnlocked(0);
}
private static class OnResultListener implements RemoteCallback.OnResultListener {
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
similarity index 61%
rename from core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
rename to services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 347024d..e9a2d34 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -14,13 +14,22 @@
* limitations under the License
*/
-package android.net;
+package com.android.server;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest.permission;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -29,144 +38,84 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
-import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.NetworkScoreManager;
+import android.net.NetworkScorerAppData;
import android.os.Bundle;
import android.provider.Settings;
-import android.test.InstrumentationTestCase;
+import android.support.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
-public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class NetworkScorerAppManagerTest {
@Mock private Context mMockContext;
@Mock private PackageManager mMockPm;
@Mock private Resources mResources;
- @Mock private ContentResolver mContentResolver;
- private Context mTargetContext;
+ @Mock private NetworkScorerAppManager.SettingsFacade mSettingsFacade;
private NetworkScorerAppManager mNetworkScorerAppManager;
+ private List<ResolveInfo> mAvailableServices;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
- // Configuration needed to make mockito/dexcache work.
- mTargetContext = getInstrumentation().getTargetContext();
- System.setProperty("dexmaker.dexcache", mTargetContext.getCacheDir().getPath());
- ClassLoader newClassLoader = getInstrumentation().getClass().getClassLoader();
- Thread.currentThread().setContextClassLoader(newClassLoader);
-
MockitoAnnotations.initMocks(this);
+ mAvailableServices = new ArrayList<>();
when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ when(mMockPm.queryIntentServices(Mockito.argThat(new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Object object) {
+ Intent intent = (Intent) object;
+ return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS.equals(intent.getAction());
+ }
+ }), eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices);
when(mMockContext.getResources()).thenReturn(mResources);
- when(mMockContext.getContentResolver()).thenReturn(mTargetContext.getContentResolver());
- mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext);
+
+ mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext, mSettingsFacade);
}
- public void testGetPotentialRecommendationProviderPackages_emptyConfig() throws Exception {
- setNetworkRecommendationPackageNames(/*no configured packages*/);
- assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
- }
-
- public void testGetPotentialRecommendationProviderPackages_permissionNotGranted()
- throws Exception {
- setNetworkRecommendationPackageNames("package1");
- mockScoreNetworksDenied("package1");
-
- assertTrue(mNetworkScorerAppManager.getPotentialRecommendationProviderPackages().isEmpty());
- }
-
- public void testGetPotentialRecommendationProviderPackages_permissionGranted()
- throws Exception {
- setNetworkRecommendationPackageNames("package1");
- mockScoreNetworksGranted("package1");
-
- List<String> potentialProviderPackages =
- mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
-
- assertFalse(potentialProviderPackages.isEmpty());
- assertEquals("package1", potentialProviderPackages.get(0));
- }
-
- public void testGetPotentialRecommendationProviderPackages_multipleConfigured()
- throws Exception {
- setNetworkRecommendationPackageNames("package1", "package2");
- mockScoreNetworksDenied("package1");
- mockScoreNetworksGranted("package2");
-
- List<String> potentialProviderPackages =
- mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
-
- assertEquals(1, potentialProviderPackages.size());
- assertEquals("package2", potentialProviderPackages.get(0));
- }
-
- public void testGetNetworkRecommendationProviderData_noPotentialPackages() throws Exception {
- setNetworkRecommendationPackageNames(/*no configured packages*/);
- assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
- }
-
- public void testGetNetworkRecommendationProviderData_serviceMissing() throws Exception {
- setNetworkRecommendationPackageNames("package1");
- mockScoreNetworksGranted("package1");
-
- assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
- }
-
- public void testGetNetworkRecommendationProviderData_scoreNetworksNotGranted()
- throws Exception {
- final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
- mockScoreNetworksDenied(recoComponent.getPackageName());
- mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
-
- assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
- }
-
- public void testGetNetworkRecommendationProviderData_available() throws Exception {
- final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
- mockScoreNetworksGranted(recoComponent.getPackageName());
- mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
-
- NetworkScorerAppData appData =
- mNetworkScorerAppManager.getNetworkRecommendationProviderData();
- assertNotNull(appData);
- assertEquals(recoComponent, appData.getRecommendationServiceComponent());
- assertEquals(924, appData.packageUid);
- }
-
+ @Test
public void testGetActiveScorer_providerAvailable() throws Exception {
final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+ setNetworkRecoPackageSetting(recoComponent.getPackageName());
mockScoreNetworksGranted(recoComponent.getPackageName());
mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNotNull(activeScorer);
assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
assertEquals(924, activeScorer.packageUid);
}
+ @Test
+ public void testGetActiveScorer_permissionMissing() throws Exception {
+ final ComponentName recoComponent = new ComponentName("package1", "class1");
+ setNetworkRecoPackageSetting(recoComponent.getPackageName());
+ mockScoreNetworksDenied(recoComponent.getPackageName());
+ mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
+
+ final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+ assertNull(activeScorer);
+ }
+
+ @Test
public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityNotSet()
throws Exception {
final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+ setNetworkRecoPackageSetting(recoComponent.getPackageName());
mockScoreNetworksGranted(recoComponent.getPackageName());
mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
null /* enableUseOpenWifiPackageActivityPackage*/);
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNotNull(activeScorer);
assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
@@ -174,17 +123,15 @@
assertNull(activeScorer.getEnableUseOpenWifiActivity());
}
+ @Test
public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityNotResolved()
throws Exception {
final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+ setNetworkRecoPackageSetting(recoComponent.getPackageName());
mockScoreNetworksGranted(recoComponent.getPackageName());
mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
"package2" /* enableUseOpenWifiPackageActivityPackage*/);
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNotNull(activeScorer);
assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
@@ -192,19 +139,17 @@
assertNull(activeScorer.getEnableUseOpenWifiActivity());
}
+ @Test
public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityResolved()
throws Exception {
final ComponentName recoComponent = new ComponentName("package1", "class1");
final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+ setNetworkRecoPackageSetting(recoComponent.getPackageName());
mockScoreNetworksGranted(recoComponent.getPackageName());
mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
enableUseOpenWifiComponent.getPackageName());
mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNotNull(activeScorer);
assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
@@ -212,33 +157,105 @@
assertEquals(enableUseOpenWifiComponent, activeScorer.getEnableUseOpenWifiActivity());
}
- public void testGetActiveScorer_providerNotAvailable()
+ @Test
+ public void testGetActiveScorer_packageSettingIsNull()
throws Exception {
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
+ // NETWORK_RECOMMENDATIONS_PACKAGE is null
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNull(activeScorer);
}
- public void testGetActiveScorer_recommendationsDisabled() throws Exception {
+ @Test
+ public void testGetActiveScorer_packageSettingIsInvalid() throws Exception {
final ComponentName recoComponent = new ComponentName("package1", "class1");
- setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+ setDefaultNetworkRecommendationPackage(recoComponent.getPackageName());
mockScoreNetworksGranted(recoComponent.getPackageName());
- mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
- ContentResolver cr = mTargetContext.getContentResolver();
- Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0);
+ // NETWORK_RECOMMENDATIONS_PACKAGE is set to a package that isn't a recommender.
final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
assertNull(activeScorer);
}
- private void setNetworkRecommendationPackageNames(String... names) {
- if (names == null) {
- names = new String[0];
- }
- when(mResources.getStringArray(R.array.config_networkRecommendationPackageNames))
- .thenReturn(names);
+ @Test
+ public void testSetActiveScorer_noChange() throws Exception {
+ String packageName = "package";
+ setNetworkRecoPackageSetting(packageName);
+
+ assertTrue(mNetworkScorerAppManager.setActiveScorer(packageName));
+ verify(mSettingsFacade, never()).putString(any(), any(), any());
+ }
+
+ @Test
+ public void testSetActiveScorer_nullPackage() throws Exception {
+ String packageName = "package";
+ String defaultPackage = "defaultPackage";
+ setNetworkRecoPackageSetting(packageName);
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+
+ assertTrue(mNetworkScorerAppManager.setActiveScorer(null));
+ verify(mSettingsFacade).putString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ }
+
+ @Test
+ public void testSetActiveScorer_validPackage() throws Exception {
+ String packageName = "package";
+ String newPackage = "newPackage";
+ setNetworkRecoPackageSetting(packageName);
+ final ComponentName recoComponent = new ComponentName(newPackage, "class1");
+ mockScoreNetworksGranted(recoComponent.getPackageName());
+ mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+
+ assertTrue(mNetworkScorerAppManager.setActiveScorer(newPackage));
+ verify(mSettingsFacade).putString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, newPackage);
+ }
+
+ @Test
+ public void testSetActiveScorer_invalidPackage() throws Exception {
+ String packageName = "package";
+ String newPackage = "newPackage";
+ setNetworkRecoPackageSetting(packageName);
+ // newPackage doesn't resolve to a valid recommender
+
+ assertFalse(mNetworkScorerAppManager.setActiveScorer(newPackage));
+ verify(mSettingsFacade, never()).putString(any(), any(), any());
+ }
+
+
+ @Test
+ public void testRevertToDefaultIfNoActive_notActive() throws Exception {
+ String defaultPackage = "defaultPackage";
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+
+ mNetworkScorerAppManager.revertToDefaultIfNoActive();
+
+ verify(mSettingsFacade).putString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ }
+
+ @Test
+ public void testRevertToDefaultIfNoActive_active() throws Exception {
+ String packageName = "package";
+ setNetworkRecoPackageSetting(packageName);
+ final ComponentName recoComponent = new ComponentName(packageName, "class1");
+ mockScoreNetworksGranted(recoComponent.getPackageName());
+ mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+
+ mNetworkScorerAppManager.revertToDefaultIfNoActive();
+
+ verify(mSettingsFacade, never()).putString(any(), any(), any());
+ }
+
+ private void setNetworkRecoPackageSetting(String packageName) {
+ when(mSettingsFacade.getString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE)).thenReturn(packageName);
+ }
+
+ private void setDefaultNetworkRecommendationPackage(String name) {
+ when(mResources.getString(R.string.config_defaultNetworkRecommendationProviderPackage))
+ .thenReturn(name);
}
private void mockScoreNetworksGranted(String packageName) {
@@ -281,6 +298,8 @@
&& compName.getPackageName().equals(intent.getPackage());
}
}), Mockito.eq(flags))).thenReturn(serviceInfo);
+
+ mAvailableServices.add(serviceInfo);
}
private void mockEnableUseOpenWifiActivity(final ComponentName useOpenWifiComp) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 384f49f..28596f7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -197,7 +197,6 @@
assertEquals(a.installLocation, b.installLocation);
assertEquals(a.coreApp, b.coreApp);
assertEquals(a.mRequiredForAllUsers, b.mRequiredForAllUsers);
- assertEquals(a.mOverlayPriority, b.mOverlayPriority);
assertEquals(a.mTrustedOverlay, b.mTrustedOverlay);
assertEquals(a.use32bitAbi, b.use32bitAbi);
assertEquals(a.packageName, b.packageName);
@@ -433,7 +432,6 @@
pkg.installLocation = 100;
pkg.coreApp = true;
pkg.mRequiredForAllUsers = true;
- pkg.mOverlayPriority = 100;
pkg.mTrustedOverlay = true;
pkg.use32bitAbi = true;
pkg.packageName = "foo";
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 0bd014c..42ddedf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -45,12 +45,12 @@
final int userId = 0;
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
- aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000);
- aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000);
+ aih.updateDisplay(true, /* elapsedRealtime= */ 1000);
+ aih.updateDisplay(false, /* elapsedRealtime= */ 2000);
// Screen On time file should be written right away
assertTrue(aih.getScreenOnTimeFile().exists());
- aih.writeAppIdleTimesLocked(userId);
+ aih.writeAppIdleTimes(userId);
// stats file should be written now
assertTrue(new File(new File(mStorageDir, "users/" + userId),
AppIdleHistory.APP_IDLE_FILENAME).exists());
@@ -58,43 +58,43 @@
public void testScreenOnTime() {
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
- aih.updateDisplayLocked(false, 2000);
- assertEquals(aih.getScreenOnTimeLocked(2000), 0);
- aih.updateDisplayLocked(true, 3000);
- assertEquals(aih.getScreenOnTimeLocked(4000), 1000);
- assertEquals(aih.getScreenOnTimeLocked(5000), 2000);
- aih.updateDisplayLocked(false, 6000);
+ aih.updateDisplay(false, 2000);
+ assertEquals(aih.getScreenOnTime(2000), 0);
+ aih.updateDisplay(true, 3000);
+ assertEquals(aih.getScreenOnTime(4000), 1000);
+ assertEquals(aih.getScreenOnTime(5000), 2000);
+ aih.updateDisplay(false, 6000);
// Screen on time should not keep progressing with screen is off
- assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
- assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
- aih.writeAppIdleDurationsLocked();
+ assertEquals(aih.getScreenOnTime(7000), 3000);
+ assertEquals(aih.getScreenOnTime(8000), 3000);
+ aih.writeAppIdleDurations();
// Check if the screen on time is persisted across instantiations
AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
- assertEquals(aih2.getScreenOnTimeLocked(11000), 3000);
- aih2.updateDisplayLocked(true, 4000);
- aih2.updateDisplayLocked(false, 5000);
- assertEquals(aih2.getScreenOnTimeLocked(13000), 4000);
+ assertEquals(aih2.getScreenOnTime(11000), 3000);
+ aih2.updateDisplay(true, 4000);
+ aih2.updateDisplay(false, 5000);
+ assertEquals(aih2.getScreenOnTime(13000), 4000);
}
public void testPackageEvents() {
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.setThresholds(4000, 1000);
- aih.updateDisplayLocked(true, 1000);
+ aih.updateDisplay(true, 1000);
// App is not-idle by default
- assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500));
+ assertFalse(aih.isIdle(PACKAGE_1, 0, 1500));
// Still not idle
- assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000));
+ assertFalse(aih.isIdle(PACKAGE_1, 0, 3000));
// Idle now
- assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000));
+ assertTrue(aih.isIdle(PACKAGE_1, 0, 8000));
// Not idle
- assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000));
+ assertFalse(aih.isIdle(PACKAGE_2, 0, 9000));
// Screen off
- aih.updateDisplayLocked(false, 9100);
+ aih.updateDisplay(false, 9100);
// Still idle after 10 seconds because screen hasn't been on long enough
- assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000));
- aih.updateDisplayLocked(true, 21000);
- assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000));
+ assertFalse(aih.isIdle(PACKAGE_2, 0, 20000));
+ aih.updateDisplay(true, 21000);
+ assertTrue(aih.isIdle(PACKAGE_2, 0, 23000));
}
}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index f69dae4..f298559 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -18,7 +18,6 @@
import android.os.Environment;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
@@ -101,7 +100,7 @@
mElapsedSnapshot = elapsedRealtime;
mScreenOnSnapshot = elapsedRealtime;
mStorageDir = storageDir;
- readScreenOnTimeLocked();
+ readScreenOnTime();
}
public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
@@ -109,7 +108,7 @@
mScreenOnTimeThreshold = screenOnTimeThreshold;
}
- public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) {
+ public void updateDisplay(boolean screenOn, long elapsedRealtime) {
if (screenOn == mScreenOn) return;
mScreenOn = screenOn;
@@ -122,7 +121,7 @@
}
}
- public long getScreenOnTimeLocked(long elapsedRealtime) {
+ public long getScreenOnTime(long elapsedRealtime) {
long screenOnTime = mScreenOnDuration;
if (mScreenOn) {
screenOnTime += elapsedRealtime - mScreenOnSnapshot;
@@ -135,7 +134,7 @@
return new File(mStorageDir, "screen_on_time");
}
- private void readScreenOnTimeLocked() {
+ private void readScreenOnTime() {
File screenOnTimeFile = getScreenOnTimeFile();
if (screenOnTimeFile.exists()) {
try {
@@ -146,11 +145,11 @@
} catch (IOException | NumberFormatException e) {
}
} else {
- writeScreenOnTimeLocked();
+ writeScreenOnTime();
}
}
- private void writeScreenOnTimeLocked() {
+ private void writeScreenOnTime() {
AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
FileOutputStream fos = null;
try {
@@ -166,30 +165,30 @@
/**
* To be called periodically to keep track of elapsed time when app idle times are written
*/
- public void writeAppIdleDurationsLocked() {
+ public void writeAppIdleDurations() {
final long elapsedRealtime = SystemClock.elapsedRealtime();
// Only bump up and snapshot the elapsed time. Don't change screen on duration.
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
mElapsedSnapshot = elapsedRealtime;
- writeScreenOnTimeLocked();
+ writeScreenOnTime();
}
- public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
- PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+ public void reportUsage(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
+ PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime);
shiftHistoryToNow(userHistory, elapsedRealtime);
packageHistory.lastUsedElapsedTime = mElapsedDuration
+ (elapsedRealtime - mElapsedSnapshot);
- packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
+ packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
}
public void setIdle(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
- PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+ ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
+ PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime);
shiftHistoryToNow(userHistory, elapsedRealtime);
@@ -222,23 +221,23 @@
mLastPeriod = thisPeriod;
}
- private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) {
+ private ArrayMap<String, PackageHistory> getUserHistory(int userId) {
ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
if (userHistory == null) {
userHistory = new ArrayMap<>();
mIdleHistory.put(userId, userHistory);
- readAppIdleTimesLocked(userId, userHistory);
+ readAppIdleTimes(userId, userHistory);
}
return userHistory;
}
- private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory,
+ private PackageHistory getPackageHistory(ArrayMap<String, PackageHistory> userHistory,
String packageName, long elapsedRealtime) {
PackageHistory packageHistory = userHistory.get(packageName);
if (packageHistory == null) {
packageHistory = new PackageHistory();
- packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime);
- packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
+ packageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
+ packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
userHistory.put(packageName, packageHistory);
}
return packageHistory;
@@ -248,41 +247,41 @@
mIdleHistory.remove(userId);
}
- public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+ public boolean isIdle(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
PackageHistory packageHistory =
- getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
+ getPackageHistory(userHistory, packageName, elapsedRealtime);
if (packageHistory == null) {
return false; // Default to not idle
} else {
- return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
+ return hasPassedThresholds(packageHistory, elapsedRealtime);
}
}
- private long getElapsedTimeLocked(long elapsedRealtime) {
+ private long getElapsedTime(long elapsedRealtime) {
return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
}
- public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
- PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+ public void setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
+ ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
+ PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime);
- packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime)
+ packageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime)
- mElapsedTimeThreshold;
- packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime)
+ packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime)
- (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
}
- public void clearUsageLocked(String packageName, int userId) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+ public void clearUsage(String packageName, int userId) {
+ ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
userHistory.remove(packageName);
}
- private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
+ private boolean hasPassedThresholds(PackageHistory packageHistory, long elapsedRealtime) {
return (packageHistory.lastUsedScreenTime
- <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
+ <= getScreenOnTime(elapsedRealtime) - mScreenOnTimeThreshold)
&& (packageHistory.lastUsedElapsedTime
- <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
+ <= getElapsedTime(elapsedRealtime) - mElapsedTimeThreshold);
}
private File getUserFile(int userId) {
@@ -290,7 +289,7 @@
Integer.toString(userId)), APP_IDLE_FILENAME);
}
- private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) {
+ private void readAppIdleTimes(int userId, ArrayMap<String, PackageHistory> userHistory) {
FileInputStream fis = null;
try {
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
@@ -332,7 +331,7 @@
}
}
- public void writeAppIdleTimesLocked(int userId) {
+ public void writeAppIdleTimes(int userId) {
FileOutputStream fos = null;
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
try {
@@ -346,7 +345,7 @@
xml.startTag(null, TAG_PACKAGES);
- ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId);
+ ArrayMap<String,PackageHistory> userHistory = getUserHistory(userId);
final int N = userHistory.size();
for (int i = 0; i < N; i++) {
String packageName = userHistory.keyAt(i);
@@ -374,8 +373,8 @@
idpw.increaseIndent();
ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime);
- final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime);
+ final long totalElapsedTime = getElapsedTime(elapsedRealtime);
+ final long screenOnTime = getScreenOnTime(elapsedRealtime);
if (userHistory == null) return;
final int P = userHistory.size();
for (int p = 0; p < P; p++) {
@@ -386,15 +385,15 @@
TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
idpw.print(" lastUsedScreenOn=");
TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
- idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
+ idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.println();
}
idpw.println();
idpw.print("totalElapsedTime=");
- TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw);
+ TimeUtils.formatDuration(getElapsedTime(elapsedRealtime), idpw);
idpw.println();
idpw.print("totalScreenOnTime=");
- TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw);
+ TimeUtils.formatDuration(getScreenOnTime(elapsedRealtime), idpw);
idpw.println();
idpw.decreaseIndent();
}
@@ -410,7 +409,7 @@
for (int i = 0; i < HISTORY_SIZE; i++) {
idpw.print(history[i] == 0 ? '.' : 'A');
}
- idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
+ idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.print(" " + packageName);
idpw.println();
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3c743b5..469a8f1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -153,13 +153,17 @@
private volatile boolean mPendingOneTimeCheckIdleStates;
private boolean mSystemServicesReady = false;
- @GuardedBy("mLock")
+ private final Object mAppIdleLock = new Object();
+ @GuardedBy("mAppIdleLock")
private AppIdleHistory mAppIdleHistory;
+ @GuardedBy("mAppIdleLock")
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
+ @GuardedBy("mAppIdleLock")
private boolean mHaveCarrierPrivilegedApps;
+ @GuardedBy("mAppIdleLock")
private List<String> mCarrierPrivilegedApps;
public UsageStatsService(Context context) {
@@ -206,6 +210,8 @@
synchronized (mLock) {
cleanUpRemovedUsersLocked();
+ }
+ synchronized (mAppIdleLock) {
mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
}
@@ -234,8 +240,8 @@
mPowerManager = getContext().getSystemService(PowerManager.class);
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
- synchronized (mLock) {
- mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
}
if (mPendingOneTimeCheckIdleStates) {
@@ -324,8 +330,8 @@
@Override public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
final boolean displayOn = isDisplayOn();
- synchronized (UsageStatsService.this.mLock) {
- mAppIdleHistory.updateDisplayLocked(displayOn, SystemClock.elapsedRealtime());
+ synchronized (UsageStatsService.this.mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
}
}
}
@@ -386,18 +392,20 @@
PackageManager.MATCH_DISABLED_COMPONENTS,
userId);
final int packageCount = packages.size();
- for (int i = 0; i < packageCount; i++) {
- final PackageInfo pi = packages.get(i);
- String packageName = pi.packageName;
- if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+ mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+ }
}
}
}
void clearAppIdleForPackage(String packageName, int userId) {
- synchronized (mLock) {
- mAppIdleHistory.clearUsageLocked(packageName, userId);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.clearUsage(packageName, userId);
}
}
@@ -429,7 +437,7 @@
}
void setChargingState(boolean charging) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
if (mCharging != charging) {
mCharging = charging;
postParoleStateChanged();
@@ -439,15 +447,16 @@
/** Paroled here means temporary pardon from being inactive */
void setAppIdleParoled(boolean paroled) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
if (mAppIdleTempParoled != paroled) {
mAppIdleTempParoled = paroled;
if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
if (paroled) {
postParoleEndTimeout();
} else {
- mLastAppIdleParoledTime = checkAndGetTimeLocked();
- postNextParoleTimeout();
+ mLastAppIdleParoledTime = now;
+ postNextParoleTimeout(now);
}
postParoleStateChanged();
}
@@ -455,19 +464,18 @@
}
boolean isParoledOrCharging() {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
return mAppIdleTempParoled || mCharging;
}
}
- private void postNextParoleTimeout() {
+ private void postNextParoleTimeout(long now) {
if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
// Compute when the next parole needs to happen. We check more frequently than necessary
// since the message handler delays are based on elapsedRealTime and not wallclock time.
// The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis)
- - checkAndGetTimeLocked();
+ long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
if (timeLeft < 0) {
timeLeft = 0;
}
@@ -546,7 +554,7 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
userId, isIdle ? 1 : 0, packageName));
if (isIdle) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
}
}
@@ -561,18 +569,23 @@
/** Check if it's been a while since last parole and let idle apps do some work */
void checkParoleTimeout() {
- synchronized (mLock) {
+ boolean setParoled = false;
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
if (!mAppIdleTempParoled) {
- final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
+ final long timeSinceLastParole = now - mLastAppIdleParoledTime;
if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- setAppIdleParoled(true);
+ setParoled = true;
} else {
if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout();
+ postNextParoleTimeout(now);
}
}
}
+ if (setParoled) {
+ setAppIdleParoled(true);
+ }
}
private void notifyBatteryStats(String packageName, int userId, boolean idle) {
@@ -593,17 +606,23 @@
void onDeviceIdleModeChanged() {
final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
- synchronized (mLock) {
- final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
+ boolean paroled = false;
+ synchronized (mAppIdleLock) {
+ final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
if (!deviceIdle
&& timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
- if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
- setAppIdleParoled(true);
+ if (DEBUG) {
+ Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ }
+ paroled = true;
} else if (deviceIdle) {
if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
- setAppIdleParoled(false);
+ paroled = false;
+ } else {
+ return;
}
}
+ setAppIdleParoled(paroled);
}
private static void deleteRecursively(File f) {
@@ -682,21 +701,24 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
- // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
- // about apps that are on some kind of whitelist anyway.
- final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
- event.mPackage, userId, elapsedRealtime);
service.reportEvent(event);
- // Inform listeners if necessary
- if ((event.mEventType == Event.MOVE_TO_FOREGROUND
- || event.mEventType == Event.MOVE_TO_BACKGROUND
- || event.mEventType == Event.SYSTEM_INTERACTION
- || event.mEventType == Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
- if (previouslyIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ 0, event.mPackage));
- notifyBatteryStats(event.mPackage, userId, false);
+
+ synchronized (mAppIdleLock) {
+ // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+ // about apps that are on some kind of whitelist anyway.
+ final boolean previouslyIdle = mAppIdleHistory.isIdle(
+ event.mPackage, userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if ((event.mEventType == Event.MOVE_TO_FOREGROUND
+ || event.mEventType == Event.MOVE_TO_BACKGROUND
+ || event.mEventType == Event.SYSTEM_INTERACTION
+ || event.mEventType == Event.USER_INTERACTION)) {
+ mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ if (previouslyIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ 0, event.mPackage));
+ notifyBatteryStats(event.mPackage, userId, false);
+ }
}
}
}
@@ -716,7 +738,7 @@
continue;
}
if (!packageName.equals(providerPkgName)) {
- forceIdleState(packageName, userId, false);
+ setAppIdleAsync(packageName, false, userId);
}
} catch (NameNotFoundException e) {
// Shouldn't happen
@@ -728,25 +750,28 @@
* Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
* then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
* the threshold for idle.
+ *
+ * This method is always called from the handler thread, so not much synchronization is
+ * required.
*/
void forceIdleState(String packageName, int userId, boolean idle) {
final int appId = getAppId(packageName);
if (appId < 0) return;
- synchronized (mLock) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
- final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime);
- final boolean stillIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- // Inform listeners if necessary
- if (previouslyIdle != stillIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ stillIdle ? 1 : 0, packageName));
- if (!stillIdle) {
- notifyBatteryStats(packageName, userId, idle);
- }
+ final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+ }
+ final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if (previouslyIdle != stillIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ stillIdle ? 1 : 0, packageName));
+ if (!stillIdle) {
+ notifyBatteryStats(packageName, userId, idle);
}
}
}
@@ -767,7 +792,9 @@
synchronized (mLock) {
Slog.i(TAG, "Removing user " + userId + " and all data.");
mUserState.remove(userId);
- mAppIdleHistory.onUserRemoved(userId);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.onUserRemoved(userId);
+ }
cleanUpRemovedUsersLocked();
}
}
@@ -822,13 +849,13 @@
}
private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
- synchronized (mLock) {
- return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
}
}
void addListener(AppIdleStateChangeListener listener) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
if (!mPackageAccessListeners.contains(listener)) {
mPackageAccessListeners.add(listener);
}
@@ -836,7 +863,7 @@
}
void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
mPackageAccessListeners.remove(listener);
}
}
@@ -988,7 +1015,7 @@
return res;
}
- void setAppIdle(String packageName, boolean idle, int userId) {
+ void setAppIdleAsync(String packageName, boolean idle, int userId) {
if (packageName == null) return;
mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
@@ -1012,9 +1039,9 @@
}
private boolean isCarrierApp(String packageName) {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLocked();
+ fetchCarrierPrivilegedAppsLA();
}
if (mCarrierPrivilegedApps != null) {
return mCarrierPrivilegedApps.contains(packageName);
@@ -1027,13 +1054,14 @@
if (DEBUG) {
Slog.i(TAG, "Clearing carrier privileged apps list");
}
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
mHaveCarrierPrivilegedApps = false;
mCarrierPrivilegedApps = null; // Need to be refetched.
}
}
- private void fetchCarrierPrivilegedAppsLocked() {
+ @GuardedBy("mAppIdleLock")
+ private void fetchCarrierPrivilegedAppsLA() {
TelephonyManager telephonyManager =
getContext().getSystemService(TelephonyManager.class);
mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
@@ -1071,11 +1099,15 @@
for (int i = 0; i < userCount; i++) {
UserUsageStatsService service = mUserState.valueAt(i);
service.persistActiveStats();
- mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
+ }
}
// Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
// considered not-idle, which is the safest outcome in such an event.
- mAppIdleHistory.writeAppIdleDurationsLocked();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleDurations();
+ }
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
@@ -1100,20 +1132,26 @@
idpw.println();
if (args.length > 0) {
if ("history".equals(args[0])) {
- mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
+ }
} else if ("flush".equals(args[0])) {
UsageStatsService.this.flushToDiskLocked();
pw.println("Flushed stats to disk");
}
}
}
- mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+ }
idpw.decreaseIndent();
}
pw.println();
- pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
- + "): " + mCarrierPrivilegedApps);
+ synchronized (mAppIdleLock) {
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
+ }
pw.println();
pw.println("Settings:");
@@ -1252,7 +1290,7 @@
}
void updateSettings() {
- synchronized (mLock) {
+ synchronized (mAppIdleLock) {
// Look at global settings for this.
// TODO: Maybe apply different thresholds for different users.
try {
@@ -1384,7 +1422,7 @@
try {
userId = ActivityManager.getService().handleIncomingUser(
Binder.getCallingPid(), callingUid, userId, false, true,
- "setAppIdle", null);
+ "setAppInactive", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1394,7 +1432,7 @@
try {
final int appId = getAppId(packageName);
if (appId < 0) return;
- UsageStatsService.this.setAppIdle(packageName, idle, userId);
+ UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1586,21 +1624,25 @@
@Override
public byte[] getBackupPayload(int user, String key) {
// Check to ensure that only user 0's data is b/r for now
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats =
- getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
- return userStats.getBackupPayload(key);
- } else {
- return null;
+ synchronized (UsageStatsService.this.mLock) {
+ if (user == UserHandle.USER_SYSTEM) {
+ final UserUsageStatsService userStats =
+ getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
+ return userStats.getBackupPayload(key);
+ } else {
+ return null;
+ }
}
}
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats =
- getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
- userStats.applyRestoredPayload(key, payload);
+ synchronized (UsageStatsService.this.mLock) {
+ if (user == UserHandle.USER_SYSTEM) {
+ final UserUsageStatsService userStats =
+ getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
+ userStats.applyRestoredPayload(key, payload);
+ }
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d9f352c..8d335a5 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -529,7 +529,6 @@
pw.decreaseIndent();
pw.println();
- pw.increaseIndent();
pw.println("ChooserCounts");
pw.increaseIndent();
for (UsageStats usageStats : pkgStats.values()) {
@@ -553,6 +552,7 @@
}
pw.println();
}
+ pw.decreaseIndent();
pw.println("configurations");
pw.increaseIndent();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 70df69c..7bb58dd4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -942,7 +942,7 @@
/**
* Defines carrier-specific actions which act upon
- * android.intent.action.CARRIER_SIGNAL_REDIRECTED, used for customization of the
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the
* default carrier app
* Format: "CARRIER_ACTION_IDX, ..."
* Where {@code CARRIER_ACTION_IDX} is an integer defined in
@@ -957,7 +957,7 @@
/**
* Defines carrier-specific actions which act upon
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* and configured signal args:
* {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType},
* {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode}
@@ -998,11 +998,11 @@
* @see com.android.internal.telephony.TelephonyIntents
* Example:
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
- * android.intent.action.CARRIER_SIGNAL_REDIRECTED,
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* @hide
*/
@@ -1017,11 +1017,11 @@
* @see com.android.internal.telephony.TelephonyIntents
* Example:
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* </item>
* @hide
*/
@@ -1493,7 +1493,7 @@
sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{
"com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
- "android.intent.action.CARRIER_SIGNAL_REDIRECTED"
+ "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"
});
sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index dd03305..afff6d5 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -208,7 +208,9 @@
*
* @see #onOemHookRawEvent
* @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
*/
+ @Deprecated
public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a4235d7..f5974cd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5229,7 +5229,9 @@
* 0 request was handled succesfully, but no response data
* positive value success, data length of response
* @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
*/
+ @Deprecated
public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
try {
ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..69b8acc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods in the
+ * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ @Override
+ public String getCallId() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getLocalCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getRemoteCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ @Override
+ public String getProperty(String name) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link ImsCallSession.State}.
+ *
+ * @return the session state
+ */
+ @Override
+ public int getState() throws RemoteException {
+ return ImsCallSession.State.INVALID;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call, false otherwise
+ */
+ @Override
+ public boolean isInCall() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ @Override
+ public void setListener(IImsCallSessionListener listener) throws RemoteException {
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ @Override
+ public void setMute(boolean muted) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void start(String callee, ImsCallProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile)
+ throws RemoteException {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in
+ * com.android.ims.ImsReasonInfo
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void reject(int reason) throws RemoteException {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in
+ * com.android.ims.ImsReasonInfo
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ @Override
+ public void terminate(int reason) throws RemoteException {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ @Override
+ public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ @Override
+ public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Merges the active & hold call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ @Override
+ public void merge() throws RemoteException {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ @Override
+ public void extendToConference(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ @Override
+ public void inviteParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ @Override
+ public void removeParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void sendDtmf(char c, Message result) throws RemoteException {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void startDtmf(char c) throws RemoteException {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ @Override
+ public void stopDtmf() throws RemoteException {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ @Override
+ public void sendUssd(String ussdMessage) throws RemoteException {
+ }
+
+ /**
+ * Returns a binder for the video call provider implementation contained within the IMS service
+ * process. This binder is used by the VideoCallProvider subclass in Telephony which
+ * intermediates between the propriety implementation and Telecomm/InCall.
+ */
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ @Override
+ public boolean isMultiparty() throws RemoteException {
+ return false;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
new file mode 100644
index 0000000..46f8f80
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
+ * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
+ * ImsCallSessionListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call merge operation.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeComplete(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call
+ * updates.
+ */
+ @Override
+ public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from the
+ * conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the changes of the conference info. the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
+ String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the TTY mode change by remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this
+ * {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty,
+ * {@code false} otherwise.
+ */
+ @Override
+ public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Notifies the supplementary service information for the current session.
+ */
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession session,
+ ImsSuppServiceNotification suppSrvNotification) {
+ // no-op
+ }
+}
+
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..5a4db99
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Base implementation of ImsConfig, which implements stub versions of the methods
+ * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase extends IImsConfig.Stub {
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public int getProvisionedValue(int item) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public String getProvisionedStringValue(int item) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedValue(int item, int value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedStringValue(int item, String value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value of the specified IMS feature item for specified network type.
+ * This operation gets the feature config value from the master storage (i.e. final
+ * value). Asynchronous non-blocking call.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener feature value returned asynchronously through listener.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ * This operation stores the user setting in setting db from which master db
+ * is derived.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Gets the value for IMS VoLTE provisioned.
+ * This should be the same as the operator provisioned value if applies.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Gets the value for IMS feature item video quality.
+ *
+ * @param listener Video quality value returned asynchronously through listener.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item video quality.
+ *
+ * @param quality, defines the value of video quality.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 0000000..89f95ff
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsEcbmImplBase extends IImsEcbm.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsEcbmListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Requests Modem to come out of ECBM mode
+ */
+ @Override
+ public void exitEmergencyCallbackMode() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 0000000..05da9da
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Query API to get the latest Dialog Event Package information
+ * Should be invoked only after setListener is done
+ */
+ @Override
+ public void requestImsExternalCallStateInfo() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 0000000..92f1a01
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+ @Override
+ public void close() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 0000000..dc74094
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUt, which implements stub versions of the methods
+ * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUt maintained by other ImsServices.
+ *
+ * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ *
+ * @hide
+ */
+
+public class ImsUtImplBase extends IImsUt.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Retrieves the configuration of the call barring.
+ */
+ @Override
+ public int queryCallBarring(int cbType) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call forward.
+ */
+ @Override
+ public int queryCallForward(int condition, String number) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ */
+ @Override
+ public int queryCallWaiting() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ @Override
+ public int queryCLIR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ @Override
+ public int queryCLIP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ @Override
+ public int queryCOLR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ @Override
+ public int queryCOLP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates or retrieves the supplementary service configuration.
+ */
+ @Override
+ public int transact(Bundle ssInfo) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring.
+ */
+ @Override
+ public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call forward.
+ */
+ @Override
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Updates the configuration of the call waiting.
+ */
+ @Override
+ public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ @Override
+ public int updateCLIR(int clirMode) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ @Override
+ public int updateCLIP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ @Override
+ public int updateCOLR(int presentation) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ @Override
+ public int updateCOLP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsUtListener listener) throws RemoteException {
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..b371efb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ @Override
+ public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ @Override
+ public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ @Override
+ public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ @Override
+ public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ @Override
+ public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+ throws RemoteException {
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
new file mode 100644
index 0000000..cd076b1
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+public class ImsConfig {
+ private static final String TAG = "ImsConfig";
+ private boolean DBG = true;
+ private final IImsConfig miConfig;
+ private Context mContext;
+
+ /**
+ * Broadcast action: the feature enable status was changed
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_FEATURE_CHANGED =
+ "com.android.intent.action.IMS_FEATURE_CHANGED";
+
+ /**
+ * Broadcast action: the configuration was changed
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_CONFIG_CHANGED =
+ "com.android.intent.action.IMS_CONFIG_CHANGED";
+
+ /**
+ * Extra parameter "item" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+ * It is the value of FeatureConstants or ConfigConstants.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CHANGED_ITEM = "item";
+
+ /**
+ * Extra parameter "value" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+ * It is the new value of "item".
+ *
+ * @hide
+ */
+ public static final String EXTRA_NEW_VALUE = "value";
+
+ /**
+ * Defines IMS service/capability feature constants.
+ */
+ public static class FeatureConstants {
+ public static final int FEATURE_TYPE_UNKNOWN = -1;
+
+ /**
+ * FEATURE_TYPE_VOLTE supports features defined in 3GPP and
+ * GSMA IR.92 over LTE.
+ */
+ public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
+
+ /**
+ * FEATURE_TYPE_LVC supports features defined in 3GPP and
+ * GSMA IR.94 over LTE.
+ */
+ public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
+
+ /**
+ * FEATURE_TYPE_VOICE_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.92 over WiFi.
+ */
+ public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
+
+ /**
+ * FEATURE_TYPE_VIDEO_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.94 over WiFi.
+ */
+ public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
+
+ /**
+ * FEATURE_TYPE_UT supports features defined in 3GPP and
+ * GSMA IR.92 over LTE.
+ */
+ public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
+
+ /**
+ * FEATURE_TYPE_UT_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.92 over WiFi.
+ */
+ public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
+ }
+
+ /**
+ * Defines IMS service/capability parameters.
+ */
+ public static class ConfigConstants {
+
+ // Define IMS config items
+ public static final int CONFIG_START = 0;
+
+ // Define operator provisioned config items
+ public static final int PROVISIONED_CONFIG_START = CONFIG_START;
+
+ /**
+ * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+ * Value is in String format.
+ */
+ public static final int VOCODER_AMRMODESET = CONFIG_START;
+
+ /**
+ * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+ * Value is in String format.
+ */
+ public static final int VOCODER_AMRWBMODESET = 1;
+
+ /**
+ * SIP Session Timer value (seconds).
+ * Value is in Integer format.
+ */
+ public static final int SIP_SESSION_TIMER = 2;
+
+ /**
+ * Minimum SIP Session Expiration Timer in (seconds).
+ * Value is in Integer format.
+ */
+ public static final int MIN_SE = 3;
+
+ /**
+ * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+ * Value is in Integer format.
+ */
+ public static final int CANCELLATION_TIMER = 4;
+
+ /**
+ * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE.
+ * Value is in Integer format.
+ */
+ public static final int TDELAY = 5;
+
+ /**
+ * Silent redial status of Enabled (True), or Disabled (False).
+ * Value is in Integer format.
+ */
+ public static final int SILENT_REDIAL_ENABLE = 6;
+
+ /**
+ * SIP T1 timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_T1_TIMER = 7;
+
+ /**
+ * SIP T2 timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_T2_TIMER = 8;
+
+ /**
+ * SIP TF timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_TF_TIMER = 9;
+
+ /**
+ * VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VLT_SETTING_ENABLED = 10;
+
+ /**
+ * VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int LVC_SETTING_ENABLED = 11;
+ /**
+ * Domain Name for the device to populate the request URI for REGISTRATION.
+ * Value is in String format.
+ */
+ public static final int DOMAIN_NAME = 12;
+ /**
+ * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+ * Value is in Integer format. 3GPP2(0), 3GPP(1)
+ */
+ public static final int SMS_FORMAT = 13;
+ /**
+ * Turns IMS ON/OFF on the device.
+ * Value is in Integer format. ON (1), OFF(0).
+ */
+ public static final int SMS_OVER_IP = 14;
+ /**
+ * Requested expiration for Published Online availability.
+ * Value is in Integer format.
+ */
+ public static final int PUBLISH_TIMER = 15;
+ /**
+ * Requested expiration for Published Offline availability.
+ * Value is in Integer format.
+ */
+ public static final int PUBLISH_TIMER_EXTENDED = 16;
+ /**
+ *
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITY_DISCOVERY_ENABLED = 17;
+ /**
+ * Period of time the capability information of the contact is cached on handset.
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITIES_CACHE_EXPIRATION = 18;
+ /**
+ * Peiod of time the availability information of a contact is cached on device.
+ * Value is in Integer format.
+ */
+ public static final int AVAILABILITY_CACHE_EXPIRATION = 19;
+ /**
+ * Interval between successive capabilities polling.
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITIES_POLL_INTERVAL = 20;
+ /**
+ * Minimum time between two published messages from the device.
+ * Value is in Integer format.
+ */
+ public static final int SOURCE_THROTTLE_PUBLISH = 21;
+ /**
+ * The Maximum number of MDNs contained in one Request Contained List.
+ * Value is in Integer format.
+ */
+ public static final int MAX_NUMENTRIES_IN_RCL = 22;
+ /**
+ * Expiration timer for subscription of a Request Contained List, used in capability
+ * polling.
+ * Value is in Integer format.
+ */
+ public static final int CAPAB_POLL_LIST_SUB_EXP = 23;
+ /**
+ * Applies compression to LIST Subscription.
+ * Value is in Integer format. Enable (1), Disable(0).
+ */
+ public static final int GZIP_FLAG = 24;
+ /**
+ * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int EAB_SETTING_ENABLED = 25;
+ /**
+ * Wi-Fi calling roaming status.
+ * Value is in Integer format. ON (1), OFF(0).
+ */
+ public static final int VOICE_OVER_WIFI_ROAMING = 26;
+ /**
+ * Wi-Fi calling modem - WfcModeFeatureValueConstants.
+ * Value is in Integer format.
+ */
+ public static final int VOICE_OVER_WIFI_MODE = 27;
+ /**
+ * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+ /**
+ * Mobile data enabled.
+ * Value is in Integer format. On (1), OFF(0).
+ */
+ public static final int MOBILE_DATA_ENABLED = 29;
+ /**
+ * VoLTE user opted in status.
+ * Value is in Integer format. Opted-in (1) Opted-out (0).
+ */
+ public static final int VOLTE_USER_OPT_IN_STATUS = 30;
+ /**
+ * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+ * Value is in String format.
+ */
+ public static final int LBO_PCSCF_ADDRESS = 31;
+ /**
+ * Keep Alive Enabled for SIP.
+ * Value is in Integer format. On(1), OFF(0).
+ */
+ public static final int KEEP_ALIVE_ENABLED = 32;
+ /**
+ * Registration retry Base Time value in seconds.
+ * Value is in Integer format.
+ */
+ public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+ /**
+ * Registration retry Max Time value in seconds.
+ * Value is in Integer format.
+ */
+ public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+ /**
+ * Smallest RTP port for speech codec.
+ * Value is in integer format.
+ */
+ public static final int SPEECH_START_PORT = 35;
+ /**
+ * Largest RTP port for speech code.
+ * Value is in Integer format.
+ */
+ public static final int SPEECH_END_PORT = 36;
+ /**
+ * SIP Timer A's value in milliseconds. Timer A is the INVITE request
+ * retransmit interval, for UDP only.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
+ /**
+ * SIP Timer B's value in milliseconds. Timer B is the wait time for
+ * INVITE message to be acknowledged.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
+ /**
+ * SIP Timer D's value in milliseconds. Timer D is the wait time for
+ * response retransmits of the invite client transactions.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
+ /**
+ * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
+ * request retransmit interval, for UDP only.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
+ /**
+ * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
+ * timeout timer.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
+ /**
+ * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+ * retransmit interval.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
+ /**
+ * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+ * ACK receipt.
+ * Value is in Integer format.
+ */
+ public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
+ /**
+ * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+ * ACK retransmits.
+ * Value is in Integer format.
+ */
+ public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
+ /**
+ * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+ * non-invite request retransmission.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
+ /**
+ * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+ * non-invite response retransmits.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
+ /**
+ * AMR WB octet aligned dynamic payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
+ /**
+ * AMR WB bandwidth efficient payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
+ /**
+ * AMR octet aligned dynamic payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_OCTET_ALIGNED_PT = 49;
+ /**
+ * AMR bandwidth efficient payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
+ /**
+ * DTMF WB payload type.
+ * Value is in Integer format.
+ */
+ public static final int DTMF_WB_PT = 51;
+ /**
+ * DTMF NB payload type.
+ * Value is in Integer format.
+ */
+ public static final int DTMF_NB_PT = 52;
+ /**
+ * AMR Default encoding mode.
+ * Value is in Integer format.
+ */
+ public static final int AMR_DEFAULT_MODE = 53;
+ /**
+ * SMS Public Service Identity.
+ * Value is in String format.
+ */
+ public static final int SMS_PSI = 54;
+ /**
+ * Video Quality - VideoQualityFeatureValuesConstants.
+ * Value is in Integer format.
+ */
+ public static final int VIDEO_QUALITY = 55;
+ /**
+ * LTE threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ */
+ public static final int TH_LTE1 = 56;
+ /**
+ * LTE threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int TH_LTE2 = 57;
+ /**
+ * LTE threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int TH_LTE3 = 58;
+ /**
+ * 1x threshold.
+ * Handover from 1x to WiFi if 1x < TH1x
+ */
+ public static final int TH_1x = 59;
+ /**
+ * WiFi threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ */
+ public static final int VOWT_A = 60;
+ /**
+ * WiFi threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int VOWT_B = 61;
+ /**
+ * LTE ePDG timer.
+ * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+ */
+ public static final int T_EPDG_LTE = 62;
+ /**
+ * WiFi ePDG timer.
+ * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+ */
+ public static final int T_EPDG_WIFI = 63;
+ /**
+ * 1x ePDG timer.
+ * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ */
+ public static final int T_EPDG_1X = 64;
+ /**
+ * MultiEndpoint status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VICE_SETTING_ENABLED = 65;
+
+ // Expand the operator config items as needed here, need to change
+ // PROVISIONED_CONFIG_END after that.
+ public static final int PROVISIONED_CONFIG_END = VICE_SETTING_ENABLED;
+
+ // Expand the operator config items as needed here.
+ }
+
+ /**
+ * Defines IMS set operation status.
+ */
+ public static class OperationStatusConstants {
+ public static final int UNKNOWN = -1;
+ public static final int SUCCESS = 0;
+ public static final int FAILED = 1;
+ public static final int UNSUPPORTED_CAUSE_NONE = 2;
+ public static final int UNSUPPORTED_CAUSE_RAT = 3;
+ public static final int UNSUPPORTED_CAUSE_DISABLED = 4;
+ }
+
+ /**
+ * Defines IMS get operation values.
+ */
+ public static class OperationValuesConstants {
+ /**
+ * Values related to Video Quality
+ */
+ public static final int VIDEO_QUALITY_UNKNOWN = -1;
+ public static final int VIDEO_QUALITY_LOW = 0;
+ public static final int VIDEO_QUALITY_HIGH = 1;
+ }
+
+ /**
+ * Defines IMS video quality feature value.
+ */
+ public static class VideoQualityFeatureValuesConstants {
+ public static final int LOW = 0;
+ public static final int HIGH = 1;
+ }
+
+ /**
+ * Defines IMS feature value.
+ */
+ public static class FeatureValueConstants {
+ public static final int OFF = 0;
+ public static final int ON = 1;
+ }
+
+ /**
+ * Defines IMS feature value.
+ */
+ public static class WfcModeFeatureValueConstants {
+ public static final int WIFI_ONLY = 0;
+ public static final int CELLULAR_PREFERRED = 1;
+ public static final int WIFI_PREFERRED = 2;
+ }
+
+ public ImsConfig(IImsConfig iconfig, Context context) {
+ if (DBG) Rlog.d(TAG, "ImsConfig creates");
+ miConfig = iconfig;
+ mContext = context;
+ }
+
+ /**
+ * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+ * This function should not be called from the mainthread as it could block the
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return the value in Integer format.
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int getProvisionedValue(int item) throws ImsException {
+ int ret = 0;
+ try {
+ ret = miConfig.getProvisionedValue(item);
+ } catch (RemoteException e) {
+ throw new ImsException("getValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret);
+
+ return ret;
+ }
+
+ /**
+ * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+ * This function should not be called from the mainthread as it could block the
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public String getProvisionedStringValue(int item) throws ImsException {
+ String ret = "Unknown";
+ try {
+ ret = miConfig.getProvisionedStringValue(item);
+ } catch (RemoteException e) {
+ throw new ImsException("getProvisionedStringValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret);
+
+ return ret;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by
+ * the operator device management entity.
+ * This function should not be called from main thread as it could block
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int setProvisionedValue(int item, int value)
+ throws ImsException {
+ int ret = OperationStatusConstants.UNKNOWN;
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ "value = " + value);
+ }
+ try {
+ ret = miConfig.setProvisionedValue(item, value);
+ } catch (RemoteException e) {
+ throw new ImsException("setProvisionedValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ " value = " + value + " ret = " + ret);
+ }
+ return ret;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by
+ * the operator device management entity.
+ * This function should not be called from main thread as it could block
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int setProvisionedStringValue(int item, String value)
+ throws ImsException {
+ int ret = OperationStatusConstants.UNKNOWN;
+ try {
+ ret = miConfig.setProvisionedStringValue(item, value);
+ } catch (RemoteException e) {
+ throw new ImsException("setProvisionedStringValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
+ ", value =" + value);
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the value for IMS feature item for specified network type.
+ *
+ * @param feature, defined as in FeatureConstants.
+ * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener, provided to be notified for the feature on/off status.
+ * @return void
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public void getFeatureValue(int feature, int network,
+ ImsConfigListener listener) throws ImsException {
+ if (DBG) {
+ Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network +
+ ", listener =" + listener);
+ }
+ try {
+ miConfig.getFeatureValue(feature, network, listener);
+ } catch (RemoteException e) {
+ throw new ImsException("getFeatureValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ *
+ * @param feature, as defined in FeatureConstants.
+ * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value, as defined in FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ * @return void
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public void setFeatureValue(int feature, int network, int value,
+ ImsConfigListener listener) throws ImsException {
+ if (DBG) {
+ Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
+ ", value =" + value + ", listener =" + listener);
+ }
+ try {
+ miConfig.setFeatureValue(feature, network, value, listener);
+ } catch (RemoteException e) {
+ throw new ImsException("setFeatureValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
new file mode 100644
index 0000000..74b20f4
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ims;
+
+/**
+ * This class defines a general IMS-related exception.
+ *
+ * @hide
+ */
+public class ImsException extends Exception {
+
+ /**
+ * Refer to CODE_LOCAL_* in {@link ImsReasonInfo}
+ */
+ private int mCode;
+
+ public ImsException() {
+ }
+
+ public ImsException(String message, int code) {
+ super(message + ", code = " + code);
+ mCode = code;
+ }
+
+ public ImsException(String message, Throwable cause, int code) {
+ super(message, cause);
+ mCode = code;
+ }
+
+ /**
+ * Gets the detailed exception code when ImsException is throwed
+ *
+ * @return the exception code in {@link ImsReasonInfo}
+ */
+ public int getCode() {
+ return mCode;
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index c71808c..e4f380f 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -313,6 +313,12 @@
public static final int CODE_WIFI_LOST = 1407;
/**
+ * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure
+ * during tunnel establishment.
+ */
+ public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
new file mode 100644
index 0000000..5984e78
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -0,0 +1,179 @@
+/*
+ * 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.ims;
+
+import android.os.Message;
+
+/**
+ * Provides APIs for the supplementary service settings using IMS (Ut interface).
+ * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
+ * over the Ut interface for manipulating supplementary services).
+ *
+ * @hide
+ */
+public interface ImsUtInterface {
+ /**
+ * Actions
+ * @hide
+ */
+ public static final int ACTION_DEACTIVATION = 0;
+ public static final int ACTION_ACTIVATION = 1;
+ public static final int ACTION_REGISTRATION = 3;
+ public static final int ACTION_ERASURE = 4;
+ public static final int ACTION_INTERROGATION = 5;
+
+ /**
+ * OIR (Originating Identification Restriction, 3GPP TS 24.607)
+ * OIP (Originating Identification Presentation, 3GPP TS 24.607)
+ * TIR (Terminating Identification Restriction, 3GPP TS 24.608)
+ * TIP (Terminating Identification Presentation, 3GPP TS 24.608)
+ */
+ public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+
+ /**
+ * CW (Communication Waiting, 3GPP TS 24.615)
+ */
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604)
+ * actions: target, no reply timer
+ */
+ public static final int CDIV_CF_UNCONDITIONAL = 0;
+ public static final int CDIV_CF_BUSY = 1;
+ public static final int CDIV_CF_NO_REPLY = 2;
+ public static final int CDIV_CF_NOT_REACHABLE = 3;
+ // For CS service code: 002
+ public static final int CDIV_CF_ALL = 4;
+ // For CS service code: 004
+ public static final int CDIV_CF_ALL_CONDITIONAL = 5;
+ // It's only supported in the IMS service (CS does not define it).
+ // IR.92 recommends that an UE activates both the CFNRc and the CFNL
+ // (CDIV using condition not-registered) to the same target.
+ public static final int CDIV_CF_NOT_LOGGED_IN = 6;
+
+ /**
+ * CB (Communication Barring, 3GPP TS 24.611)
+ */
+ // Barring of All Incoming Calls
+ public static final int CB_BAIC = 1;
+ // Barring of All Outgoing Calls
+ public static final int CB_BAOC = 2;
+ // Barring of Outgoing International Calls
+ public static final int CB_BOIC = 3;
+ // Barring of Outgoing International Calls - excluding Home Country
+ public static final int CB_BOIC_EXHC = 4;
+ // Barring of Incoming Calls - when roaming
+ public static final int CB_BIC_WR = 5;
+ // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service
+ public static final int CB_BIC_ACR = 6;
+ // Barring of All Calls
+ public static final int CB_BA_ALL = 7;
+ // Barring of Outgoing Services (Service Code 333 - 3GPP TS 22.030 Table B-1)
+ public static final int CB_BA_MO = 8;
+ // Barring of Incoming Services (Service Code 353 - 3GPP TS 22.030 Table B-1)
+ public static final int CB_BA_MT = 9;
+ // Barring of Specific Incoming calls
+ public static final int CB_BS_MT = 10;
+
+ /**
+ * Invalid result value.
+ */
+ public static final int INVALID = (-1);
+
+
+
+ /**
+ * Operations for the supplementary service configuration
+ */
+
+ /**
+ * Retrieves the configuration of the call barring.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallBarring(int cbType, Message result);
+
+ /**
+ * Retrieves the configuration of the call forward.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}.
+ */
+ public void queryCallForward(int condition, String number, Message result);
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallWaiting(Message result);
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ public void queryCLIR(Message result);
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ public void queryCLIP(Message result);
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ public void queryCOLR(Message result);
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ public void queryCOLP(Message result);
+
+ /**
+ * Modifies the configuration of the call barring.
+ */
+ public void updateCallBarring(int cbType, int action,
+ Message result, String[] barrList);
+
+ /**
+ * Modifies the configuration of the call forward.
+ */
+ public void updateCallForward(int action, int condition, String number,
+ int serviceClass, int timeSeconds, Message result);
+
+ /**
+ * Modifies the configuration of the call waiting.
+ */
+ public void updateCallWaiting(boolean enable, int serviceClass, Message result);
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ public void updateCLIR(int clirMode, Message result);
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ public void updateCLIP(boolean enable, Message result);
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ public void updateCOLR(int presentation, Message result);
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ public void updateCOLP(boolean enable, Message result);
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
index 82a13dc..df10700 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
@@ -17,7 +17,8 @@
package com.android.ims.internal;
/**
-* Interface from ImsResolver to ImsServiceProxy in ImsManager.
+ * Interface from ImsResolver to ImsServiceProxy in ImsManager.
+ * Callback to ImsManager when a feature changes in the ImsServiceController.
* {@hide}
*/
oneway interface IImsServiceFeatureListener {
diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/com/android/ims/internal/ImsCallSession.java
new file mode 100644
index 0000000..8196b23
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsCallSession.java
@@ -0,0 +1,1290 @@
+/*
+ * 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.ims.internal;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+import android.telephony.ims.stub.ImsCallSessionListenerImplBase;
+import android.util.Log;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+ private static final String TAG = "ImsCallSession";
+
+ /**
+ * Defines IMS call session state.
+ */
+ public static class State {
+ public static final int IDLE = 0;
+ public static final int INITIATED = 1;
+ public static final int NEGOTIATING = 2;
+ public static final int ESTABLISHING = 3;
+ public static final int ESTABLISHED = 4;
+
+ public static final int RENEGOTIATING = 5;
+ public static final int REESTABLISHING = 6;
+
+ public static final int TERMINATING = 7;
+ public static final int TERMINATED = 8;
+
+ public static final int INVALID = (-1);
+
+ /**
+ * Converts the state to string.
+ */
+ public static String toString(int state) {
+ switch (state) {
+ case IDLE:
+ return "IDLE";
+ case INITIATED:
+ return "INITIATED";
+ case NEGOTIATING:
+ return "NEGOTIATING";
+ case ESTABLISHING:
+ return "ESTABLISHING";
+ case ESTABLISHED:
+ return "ESTABLISHED";
+ case RENEGOTIATING:
+ return "RENEGOTIATING";
+ case REESTABLISHING:
+ return "REESTABLISHING";
+ case TERMINATING:
+ return "TERMINATING";
+ case TERMINATED:
+ return "TERMINATED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ private State() {
+ }
+ }
+
+ /**
+ * Listener for events relating to an IMS session, such as when a session is being
+ * recieved ("on ringing") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+ */
+ public static class Listener {
+ /**
+ * Called when a request is sent out to initiate a new session
+ * and 1xx response is received from the network.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionProgressing(ImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is established.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionStarted(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session establishment is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session establishment failure
+ */
+ public void callSessionStartFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is terminated.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session termination
+ */
+ public void callSessionTerminated(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is in hold.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHeld(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session hold is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session hold failure
+ */
+ public void callSessionHoldFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session hold is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHoldReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is done.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumed(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session resume failure
+ */
+ public void callSessionResumeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session resume is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumeReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge has been started. At this point, the {@code newSession}
+ * represents the session which has been initiated to the IMS conference server for the
+ * new merged conference.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is merged with an active & hold session
+ */
+ public void callSessionMergeStarted(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge is successful and the merged session is active.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionMergeComplete(ImsCallSession session) {
+ }
+
+ /**
+ * Called when the session merge has failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the call merge failure
+ */
+ public void callSessionMergeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is updated (except for hold/unhold).
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdated(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session update is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session update failure
+ */
+ public void callSessionUpdateFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session update is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdateReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is extended to the conference session.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is extended to the conference
+ * from the active session
+ */
+ public void callSessionConferenceExtended(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the conference extension is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference extension failure
+ */
+ public void callSessionConferenceExtendFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the conference extension is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference invitation failure
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference removal failure
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the conference state is updated.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceStateUpdated(ImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Called when the USSD message is received from the network.
+ *
+ * @param mode mode of the USSD message (REQUEST / NOTIFY)
+ * @param ussdMessage USSD message
+ */
+ public void callSessionUssdMessageReceived(ImsCallSession session,
+ int mode, String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Called when session access technology changes
+ *
+ * @param session IMS session object
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo
+ */
+ public void callSessionHandover(ImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when session access technology change fails
+ *
+ * @param session IMS session object
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo handover failure reason
+ */
+ public void callSessionHandoverFailed(ImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when TTY mode of remote party changed
+ *
+ * @param session IMS session object
+ * @param mode TTY mode of remote party
+ */
+ public void callSessionTtyModeReceived(ImsCallSession session,
+ int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(ImsCallSession session,
+ boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Called when the session supplementary service is received
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionSuppServiceReceived(ImsCallSession session,
+ ImsSuppServiceNotification suppServiceInfo) {
+ }
+ }
+
+ private final IImsCallSession miSession;
+ private boolean mClosed = false;
+ private Listener mListener;
+
+ public ImsCallSession(IImsCallSession iSession) {
+ miSession = iSession;
+
+ if (iSession != null) {
+ try {
+ iSession.setListener(new IImsCallSessionListenerProxy());
+ } catch (RemoteException e) {
+ }
+ } else {
+ mClosed = true;
+ }
+ }
+
+ public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ this(iSession);
+ setListener(listener);
+ }
+
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ public synchronized void close() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.close();
+ mClosed = true;
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ public String getCallId() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallId();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the call profile that this session is associated with
+ */
+ public ImsCallProfile getCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local call profile that this session is associated with
+ */
+ public ImsCallProfile getLocalCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getLocalCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote call profile that this session is associated with
+ */
+ public ImsCallProfile getRemoteCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getRemoteCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the video call provider for the session.
+ *
+ * @return The video call provider.
+ */
+ public IImsVideoCallProvider getVideoCallProvider() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getVideoCallProvider();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ public String getProperty(String name) {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getProperty(name);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link State}.
+ *
+ * @return the session state
+ */
+ public int getState() {
+ if (mClosed) {
+ return State.INVALID;
+ }
+
+ try {
+ return miSession.getState();
+ } catch (RemoteException e) {
+ return State.INVALID;
+ }
+ }
+
+ /**
+ * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+ * closed state).
+ *
+ * @return {@code True} if the session is alive.
+ */
+ public boolean isAlive() {
+ if (mClosed) {
+ return false;
+ }
+
+ int state = getState();
+ switch (state) {
+ case State.IDLE:
+ case State.INITIATED:
+ case State.NEGOTIATING:
+ case State.ESTABLISHING:
+ case State.ESTABLISHED:
+ case State.RENEGOTIATING:
+ case State.REESTABLISHING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets the native IMS call session.
+ * @hide
+ */
+ public IImsCallSession getSession() {
+ return miSession;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call
+ */
+ public boolean isInCall() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isInCall();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the listener to listen to the session events. A {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ public void setMute(boolean muted) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.setMute(muted);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String callee, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.start(callee, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS conference call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String[] participants, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startConference(participants, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see Listener#callSessionStarted
+ */
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.accept(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call
+ * @see Listener#callSessionStartFailed
+ */
+ public void reject(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.reject(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @see Listener#callSessionTerminated
+ */
+ public void terminate(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.terminate(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+ */
+ public void hold(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.hold(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+ * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+ */
+ public void resume(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.resume(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Merges the active & hold call. When it succeeds,
+ * {@link Listener#callSessionMergeStarted} is called.
+ *
+ * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+ */
+ public void merge() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.merge();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+ */
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.update(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants list to be invited to the conference call after extending the call
+ * @see Listener#callSessionConferenceExtended
+ * @see Listener#callSessionConferenceExtendFailed
+ */
+ public void extendToConference(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.extendToConference(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants list to be invited to the conference call
+ * @see Listener#callSessionInviteParticipantsRequestDelivered
+ * @see Listener#callSessionInviteParticipantsRequestFailed
+ */
+ public void inviteParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.inviteParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see Listener#callSessionRemoveParticipantsRequestDelivered
+ * @see Listener#callSessionRemoveParticipantsRequestFailed
+ */
+ public void removeParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.removeParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void sendDtmf(char c, Message result) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendDtmf(c, result);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void startDtmf(char c) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startDtmf(c);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Stops a DTMF code.
+ */
+ public void stopDtmf() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.stopDtmf();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ public void sendUssd(String ussdMessage) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendUssd(ussdMessage);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Determines if the session is multiparty.
+ *
+ * @return {@code True} if the session is multiparty.
+ */
+ public boolean isMultiparty() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isMultiparty();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * A listener type for receiving notification on IMS call session events.
+ * When an event is generated for an {@link IImsCallSession},
+ * the application is notified by having one of the methods called on
+ * the {@link IImsCallSessionListener}.
+ */
+ private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(IImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the start of a call merge operation.
+ *
+ * @param session The call session.
+ * @param newSession The merged call session.
+ * @param profile The call profile.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ // This callback can be used for future use to add additional
+ // functionality that may be needed between conference start and complete
+ Log.d(TAG, "callSessionMergeStarted");
+ }
+
+ /**
+ * Notifies the successful completion of a call merge operation.
+ *
+ * @param newSession The call session.
+ */
+ @Override
+ public void callSessionMergeComplete(IImsCallSession newSession) {
+ if (mListener != null) {
+ if (newSession != null) {
+ // Check if the active session is the same session that was
+ // active before the merge request was sent.
+ ImsCallSession validActiveSession = ImsCallSession.this;
+ try {
+ if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
+ // New session created after conference
+ validActiveSession = new ImsCallSession(newSession);
+ }
+ } catch (RemoteException rex) {
+ Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
+ }
+ mListener.callSessionMergeComplete(validActiveSession);
+ } else {
+ // Session already exists. Hence no need to pass
+ mListener.callSessionMergeComplete(null);
+ }
+ }
+ }
+
+ /**
+ * Notifies of a failure to perform a call merge operation.
+ *
+ * @param session The call session.
+ * @param reasonInfo The merge failure reason.
+ */
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call updates.
+ */
+ @Override
+ public void callSessionUpdated(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from
+ * the conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the changes of the conference info. in the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession session,
+ ImsConferenceState state) {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session,
+ int mode, String ussdMessage) {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+ }
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(IImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
+ targetAccessTech, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies of handover failure info for this call
+ */
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
+ targetAccessTech, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the TTY mode received from remote party.
+ */
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession session,
+ int mode) {
+ if (mListener != null) {
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ }
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(IImsCallSession session,
+ boolean isMultiParty) {
+
+ if (mListener != null) {
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+ }
+ }
+
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession session,
+ ImsSuppServiceNotification suppServiceInfo ) {
+ if (mListener != null) {
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+ }
+ }
+
+ }
+
+ /**
+ * Provides a string representation of the {@link ImsCallSession}. Primarily intended for
+ * use in log statements.
+ *
+ * @return String representation of session.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ImsCallSession objId:");
+ sb.append(System.identityHashCode(this));
+ sb.append(" state:");
+ sb.append(State.toString(getState()));
+ sb.append(" callId:");
+ sb.append(getCallId());
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
new file mode 100644
index 0000000..432dc39
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 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.ims.internal;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+public abstract class ImsVideoCallProvider {
+ private static final int MSG_SET_CALLBACK = 1;
+ private static final int MSG_SET_CAMERA = 2;
+ private static final int MSG_SET_PREVIEW_SURFACE = 3;
+ private static final int MSG_SET_DISPLAY_SURFACE = 4;
+ private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+ private static final int MSG_SET_ZOOM = 6;
+ private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+ private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+ private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+ private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+ private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+ private final ImsVideoCallProviderBinder mBinder;
+
+ private IImsVideoCallCallback mCallback;
+
+ /**
+ * Default handler used to consolidate binder method calls onto a single thread.
+ */
+ private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_CALLBACK:
+ mCallback = (IImsVideoCallCallback) msg.obj;
+ break;
+ case MSG_SET_CAMERA:
+ {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ onSetCamera((String) args.arg1);
+ onSetCamera((String) args.arg1, args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_PREVIEW_SURFACE:
+ onSetPreviewSurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DISPLAY_SURFACE:
+ onSetDisplaySurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DEVICE_ORIENTATION:
+ onSetDeviceOrientation(msg.arg1);
+ break;
+ case MSG_SET_ZOOM:
+ onSetZoom((Float) msg.obj);
+ break;
+ case MSG_SEND_SESSION_MODIFY_REQUEST: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ VideoProfile fromProfile = (VideoProfile) args.arg1;
+ VideoProfile toProfile = (VideoProfile) args.arg2;
+
+ onSendSessionModifyRequest(fromProfile, toProfile);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SEND_SESSION_MODIFY_RESPONSE:
+ onSendSessionModifyResponse((VideoProfile) msg.obj);
+ break;
+ case MSG_REQUEST_CAMERA_CAPABILITIES:
+ onRequestCameraCapabilities();
+ break;
+ case MSG_REQUEST_CALL_DATA_USAGE:
+ onRequestCallDataUsage();
+ break;
+ case MSG_SET_PAUSE_IMAGE:
+ onSetPauseImage((Uri) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /**
+ * IImsVideoCallProvider stub implementation.
+ */
+ private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+ public void setCallback(IImsVideoCallCallback callback) {
+ mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+ }
+
+ public void setCamera(String cameraId, int uid) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = cameraId;
+ args.argi1 = uid;
+ mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+ }
+
+ public void setPreviewSurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDisplaySurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDeviceOrientation(int rotation) {
+ mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+ }
+
+ public void setZoom(float value) {
+ mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+ }
+
+ public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = fromProfile;
+ args.arg2 = toProfile;
+ mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+ }
+
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ mProviderHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+ }
+
+ public void requestCameraCapabilities() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+ }
+
+ public void requestCallDataUsage() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+ }
+
+ public void setPauseImage(Uri uri) {
+ mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+ }
+ }
+
+ public ImsVideoCallProvider() {
+ mBinder = new ImsVideoCallProviderBinder();
+ }
+
+ /**
+ * Returns binder object which can be used across IPC methods.
+ */
+ public final IImsVideoCallProvider getInterface() {
+ return mBinder;
+ }
+
+ /** @see Connection.VideoProvider#onSetCamera */
+ public abstract void onSetCamera(String cameraId);
+
+ /**
+ * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+ * the IMS service uses when opening the camera. This ensures camera permissions are verified
+ * by the camera service.
+ *
+ * @param cameraId The id of the camera to be opened.
+ * @param uid The uid of the caller, used when opening the camera for permission verification.
+ * @see Connection.VideoProvider#onSetCamera
+ */
+ public void onSetCamera(String cameraId, int uid) {
+ }
+
+ /** @see Connection.VideoProvider#onSetPreviewSurface */
+ public abstract void onSetPreviewSurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDisplaySurface */
+ public abstract void onSetDisplaySurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDeviceOrientation */
+ public abstract void onSetDeviceOrientation(int rotation);
+
+ /** @see Connection.VideoProvider#onSetZoom */
+ public abstract void onSetZoom(float value);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+ public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+ VideoProfile toProfile);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+ public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+ /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+ public abstract void onRequestCameraCapabilities();
+
+ /** @see Connection.VideoProvider#onRequestCallDataUsage */
+ public abstract void onRequestCallDataUsage();
+
+ /** @see Connection.VideoProvider#onSetPauseImage */
+ public abstract void onSetPauseImage(Uri uri);
+
+ /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+ public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyRequest(VideoProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+ public void receiveSessionModifyResponse(
+ int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#handleCallSessionEvent */
+ public void handleCallSessionEvent(int event) {
+ if (mCallback != null) {
+ try {
+ mCallback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changePeerDimensions */
+ public void changePeerDimensions(int width, int height) {
+ if (mCallback != null) {
+ try {
+ mCallback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCallDataUsage */
+ public void changeCallDataUsage(long dataUsage) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCameraCapabilities */
+ public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCameraCapabilities(CameraCapabilities);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeVideoQuality */
+ public void changeVideoQuality(int videoQuality) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeVideoQuality(videoQuality);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index d40835b..dc8dfba 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -264,15 +264,6 @@
= "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
/**
- * Activity Action: Start this activity to invoke the carrier setup app.
- * The carrier app must be signed using a certificate that matches the UICC access rules.
- *
- * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP
- * permission.</p>
- */
- public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP";
-
- /**
* <p>Broadcast Action: Indicates that the action is forbidden by network.
* <p class="note">
* This is for the OEM applications to understand about possible provisioning issues.
diff --git a/test-runner/src/android/test/suitebuilder/TestMethod.java b/test-runner/src/android/test/suitebuilder/TestMethod.java
index 08568d5..ae1db5e 100644
--- a/test-runner/src/android/test/suitebuilder/TestMethod.java
+++ b/test-runner/src/android/test/suitebuilder/TestMethod.java
@@ -26,7 +26,11 @@
/**
* Represents a test to be run. Can be constructed without instantiating the TestCase or even
* loading the class.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+@Deprecated
public class TestMethod {
private final String enclosingClassname;
diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
index 8c89489..3b920cf 100644
--- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
@@ -38,7 +38,11 @@
/**
* Build suites based on a combination of included packages, excluded packages,
* and predicates that must be satisfied.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+@Deprecated
public class TestSuiteBuilder {
private Context context;
@@ -223,7 +227,11 @@
/**
* A special {@link junit.framework.TestCase} used to indicate a failure during the build()
* step.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+ @Deprecated
public static class FailedToCreateTests extends TestCase {
private final Exception exception;
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 0fd8b12..2c6c8d7 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -87,6 +87,7 @@
android:text="@string/capture"
android:id="@+id/caputre_check_box"
android:layout_gravity="center_horizontal"
+ android:onClick="onCaptureAudioCheckboxClicked"
android:padding="20dp" />
<Button
@@ -94,6 +95,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/play_capture"
+ android:onClick="onPlayCapturedAudioButtonClicked"
android:padding="20dp"
android:enabled="false" />
</LinearLayout>
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 56aad23..9b1a9f2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -297,6 +297,7 @@
Notification n = new Notification.Builder(NotificationTestList.this)
.setSmallIcon(R.drawable.icon2)
.setContentTitle("Low priority")
+ .setTimeout(60000)
.setLights(0xff0000ff, 1, 0)
.setPriority(Notification.PRIORITY_LOW)
.build();
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
index e201012..c5965e8 100644
--- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
+++ b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
@@ -17,101 +17,59 @@
package com.android.server.connectivity;
import android.net.ConnectivityMetricsEvent;
-import android.net.ConnectivityMetricsLogger;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.function.Consumer;
+
abstract public class MetricsTestUtil {
private MetricsTestUtil() {
}
- static ConnectivityMetricsEvent ipEv(Parcelable p) {
- return ev(ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY, p);
+ static ConnectivityMetricsEvent ev(Parcelable p) {
+ return new ConnectivityMetricsEvent(1L, 0, 0, p);
}
- static ConnectivityMetricsEvent telephonyEv() {
- return ev(ConnectivityMetricsLogger.COMPONENT_TAG_TELEPHONY, new Bundle());
- }
-
- static ConnectivityMetricsEvent ev(int tag, Parcelable p) {
- return new ConnectivityMetricsEvent(1L, tag, 0, p);
- }
-
- // Utiliy interface for describing the content of a Parcel. This relies on
- // the implementation defails of Parcelable and on the fact that the fully
- // qualified Parcelable class names are written as string in the Parcels.
- interface ParcelField {
- void write(Parcel p);
- }
-
- static ConnectivityMetricsEvent describeIpEvent(ParcelField... fs) {
+ static ConnectivityMetricsEvent describeIpEvent(Consumer<Parcel>... fs) {
Parcel p = Parcel.obtain();
- for (ParcelField f : fs) {
- f.write(p);
+ for (Consumer<Parcel> f : fs) {
+ f.accept(p);
}
p.setDataPosition(0);
- return ipEv(p.readParcelable(ClassLoader.getSystemClassLoader()));
+ return ev(p.readParcelable(ClassLoader.getSystemClassLoader()));
}
- static ParcelField aType(Class<?> c) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeString(c.getName());
- }
- };
+ static Consumer<Parcel> aType(Class<?> c) {
+ return aString(c.getName());
}
- static ParcelField aBool(boolean b) {
+ static Consumer<Parcel> aBool(boolean b) {
return aByte((byte) (b ? 1 : 0));
}
- static ParcelField aByte(byte b) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeByte(b);
- }
- };
+ static Consumer<Parcel> aByte(byte b) {
+ return (p) -> p.writeByte(b);
}
- static ParcelField anInt(int i) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeInt(i);
- }
- };
+ static Consumer<Parcel> anInt(int i) {
+ return (p) -> p.writeInt(i);
}
- static ParcelField aLong(long l) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeLong(l);
- }
- };
+ static Consumer<Parcel> aLong(long l) {
+ return (p) -> p.writeLong(l);
}
- static ParcelField aString(String s) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeString(s);
- }
- };
+ static Consumer<Parcel> aString(String s) {
+ return (p) -> p.writeString(s);
}
- static ParcelField aByteArray(byte... ary) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeByteArray(ary);
- }
- };
+ static Consumer<Parcel> aByteArray(byte... ary) {
+ return (p) -> p.writeByteArray(ary);
}
- static ParcelField anIntArray(int... ary) {
- return new ParcelField() {
- public void write(Parcel p) {
- p.writeIntArray(ary);
- }
- };
+ static Consumer<Parcel> anIntArray(int... ary) {
+ return (p) -> p.writeIntArray(ary);
}
static byte b(int i) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 3ed56df..c72efb0 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -32,6 +32,8 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
@@ -42,6 +44,10 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -49,6 +55,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -63,6 +70,7 @@
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
+ private TestStateMachine mSM;
private TestConnectivityManager mCM;
private UpstreamNetworkMonitor mUNM;
@@ -72,7 +80,15 @@
reset(mCS);
mCM = spy(new TestConnectivityManager(mContext, mCS));
- mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+ mSM = new TestStateMachine();
+ mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+ }
+
+ @After public void tearDown() throws Exception {
+ if (mSM != null) {
+ mSM.quit();
+ mSM = null;
+ }
}
@Test
@@ -139,15 +155,17 @@
mUNM.start();
verify(mCM, Mockito.times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class));
- verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(any(NetworkCallback.class));
+ any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
+ any(NetworkCallback.class), any(Handler.class));
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.updateMobileRequiresDun(true);
mUNM.registerMobileNetworkRequest();
verify(mCM, Mockito.times(1)).requestNetwork(
- any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt());
+ any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
+ any(Handler.class));
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
@@ -224,6 +242,7 @@
}
public static class TestConnectivityManager extends ConnectivityManager {
+ public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
public Set<NetworkCallback> trackingDefault = new HashSet<>();
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
@@ -234,7 +253,8 @@
}
boolean hasNoCallbacks() {
- return trackingDefault.isEmpty() &&
+ return allCallbacks.isEmpty() &&
+ trackingDefault.isEmpty() &&
listening.isEmpty() &&
requested.isEmpty() &&
legacyTypeMap.isEmpty();
@@ -262,14 +282,23 @@
}
@Override
- public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+ public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(requested.containsKey(cb));
requested.put(cb, req);
}
@Override
+ public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb,
- int timeoutMs, int legacyType) {
+ int timeoutMs, int legacyType, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(requested.containsKey(cb));
requested.put(cb, req);
assertFalse(legacyTypeMap.containsKey(cb));
@@ -279,18 +308,32 @@
}
@Override
- public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+ public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(listening.containsKey(cb));
listening.put(cb, req);
}
@Override
- public void registerDefaultNetworkCallback(NetworkCallback cb) {
+ public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
+ public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(trackingDefault.contains(cb));
trackingDefault.add(cb);
}
@Override
+ public void registerDefaultNetworkCallback(NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
public void unregisterNetworkCallback(NetworkCallback cb) {
if (trackingDefault.contains(cb)) {
trackingDefault.remove(cb);
@@ -302,10 +345,35 @@
} else {
fail("Unexpected callback removed");
}
+ allCallbacks.remove(cb);
+ assertFalse(allCallbacks.containsKey(cb));
assertFalse(trackingDefault.contains(cb));
assertFalse(listening.containsKey(cb));
assertFalse(requested.containsKey(cb));
}
}
+
+ public static class TestStateMachine extends StateMachine {
+ public final ArrayList<Message> messages = new ArrayList<>();
+ private final State mLoggingState = new LoggingState();
+
+ class LoggingState extends State {
+ @Override public void enter() { messages.clear(); }
+
+ @Override public void exit() { messages.clear(); }
+
+ @Override public boolean processMessage(Message msg) {
+ messages.add(msg);
+ return true;
+ }
+ }
+
+ public TestStateMachine() {
+ super("UpstreamNetworkMonitor.TestStateMachine");
+ addState(mLoggingState);
+ setInitialState(mLoggingState);
+ super.start();
+ }
+ }
}
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cffe8d5..493f238 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -118,6 +118,9 @@
bool is_valid() const;
};
+constexpr const uint8_t kAppPackageId = 0x7fu;
+constexpr const uint8_t kFrameworkPackageId = 0x01u;
+
/**
* A binary identifier representing a resource. Internally it
* is a 32bit integer split as follows:
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 2d8e5a21..ca6738b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -533,6 +533,7 @@
case android::Res_value::TYPE_REFERENCE:
case android::Res_value::TYPE_ATTRIBUTE:
case android::Res_value::TYPE_DYNAMIC_REFERENCE:
+ case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
return android::ResTable_map::TYPE_REFERENCE;
case android::Res_value::TYPE_STRING:
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index f75ed7a..2868b2a 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -88,10 +88,24 @@
}
bool Reference::Flatten(android::Res_value* out_value) const {
- out_value->dataType = (reference_type == Reference::Type::kResource)
- ? android::Res_value::TYPE_REFERENCE
- : android::Res_value::TYPE_ATTRIBUTE;
- out_value->data = util::HostToDevice32(id ? id.value().id : 0);
+ const ResourceId resid = id.value_or_default(ResourceId(0));
+ const bool dynamic =
+ (resid.package_id() != kFrameworkPackageId && resid.package_id() != kAppPackageId);
+
+ if (reference_type == Reference::Type::kResource) {
+ if (dynamic) {
+ out_value->dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE;
+ } else {
+ out_value->dataType = android::Res_value::TYPE_REFERENCE;
+ }
+ } else {
+ if (dynamic) {
+ out_value->dataType = android::Res_value::TYPE_DYNAMIC_ATTRIBUTE;
+ } else {
+ out_value->dataType = android::Res_value::TYPE_ATTRIBUTE;
+ }
+ }
+ out_value->data = util::HostToDevice32(resid.id);
return true;
}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 9c0f13c..68bdb95 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -533,9 +533,14 @@
std::unique_ptr<MethodDefinition> rewrite_method;
// Generate an onResourcesLoaded() callback if requested.
- if (options_.generate_rewrite_callback) {
+ if (options_.rewrite_callback_options) {
rewrite_method =
util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
+ for (const std::string& package_to_callback :
+ options_.rewrite_callback_options.value().packages_to_callback) {
+ rewrite_method->AppendStatement(
+ StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
+ }
}
for (const auto& package : table_->packages) {
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 178f558..4510430 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -33,13 +33,19 @@
class ClassDefinition;
class MethodDefinition;
+// Options for generating onResourcesLoaded callback in R.java.
+struct OnResourcesLoadedCallbackOptions {
+ // Other R classes to delegate the same callback to (with the same package ID).
+ std::vector<std::string> packages_to_callback;
+};
+
struct JavaClassGeneratorOptions {
// Specifies whether to use the 'final' modifier on resource entries. Default is true.
bool use_final = true;
- // Whether to generate code to rewrite the package ID of resources.
- // Implies use_final == true. Default is false.
- bool generate_rewrite_callback = false;
+ // If set, generates code to rewrite the package ID of resources.
+ // Implies use_final == true. Default is unset.
+ Maybe<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
enum class SymbolTypes {
kAll,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index bcb2d4f..271279f 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -381,7 +381,9 @@
JavaClassGeneratorOptions options;
options.use_final = false;
- options.generate_rewrite_callback = true;
+ options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
+ {"com.foo", "com.boo"},
+ };
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
@@ -389,7 +391,9 @@
std::string actual = out.str();
- EXPECT_NE(std::string::npos, actual.find("onResourcesLoaded"));
+ EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
+ EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
+ EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
}
} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 4905216..dd8e14b 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1097,18 +1097,15 @@
return false;
}
- std::unique_ptr<ResourceTable> table =
- LoadTablePbFromCollection(collection.get());
+ std::unique_ptr<ResourceTable> table = LoadTablePbFromCollection(collection.get());
if (!table) {
- context_->GetDiagnostics()->Error(DiagMessage(input)
- << "invalid static library");
+ context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library");
return false;
}
ResourceTablePackage* pkg = table->FindPackageById(0x7f);
if (!pkg) {
- context_->GetDiagnostics()->Error(DiagMessage(input)
- << "static library has no package");
+ context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
return false;
}
@@ -1125,11 +1122,9 @@
pkg->name = "";
if (override) {
- result = table_merger_->MergeOverlay(Source(input), table.get(),
- collection.get());
+ result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
} else {
- result =
- table_merger_->Merge(Source(input), table.get(), collection.get());
+ result = table_merger_->Merge(Source(input), table.get(), collection.get());
}
} else {
@@ -1782,17 +1777,21 @@
}
if (options_.generate_java_class_path) {
- JavaClassGeneratorOptions options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- options.javadoc_annotations = options_.javadoc_annotations;
+ // The set of packages whose R class to call in the main classes
+ // onResourcesLoaded callback.
+ std::vector<std::string> packages_to_callback;
+
+ JavaClassGeneratorOptions template_options;
+ template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ template_options.javadoc_annotations = options_.javadoc_annotations;
if (options_.package_type == PackageType::kStaticLib || options_.generate_non_final_ids) {
- options.use_final = false;
+ template_options.use_final = false;
}
if (options_.package_type == PackageType::kSharedLib) {
- options.use_final = false;
- options.generate_rewrite_callback = true;
+ template_options.use_final = false;
+ template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
}
const StringPiece actual_package = context_->GetCompilationPackage();
@@ -1802,36 +1801,51 @@
output_package = options_.custom_java_package.value();
}
+ // Generate the private symbols if required.
if (options_.private_symbols) {
+ packages_to_callback.push_back(options_.private_symbols.value());
+
// If we defined a private symbols package, we only emit Public symbols
// to the original package, and private and public symbols to the
// private package.
-
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- if (!WriteJavaFile(&final_table_, context_->GetCompilationPackage(),
- output_package, options)) {
- return 1;
- }
-
+ JavaClassGeneratorOptions options = template_options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- output_package = options_.private_symbols.value();
- }
-
- if (!WriteJavaFile(&final_table_, actual_package, output_package,
- options)) {
- return 1;
- }
-
- for (const std::string& extra_package : options_.extra_java_packages) {
- if (!WriteJavaFile(&final_table_, actual_package, extra_package,
+ if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
options)) {
return 1;
}
}
+
+ // Generate all the symbols for all extra packages.
+ for (const std::string& extra_package : options_.extra_java_packages) {
+ packages_to_callback.push_back(extra_package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
+ return 1;
+ }
+ }
+
+ // Generate the main public R class.
+ JavaClassGeneratorOptions options = template_options;
+
+ // Only generate public symbols if we have a private package.
+ if (options_.private_symbols) {
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ }
+
+ if (options.rewrite_callback_options) {
+ options.rewrite_callback_options.value().packages_to_callback =
+ std::move(packages_to_callback);
+ }
+
+ if (!WriteJavaFile(&final_table_, actual_package, output_package, options)) {
+ return 1;
+ }
}
- if (!WriteProguardFile(options_.generate_proguard_rules_path,
- proguard_keep_set)) {
+ if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
return 1;
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d5c0dc4..313fe45 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -79,6 +79,17 @@
return false;
}
+static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std::string& attr) {
+ return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool {
+ if (el->FindAttribute(xml::kSchemaAndroid, attr) == nullptr) {
+ diag->Error(DiagMessage(el->line_number)
+ << "<" << el->name << "> is missing required attribute 'android:" << attr << "'");
+ return false;
+ }
+ return true;
+ };
+}
+
static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
xml::Attribute* attr = el->FindAttribute({}, "package");
if (!attr) {
@@ -272,6 +283,16 @@
application_action["uses-library"].Action(RequiredNameIsJavaPackage);
application_action["library"].Action(RequiredNameIsJavaPackage);
+
+ xml::XmlNodeAction& static_library_action = application_action["static-library"];
+ static_library_action.Action(RequiredNameIsJavaPackage);
+ static_library_action.Action(RequiredAndroidAttribute("version"));
+
+ xml::XmlNodeAction& uses_static_library_action = application_action["uses-static-library"];
+ uses_static_library_action.Action(RequiredNameIsJavaPackage);
+ uses_static_library_action.Action(RequiredAndroidAttribute("version"));
+ uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
+
application_action["meta-data"] = meta_data_action;
application_action["activity"] = component_action;
application_action["activity-alias"] = component_action;
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 4a42826..0331313 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -122,8 +122,7 @@
DiagMessage msg(entry.key.GetSource());
// Call the matches method again, this time with a DiagMessage so we
- // fill
- // in the actual error message.
+ // fill in the actual error message.
symbol->attribute->Matches(entry.value.get(), &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 7e7b9fb..9311091 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -39,14 +39,12 @@
bool TableMerger::Merge(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, false /* overlay */,
- true /* allow new */);
+ return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
}
bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return MergeImpl(src, table, collection, true /* overlay */,
- options_.auto_add_overlay);
+ return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
}
/**
@@ -55,25 +53,13 @@
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
io::IFileCollection* collection, bool overlay,
bool allow_new) {
- const uint8_t desired_package_id = context_->GetPackageId();
-
bool error = false;
for (auto& package : table->packages) {
- // Warn of packages with an unrelated ID.
- const Maybe<ResourceId>& id = package->id;
- if (id && id.value() != 0x0 && id.value() != desired_package_id) {
- context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
- << package->name);
- continue;
- }
-
// Only merge an empty package or the package we're building.
// Other packages may exist, which likely contain attribute definitions.
// This is because at compile time it is unknown if the attributes are
- // simply
- // uses of the attribute or definitions.
- if (package->name.empty() ||
- context_->GetCompilationPackage() == package->name) {
+ // simply uses of the attribute or definitions.
+ if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
FileMergeCallback callback;
if (collection) {
callback = [&](const ResourceNameRef& name,
@@ -83,8 +69,7 @@
io::IFile* f = collection->FindFile(*old_file->path);
if (!f) {
context_->GetDiagnostics()->Error(DiagMessage(src)
- << "file '" << *old_file->path
- << "' not found");
+ << "file '" << *old_file->path << "' not found");
return false;
}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 5d75e76..bcafbca 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -293,6 +293,11 @@
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
ResourceId id) {
+ if (!id.is_valid()) {
+ // Exit early and avoid the error logs from AssetManager.
+ return {};
+ }
+
const android::ResTable& table = assets_.getResources(false);
Maybe<ResourceName> maybe_name = GetResourceName(table, id);
if (!maybe_name) {
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 29a921c..9158bdd 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -458,10 +458,14 @@
}
if (value->dataType == Res_value::TYPE_REFERENCE ||
- value->dataType == Res_value::TYPE_ATTRIBUTE) {
- const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE)
- ? Reference::Type::kResource
- : Reference::Type::kAttribute;
+ value->dataType == Res_value::TYPE_ATTRIBUTE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+ Reference::Type type = Reference::Type::kResource;
+ if (value->dataType == Res_value::TYPE_ATTRIBUTE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+ type = Reference::Type::kAttribute;
+ }
if (data == 0) {
// A reference of 0, must be the magic @null reference.
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index f193ea4..eb8a1cc 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -14,7 +14,9 @@
LANG_TO_SCRIPT = {
'as': 'Beng',
+ 'bg': 'Cyrl',
'bn': 'Beng',
+ 'cu': 'Cyrl',
'cy': 'Latn',
'da': 'Latn',
'de': 'Latn',
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3d5d5c6..df3ce19 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -640,6 +640,10 @@
return AccessibilityManager.getInstance(this);
}
+ if (AUTO_FILL_MANAGER_SERVICE.equals(service)) {
+ return null;
+ }
+
throw new UnsupportedOperationException("Unsupported Service: " + service);
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3eb9934..a4ab64f 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1305,6 +1305,7 @@
setConnectChoice(source.getConnectChoice());
setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
setHasEverConnected(source.getHasEverConnected());
+ setNotRecommended(source.isNotRecommended());
}
public void writeToParcel(Parcel dest) {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5f949747..632cfaf 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import android.os.Parcel;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import org.junit.Before;
import org.junit.Test;
@@ -66,4 +67,35 @@
assertArrayEquals(bytes, rebytes);
}
+
+ @Test
+ public void testNetworkSelectionStatusCopy() {
+ NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+ networkSelectionStatus.setNotRecommended(true);
+
+ NetworkSelectionStatus copy = new NetworkSelectionStatus();
+ copy.copy(networkSelectionStatus);
+
+ assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+ }
+
+ @Test
+ public void testNetworkSelectionStatusParcel() {
+ NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+ networkSelectionStatus.setNotRecommended(true);
+
+ Parcel parcelW = Parcel.obtain();
+ networkSelectionStatus.writeToParcel(parcelW);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+
+ NetworkSelectionStatus copy = new NetworkSelectionStatus();
+ copy.readFromParcel(parcelR);
+
+ assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+ }
}