diff options
222 files changed, 6970 insertions, 2502 deletions
diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_10.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_10.xml index a8d3de0b9a97..4a4b3b42522a 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_10.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_10.xml @@ -35,7 +35,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="20px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_100.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_100.xml index d991862f7a09..0b326e8cf233 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_100.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_100.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="110px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_1000.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_1000.xml index 3b55b6c8b56d..2f4239d81671 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_1000.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_1000.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="1010px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_10000.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_10000.xml index 64a25c27f4a5..b6be10a1a42c 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_10000.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_10000.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="10010px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_100000.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_100000.xml index 1f6078396906..aed339cc571c 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_100000.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_100000.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="100010px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_300.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_300.xml index 54c7e71c9a9a..62522bae6f39 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_300.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_300.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="310px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_5.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_5.xml index 525b2c854c7a..f383fa4a9422 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_5.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_5.xml @@ -35,7 +35,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="15px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_50.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_50.xml index 470c4da1376e..04dd6c2115c9 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_50.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_50.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="60px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/res/layout/test_autosize_textview_500.xml b/apct-tests/perftests/core/res/layout/test_autosize_textview_500.xml index b8a6e0af49f0..a8e2b385aac4 100644 --- a/apct-tests/perftests/core/res/layout/test_autosize_textview_500.xml +++ b/apct-tests/perftests/core/res/layout/test_autosize_textview_500.xml @@ -19,7 +19,7 @@ android:layout_width="400dp" android:layout_height="600dp" android:text="@string/long_text" - android:autoSizeText="uniform" + android:autoSizeTextType="uniform" android:autoSizeMinTextSize="10px" android:textSize="510px" android:autoSizeStepGranularity="1px"/> diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java index 6ee6f70db3ca..c3101664d1ff 100644 --- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java @@ -89,7 +89,7 @@ public class TextViewAutoSizeLayoutPerfTest { while (state.keepRunning()) { TextView textView = new TextView(activity); - // TextView#onMeasure() gets called, which triggers TextView#autoSizeText() + // TextView#onLayout() gets called, which triggers TextView#autoSizeText() // which is the method we want to benchmark. textView.requestLayout(); } diff --git a/api/current.txt b/api/current.txt index b967febef784..22dbe7b51462 100644 --- a/api/current.txt +++ b/api/current.txt @@ -305,7 +305,7 @@ package android { field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 field public static final int autoSizeStepGranularity = 16844086; // 0x1010536 - field public static final int autoSizeText = 16844085; // 0x1010535 + field public static final int autoSizeTextType = 16844085; // 0x1010535 field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -410,6 +410,7 @@ package android { 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 @@ package android { 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 @@ -6718,6 +6718,7 @@ package android.app.job { method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris(); method public boolean isPeriodic(); method public boolean isPersisted(); + method public boolean isRequireBatteryNotLow(); method public boolean isRequireCharging(); method public boolean isRequireDeviceIdle(); method public void writeToParcel(android.os.Parcel, int); @@ -6744,6 +6745,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); + method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean); method public android.app.job.JobInfo.Builder setTransientExtras(android.os.Bundle); @@ -10103,10 +10105,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -10645,6 +10649,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); + method public android.content.ComponentName[] getChooserComponentNames(); + method public android.os.PersistableBundle getChooserExtras(); + method public android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -10657,6 +10664,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); + method public boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -10669,9 +10677,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); + method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -12576,6 +12586,7 @@ package android.graphics { 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; } @@ -14012,6 +14023,8 @@ package android.graphics.drawable.shapes { public class ArcShape extends android.graphics.drawable.shapes.RectShape { ctor public ArcShape(float, float); + method public final float getStartAngle(); + method public final float getSweepAngle(); } public class OvalShape extends android.graphics.drawable.shapes.RectShape { @@ -20998,13 +21011,14 @@ package android.media { 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(); @@ -22162,6 +22176,7 @@ package android.media { field public static final java.lang.String KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval"; field public static final java.lang.String KEY_LANGUAGE = "language"; + field public static final java.lang.String KEY_LATENCY = "latency"; field public static final java.lang.String KEY_LEVEL = "level"; field public static final java.lang.String KEY_MAX_HEIGHT = "max-height"; field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size"; @@ -22345,7 +22360,7 @@ package android.media { 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; @@ -22357,6 +22372,7 @@ package android.media { 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(); @@ -23184,6 +23200,10 @@ package android.media { 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(); @@ -23197,6 +23217,53 @@ package android.media { 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 { @@ -26076,8 +26143,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -36322,10 +36387,10 @@ package android.service.autofill { 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 { @@ -36333,6 +36398,23 @@ package android.service.autofill { 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_DATA_TYPE_ADDRESS = 2; // 0x2 + field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 + field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + } + + 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(); + method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + } + } package android.service.carrier { @@ -42448,30 +42530,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf @@ -44575,8 +44678,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + method public final <T extends android.view.View> T findViewById(int); + method public final <T extends android.view.View> T findViewWithTag(java.lang.Object); method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int); method protected deprecated boolean fitSystemWindows(android.graphics.Rect); method public android.view.View focusSearch(int); diff --git a/api/removed.txt b/api/removed.txt index e4678113d08f..c5dbf8daf0a4 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -20,25 +20,14 @@ package android.app.admin { package android.content { public abstract class Context { - method public deprecated android.content.Context createCredentialEncryptedStorageContext(); - method public deprecated android.content.Context createDeviceEncryptedStorageContext(); method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(java.lang.String); - method public deprecated boolean isCredentialEncryptedStorage(); - method public deprecated boolean isDeviceEncryptedStorage(); - method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); - method public deprecated boolean migrateSharedPreferencesFrom(android.content.Context, java.lang.String); } } package android.content.pm { - public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { - field public deprecated java.lang.String credentialEncryptedDataDir; - field public deprecated java.lang.String deviceEncryptedDataDir; - } - public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -47,12 +36,6 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } - public abstract class PackageManager { - field public static final deprecated int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 - field public static final deprecated int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final deprecated int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 - } - } package android.database { @@ -169,10 +152,6 @@ package android.os { 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); } } @@ -190,15 +169,6 @@ package android.os.storage { } -package android.preference { - - public class PreferenceManager { - method public deprecated void setStorageCredentialEncrypted(); - method public deprecated void setStorageDeviceEncrypted(); - } - -} - package android.provider { public class Browser { diff --git a/api/system-current.txt b/api/system-current.txt index d0404d9928fa..daf8cefeb443 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -418,7 +418,7 @@ package android { field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 field public static final int autoSizeStepGranularity = 16844086; // 0x1010536 - field public static final int autoSizeText = 16844085; // 0x1010535 + field public static final int autoSizeTextType = 16844085; // 0x1010535 field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -523,6 +523,7 @@ package android { 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 @@ package android { 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 @@ -7110,6 +7110,7 @@ package android.app.job { method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris(); method public boolean isPeriodic(); method public boolean isPersisted(); + method public boolean isRequireBatteryNotLow(); method public boolean isRequireCharging(); method public boolean isRequireDeviceIdle(); method public void writeToParcel(android.os.Parcel, int); @@ -7136,6 +7137,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); + method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean); method public android.app.job.JobInfo.Builder setTransientExtras(android.os.Bundle); @@ -10640,10 +10642,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -11267,6 +11271,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); + method public android.content.ComponentName[] getChooserComponentNames(); + method public android.os.PersistableBundle getChooserExtras(); + method public android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -11279,6 +11286,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); + method public boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -11291,9 +11299,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); + method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -13212,6 +13222,7 @@ package android.graphics { 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; } @@ -14648,6 +14659,8 @@ package android.graphics.drawable.shapes { public class ArcShape extends android.graphics.drawable.shapes.RectShape { ctor public ArcShape(float, float); + method public final float getStartAngle(); + method public final float getSweepAngle(); } public class OvalShape extends android.graphics.drawable.shapes.RectShape { @@ -22686,13 +22699,14 @@ package android.media { 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(); @@ -23850,6 +23864,7 @@ package android.media { field public static final java.lang.String KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval"; field public static final java.lang.String KEY_LANGUAGE = "language"; + field public static final java.lang.String KEY_LATENCY = "latency"; field public static final java.lang.String KEY_LEVEL = "level"; field public static final java.lang.String KEY_MAX_HEIGHT = "max-height"; field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size"; @@ -24033,7 +24048,7 @@ package android.media { 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; @@ -24045,6 +24060,7 @@ package android.media { 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(); @@ -24883,6 +24899,10 @@ package android.media { 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(); @@ -24896,6 +24916,53 @@ package android.media { 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 { @@ -25877,6 +25944,12 @@ package android.media.tv { 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_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 EXTRA_EXISTING_COLUMN_NAMES = "android.media.tv.extra.EXISTING_COLUMN_NAMES"; + 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 { @@ -28655,8 +28728,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -39236,10 +39307,10 @@ package android.service.autofill { 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 { @@ -39247,6 +39318,23 @@ package android.service.autofill { 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_DATA_TYPE_ADDRESS = 2; // 0x2 + field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 + field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + } + + 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(); + method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + } + } package android.service.carrier { @@ -45787,30 +45875,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf @@ -47914,8 +48023,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + method public final <T extends android.view.View> T findViewById(int); + method public final <T extends android.view.View> T findViewWithTag(java.lang.Object); method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int); method protected deprecated boolean fitSystemWindows(android.graphics.Rect); method public android.view.View focusSearch(int); diff --git a/api/system-removed.txt b/api/system-removed.txt index 677311288a5c..a3093e294d15 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -18,25 +18,14 @@ package android.app.admin { package android.content { public abstract class Context { - method public deprecated android.content.Context createCredentialEncryptedStorageContext(); - method public deprecated android.content.Context createDeviceEncryptedStorageContext(); method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(java.lang.String); - method public deprecated boolean isCredentialEncryptedStorage(); - method public deprecated boolean isDeviceEncryptedStorage(); - method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); - method public deprecated boolean migrateSharedPreferencesFrom(android.content.Context, java.lang.String); } } package android.content.pm { - public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { - field public deprecated java.lang.String credentialEncryptedDataDir; - field public deprecated java.lang.String deviceEncryptedDataDir; - } - public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -45,12 +34,6 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } - public abstract class PackageManager { - field public static final deprecated int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 - field public static final deprecated int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final deprecated int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 - } - } package android.database { @@ -163,10 +146,6 @@ package android.os { 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); } } @@ -184,15 +163,6 @@ package android.os.storage { } -package android.preference { - - public class PreferenceManager { - method public deprecated void setStorageCredentialEncrypted(); - method public deprecated void setStorageDeviceEncrypted(); - } - -} - package android.provider { public class Browser { diff --git a/api/test-current.txt b/api/test-current.txt index e6b8010afe62..e9dddc313fbe 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -305,7 +305,7 @@ package android { field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 field public static final int autoSizeStepGranularity = 16844086; // 0x1010536 - field public static final int autoSizeText = 16844085; // 0x1010535 + field public static final int autoSizeTextType = 16844085; // 0x1010535 field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -410,6 +410,7 @@ package android { 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 @@ package android { 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 @@ -6744,6 +6744,7 @@ package android.app.job { method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris(); method public boolean isPeriodic(); method public boolean isPersisted(); + method public boolean isRequireBatteryNotLow(); method public boolean isRequireCharging(); method public boolean isRequireDeviceIdle(); method public void writeToParcel(android.os.Parcel, int); @@ -6770,6 +6771,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); + method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean); method public android.app.job.JobInfo.Builder setTransientExtras(android.os.Bundle); @@ -10135,10 +10137,12 @@ package android.content.pm { ctor public LauncherApps.ShortcutQuery(); method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName); method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long); + method public android.content.pm.LauncherApps.ShortcutQuery setIntent(android.content.Intent); method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String); method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int); method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>); field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4 + field public static final int FLAG_MATCH_CHOOSER = 16; // 0x10 field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1 field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8 field public static final int FLAG_MATCH_PINNED = 2; // 0x2 @@ -10681,6 +10685,9 @@ package android.content.pm { method public int describeContents(); method public android.content.ComponentName getActivity(); method public java.util.Set<java.lang.String> getCategories(); + method public android.content.ComponentName[] getChooserComponentNames(); + method public android.os.PersistableBundle getChooserExtras(); + method public android.content.IntentFilter[] getChooserIntentFilters(); method public java.lang.CharSequence getDisabledMessage(); method public android.os.PersistableBundle getExtras(); method public java.lang.String getId(); @@ -10693,6 +10700,7 @@ package android.content.pm { method public java.lang.CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); method public boolean hasKeyFieldsOnly(); + method public boolean isChooser(); method public boolean isDeclaredInManifest(); method public boolean isDynamic(); method public boolean isEnabled(); @@ -10705,9 +10713,11 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String); + method public android.content.pm.ShortcutInfo.Builder addChooserIntentFilter(android.content.IntentFilter, android.content.ComponentName); method public android.content.pm.ShortcutInfo build(); method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName); method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); + method public android.content.pm.ShortcutInfo.Builder setChooserExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence); method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle); method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); @@ -12613,6 +12623,7 @@ package android.graphics { 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; } @@ -14050,6 +14061,8 @@ package android.graphics.drawable.shapes { public class ArcShape extends android.graphics.drawable.shapes.RectShape { ctor public ArcShape(float, float); + method public final float getStartAngle(); + method public final float getSweepAngle(); } public class OvalShape extends android.graphics.drawable.shapes.RectShape { @@ -21094,13 +21107,14 @@ package android.media { 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(); @@ -22258,6 +22272,7 @@ package android.media { field public static final java.lang.String KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval"; field public static final java.lang.String KEY_LANGUAGE = "language"; + field public static final java.lang.String KEY_LATENCY = "latency"; field public static final java.lang.String KEY_LEVEL = "level"; field public static final java.lang.String KEY_MAX_HEIGHT = "max-height"; field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size"; @@ -22441,7 +22456,7 @@ package android.media { 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; @@ -22453,6 +22468,7 @@ package android.media { 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(); @@ -23280,6 +23296,10 @@ package android.media { 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(); @@ -23293,6 +23313,53 @@ package android.media { 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 { @@ -26172,8 +26239,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -36461,10 +36526,10 @@ package android.service.autofill { 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 { @@ -36472,6 +36537,23 @@ package android.service.autofill { 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_DATA_TYPE_ADDRESS = 2; // 0x2 + field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3 + field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 + field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + } + + 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(); + method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence); + } + } package android.service.carrier { @@ -42637,30 +42719,51 @@ package android.util { method public abstract void setValue(T, float); } - public final class Half { + public final class Half extends java.lang.Number implements java.lang.Comparable { + ctor public Half(short); + ctor public Half(float); + ctor public Half(double); + ctor public Half(java.lang.String) throws java.lang.NumberFormatException; method public static short abs(short); method public static short ceil(short); + method public static int compare(short, short); + method public int compareTo(android.util.Half); method public static short copySign(short, short); + method public double doubleValue(); method public static boolean equals(short, short); + method public float floatValue(); method public static short floor(short); method public static int getExponent(short); method public static int getSign(short); method public static int getSignificand(short); method public static boolean greater(short, short); method public static boolean greaterEquals(short, short); + method public static int halfToIntBits(short); + method public static int halfToRawIntBits(short); + method public static short halfToShortBits(short); + method public short halfValue(); + method public static int hashCode(short); + method public static short intBitsToHalf(int); + method public int intValue(); method public static boolean isInfinite(short); + method public boolean isNaN(); method public static boolean isNaN(short); method public static boolean isNormalized(short); method public static boolean less(short, short); method public static boolean lessEquals(short, short); + method public long longValue(); method public static short max(short, short); method public static short min(short, short); + method public static short parseHalf(java.lang.String) throws java.lang.NumberFormatException; method public static short round(short); method public static float toFloat(short); + method public static short toHalf(float); method public static java.lang.String toHexString(short); method public static java.lang.String toString(short); method public static short trunc(short); - method public static short valueOf(float); + method public static android.util.Half valueOf(short); + method public static android.util.Half valueOf(float); + method public static android.util.Half valueOf(java.lang.String); field public static final short EPSILON = 5120; // 0x1400 field public static final short LOWEST_VALUE = -1025; // 0xfffffbff field public static final int MAX_EXPONENT = 15; // 0xf @@ -44930,8 +45033,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + method public final <T extends android.view.View> T findViewById(int); + method public final <T extends android.view.View> T findViewWithTag(java.lang.Object); method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int); method protected deprecated boolean fitSystemWindows(android.graphics.Rect); method public android.view.View focusSearch(int); diff --git a/api/test-removed.txt b/api/test-removed.txt index e4678113d08f..c5dbf8daf0a4 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -20,25 +20,14 @@ package android.app.admin { package android.content { public abstract class Context { - method public deprecated android.content.Context createCredentialEncryptedStorageContext(); - method public deprecated android.content.Context createDeviceEncryptedStorageContext(); method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(java.lang.String); - method public deprecated boolean isCredentialEncryptedStorage(); - method public deprecated boolean isDeviceEncryptedStorage(); - method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); - method public deprecated boolean migrateSharedPreferencesFrom(android.content.Context, java.lang.String); } } package android.content.pm { - public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { - field public deprecated java.lang.String credentialEncryptedDataDir; - field public deprecated java.lang.String deviceEncryptedDataDir; - } - public class ComponentInfo extends android.content.pm.PackageItemInfo { field public deprecated boolean encryptionAware; } @@ -47,12 +36,6 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 } - public abstract class PackageManager { - field public static final deprecated int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 - field public static final deprecated int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final deprecated int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 - } - } package android.database { @@ -169,10 +152,6 @@ package android.os { 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); } } @@ -190,15 +169,6 @@ package android.os.storage { } -package android.preference { - - public class PreferenceManager { - method public deprecated void setStorageCredentialEncrypted(); - method public deprecated void setStorageDeviceEncrypted(); - } - -} - package android.provider { public class Browser { diff --git a/core/java/android/annotation/HalfFloat.java b/core/java/android/annotation/HalfFloat.java index d3e9f08dee9e..256008c5e31f 100644 --- a/core/java/android/annotation/HalfFloat.java +++ b/core/java/android/annotation/HalfFloat.java @@ -37,7 +37,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * }</pre> * * @see android.util.Half - * @see android.util.Half#valueOf(float) + * @see android.util.Half#toHalf(float) * @see android.util.Half#toFloat(short) * * @hide diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 165229912547..0608acbd6ca0 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1024,12 +1024,18 @@ public class ApplicationPackageManager extends PackageManager { } @Override - @SuppressWarnings("unchecked") public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { + return queryContentProviders(processName, uid, flags, null); + } + + @Override + @SuppressWarnings("unchecked") + public List<ProviderInfo> queryContentProviders(String processName, + int uid, int flags, String metaDataKey) { try { ParceledListSlice<ProviderInfo> slice = - mPM.queryContentProviders(processName, uid, flags); + mPM.queryContentProviders(processName, uid, flags, metaDataKey); return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 3e9b9877ee49..7ee93d0ee790 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -38,6 +38,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetManager; +import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -49,6 +50,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Binder; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Debug; import android.os.Environment; @@ -1925,8 +1927,8 @@ class ContextImpl extends Context { final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.mResources = createResources(mActivityToken, pi, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo()); + c.setResources(createResources(mActivityToken, pi, displayId, null, + getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } @@ -1962,8 +1964,8 @@ class ContextImpl extends Context { final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - c.mResources = createResources(mActivityToken, pi, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo()); + c.setResources(createResources(mActivityToken, pi, displayId, null, + getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } @@ -1990,7 +1992,7 @@ class ContextImpl extends Context { final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.mResources = ResourcesManager.getInstance().getResources( + context.setResources(ResourcesManager.getInstance().getResources( mActivityToken, mPackageInfo.getResDir(), paths, @@ -1999,7 +2001,7 @@ class ContextImpl extends Context { displayId, null, mPackageInfo.getCompatibilityInfo(), - classLoader); + classLoader)); return context; } @@ -2013,8 +2015,8 @@ class ContextImpl extends Context { mUser, mFlags, mClassLoader); final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY; - context.mResources = createResources(mActivityToken, mPackageInfo, displayId, - overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()); + context.setResources(createResources(mActivityToken, mPackageInfo, displayId, + overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); return context; } @@ -2028,8 +2030,8 @@ class ContextImpl extends Context { mUser, mFlags, mClassLoader); final int displayId = display.getDisplayId(); - context.mResources = createResources(mActivityToken, mPackageInfo, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo()); + context.setResources(createResources(mActivityToken, mPackageInfo, displayId, null, + getDisplayAdjustments(displayId).getCompatibilityInfo())); context.mDisplay = display; return context; } @@ -2136,7 +2138,7 @@ class ContextImpl extends Context { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0, null); - context.mResources = packageInfo.getResources(); + context.setResources(packageInfo.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); return context; @@ -2146,7 +2148,7 @@ class ContextImpl extends Context { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0, null); - context.mResources = packageInfo.getResources(); + context.setResources(packageInfo.getResources()); return context; } @@ -2185,7 +2187,7 @@ class ContextImpl extends Context { // Create the base resources for which all configuration contexts for this Activity // will be rebased upon. - context.mResources = resourcesManager.createBaseActivityResources(activityToken, + context.setResources(resourcesManager.createBaseActivityResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), @@ -2193,7 +2195,7 @@ class ContextImpl extends Context { displayId, overrideConfiguration, compatInfo, - classLoader); + classLoader)); context.mDisplay = resourcesManager.getAdjustedDisplay(displayId, context.getResources()); return context; @@ -2232,7 +2234,7 @@ class ContextImpl extends Context { if (container != null) { mBasePackageName = container.mBasePackageName; mOpPackageName = container.mOpPackageName; - mResources = container.mResources; + setResources(container.mResources); mDisplay = container.mDisplay; } else { mBasePackageName = packageInfo.mPackageName; @@ -2251,6 +2253,14 @@ class ContextImpl extends Context { mContentResolver = new ApplicationContentResolver(this, mainThread, user); } + void setResources(Resources r) { + if (mPackageInfo.getTargetSdkVersion() < VERSION_CODES.O) { + mResources = new CompatResources(r, this); + } else { + mResources = r; + } + } + void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { mPackageInfo.installSystemApplicationInfo(info, classLoader); } diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 2d6b45d9c33d..3887556ebe7d 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -174,12 +174,26 @@ public class JobInfo implements Parcelable { */ public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0; + /** + * @hide + */ + public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; + + /** + * @hide + */ + public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1; + + /** + * @hide + */ + public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2; + private final int jobId; private final PersistableBundle extras; private final Bundle transientExtras; private final ComponentName service; - private final boolean requireCharging; - private final boolean requireDeviceIdle; + private final int constraintFlags; private final TriggerContentUri[] triggerContentUris; private final long triggerContentUpdateDelay; private final long triggerContentMaxDelay; @@ -241,14 +255,28 @@ public class JobInfo implements Parcelable { * Whether this job needs the device to be plugged in. */ public boolean isRequireCharging() { - return requireCharging; + return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; + } + + /** + * Whether this job needs the device's battery level to not be at below the critical threshold. + */ + public boolean isRequireBatteryNotLow() { + return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0; } /** * Whether this job needs the device to be in an Idle maintenance window. */ public boolean isRequireDeviceIdle() { - return requireDeviceIdle; + return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; + } + + /** + * @hide + */ + public int getConstraintFlags() { + return constraintFlags; } /** @@ -376,8 +404,7 @@ public class JobInfo implements Parcelable { extras = in.readPersistableBundle(); transientExtras = in.readBundle(); service = in.readParcelable(null); - requireCharging = in.readInt() == 1; - requireDeviceIdle = in.readInt() == 1; + constraintFlags = in.readInt(); triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); triggerContentUpdateDelay = in.readLong(); triggerContentMaxDelay = in.readLong(); @@ -401,8 +428,7 @@ public class JobInfo implements Parcelable { extras = b.mExtras.deepcopy(); transientExtras = b.mTransientExtras.deepcopy(); service = b.mJobService; - requireCharging = b.mRequiresCharging; - requireDeviceIdle = b.mRequiresDeviceIdle; + constraintFlags = b.mConstraintFlags; triggerContentUris = b.mTriggerContentUris != null ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) : null; @@ -434,8 +460,7 @@ public class JobInfo implements Parcelable { out.writePersistableBundle(extras); out.writeBundle(transientExtras); out.writeParcelable(service, flags); - out.writeInt(requireCharging ? 1 : 0); - out.writeInt(requireDeviceIdle ? 1 : 0); + out.writeInt(constraintFlags); out.writeTypedArray(triggerContentUris, flags); out.writeLong(triggerContentUpdateDelay); out.writeLong(triggerContentMaxDelay); @@ -563,8 +588,7 @@ public class JobInfo implements Parcelable { private int mPriority = PRIORITY_DEFAULT; private int mFlags; // Requirements. - private boolean mRequiresCharging; - private boolean mRequiresDeviceIdle; + private int mConstraintFlags; private int mNetworkType; private ArrayList<TriggerContentUri> mTriggerContentUris; private long mTriggerContentUpdateDelay = -1; @@ -651,7 +675,21 @@ public class JobInfo implements Parcelable { * @param requiresCharging Whether or not the device is plugged in. */ public Builder setRequiresCharging(boolean requiresCharging) { - mRequiresCharging = requiresCharging; + mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) + | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); + return this; + } + + /** + * Specify that to run this job, the device's battery level must not be low. + * This defaults to false. If true, the job will only run when the battery level + * is not low, which is generally the point where the user is given a "low battery" + * warning. + * @param batteryNotLow Whether or not the device's battery level must not be low. + */ + public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { + mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) + | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); return this; } @@ -666,7 +704,8 @@ public class JobInfo implements Parcelable { * window. */ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { - mRequiresDeviceIdle = requiresDeviceIdle; + mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) + | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); return this; } @@ -816,8 +855,8 @@ public class JobInfo implements Parcelable { */ public JobInfo build() { // Allow jobs with no constraints - What am I, a database? - if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging && - !mRequiresDeviceIdle && mNetworkType == NETWORK_TYPE_NONE && + if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 && + mNetworkType == NETWORK_TYPE_NONE && mTriggerContentUris == null) { throw new IllegalArgumentException("You're trying to build a job with no " + "constraints, this is not allowed."); @@ -843,7 +882,7 @@ public class JobInfo implements Parcelable { throw new IllegalArgumentException("Can't call setTransientExtras() on a " + "persisted job"); } - if (mBackoffPolicySet && mRequiresDeviceIdle) { + if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { throw new IllegalArgumentException("An idle mode job will not respect any" + " back-off policy, so calling setBackoffCriteria with" + " setRequiresDeviceIdle is an error."); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 44f6c43c1a1f..aff00c3e84b6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -730,12 +730,6 @@ public abstract class Context { */ public abstract boolean moveSharedPreferencesFrom(Context sourceContext, String name); - /** @removed */ - @Deprecated - public boolean migrateSharedPreferencesFrom(Context sourceContext, String name) { - return moveSharedPreferencesFrom(sourceContext, name); - } - /** * Delete an existing shared preferences file. * @@ -1451,12 +1445,6 @@ public abstract class Context { */ public abstract boolean moveDatabaseFrom(Context sourceContext, String name); - /** @removed */ - @Deprecated - public boolean migrateDatabaseFrom(Context sourceContext, String name) { - return moveDatabaseFrom(sourceContext, name); - } - /** * Delete an existing private SQLiteDatabase associated with this Context's * application package. @@ -4382,12 +4370,6 @@ public abstract class Context { */ public abstract Context createDeviceProtectedStorageContext(); - /** @removed */ - @Deprecated - public Context createDeviceEncryptedStorageContext() { - return createDeviceProtectedStorageContext(); - } - /** * Return a new Context object for the current Context but whose storage * APIs are backed by credential-protected storage. This is the default @@ -4416,12 +4398,6 @@ public abstract class Context { @SystemApi public abstract Context createCredentialProtectedStorageContext(); - /** @removed */ - @Deprecated - public Context createCredentialEncryptedStorageContext() { - return createCredentialProtectedStorageContext(); - } - /** * Gets the display adjustments holder for this context. This information * is provided on a per-application or activity basis and is used to simulate lower density @@ -4462,12 +4438,6 @@ public abstract class Context { */ public abstract boolean isDeviceProtectedStorage(); - /** @removed */ - @Deprecated - public boolean isDeviceEncryptedStorage() { - return isDeviceProtectedStorage(); - } - /** * Indicates if the storage APIs of this Context are backed by * credential-protected storage. @@ -4478,12 +4448,6 @@ public abstract class Context { @SystemApi public abstract boolean isCredentialProtectedStorage(); - /** @removed */ - @Deprecated - public boolean isCredentialEncryptedStorage() { - return isCredentialProtectedStorage(); - } - /** * @hide */ diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9737b11a0133..b4d77a0fe54a 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -720,10 +720,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public String deviceProtectedDataDir; - /** @removed */ - @Deprecated - public String deviceEncryptedDataDir; - /** * Full path to the credential-protected directory assigned to the package * for its persistent data. @@ -733,10 +729,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @SystemApi public String credentialProtectedDataDir; - /** @removed */ - @Deprecated - public String credentialEncryptedDataDir; - /** * Full path to the directory where native JNI libraries are stored. */ @@ -1140,8 +1132,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; dataDir = orig.dataDir; - deviceEncryptedDataDir = deviceProtectedDataDir = orig.deviceProtectedDataDir; - credentialEncryptedDataDir = credentialProtectedDataDir = orig.credentialProtectedDataDir; + deviceProtectedDataDir = orig.deviceProtectedDataDir; + credentialProtectedDataDir = orig.credentialProtectedDataDir; uid = orig.uid; minSdkVersion = orig.minSdkVersion; targetSdkVersion = orig.targetSdkVersion; @@ -1264,8 +1256,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = source.readString(); sharedLibraryFiles = source.readStringArray(); dataDir = source.readString(); - deviceEncryptedDataDir = deviceProtectedDataDir = source.readString(); - credentialEncryptedDataDir = credentialProtectedDataDir = source.readString(); + deviceProtectedDataDir = source.readString(); + credentialProtectedDataDir = source.readString(); uid = source.readInt(); minSdkVersion = source.readInt(); targetSdkVersion = source.readInt(); @@ -1336,10 +1328,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return; } - deviceEncryptedDataDir = deviceProtectedDataDir = Environment + deviceProtectedDataDir = Environment .getDataUserDePackageDirectory(volumeUuid, userId, packageName) .getAbsolutePath(); - credentialEncryptedDataDir = credentialProtectedDataDir = Environment + credentialProtectedDataDir = Environment .getDataUserCePackageDirectory(volumeUuid, userId, packageName) .getAbsolutePath(); diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index c08bd1db8302..41311eb04837 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -55,7 +55,8 @@ interface ILauncherApps { String callingPackage, String packageName, int flags, in UserHandle user); ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, - in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user); + in List shortcutIds, in ComponentName componentName, in Intent intent, int flags, + in UserHandle user); void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); boolean startShortcut(String callingPackage, String packageName, String id, diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 11948dbc6a29..147b3e13b998 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -211,7 +211,7 @@ interface IPackageManager { inout List<ProviderInfo> outInfo); ParceledListSlice queryContentProviders( - String processName, int uid, int flags); + String processName, int uid, int flags, String metaDataKey); InstrumentationInfo getInstrumentationInfo( in ComponentName className, int flags); diff --git a/core/java/android/content/pm/IPinItemRequest.aidl b/core/java/android/content/pm/IPinItemRequest.aidl index efe283527b01..eddce5849432 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 @@ import android.os.Bundle; 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 0866af2f1bec..776492a140c3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -275,7 +275,18 @@ public class LauncherApps { @Deprecated public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST; - /** @hide */ + /** + * Include chooser shortcuts in the result. + * STOPSHIP TODO: Unless explicitly requesting chooser fields, we should strip out chooser + * relevant fields from the Shortcut. This should also be adequately documented. + */ + public static final int FLAG_MATCH_CHOOSER = 1 << 4; + + /** + * Does not retrieve CHOOSER only shortcuts. + * TODO: Add another flag for MATCH_ALL_PINNED + * @hide + */ public static final int FLAG_MATCH_ALL_KINDS = FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST; @@ -308,6 +319,7 @@ public class LauncherApps { FLAG_MATCH_DYNAMIC, FLAG_MATCH_PINNED, FLAG_MATCH_MANIFEST, + FLAG_MATCH_CHOOSER, FLAG_GET_KEY_FIELDS_ONLY, }) @Retention(RetentionPolicy.SOURCE) @@ -324,6 +336,9 @@ public class LauncherApps { @Nullable ComponentName mActivity; + @Nullable + Intent mIntent; + @QueryFlags int mQueryFlags; @@ -368,6 +383,14 @@ public class LauncherApps { } /** + * If non-null, returns only shortcuts with intent filters that match this intent. + */ + public ShortcutQuery setIntent(@Nullable Intent intent) { + mIntent = intent; + return this; + } + + /** * Set query options. At least one of the {@code MATCH} flags should be set. Otherwise, * no shortcuts will be returned. * @@ -681,7 +704,7 @@ public class LauncherApps { try { return mService.getShortcuts(mContext.getPackageName(), query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, - query.mQueryFlags, user) + query.mIntent, query.mQueryFlags, user) .getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1291,28 +1314,14 @@ public class LauncherApps { 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; - mInner = inner; - } - - /** - * @hide - */ - public PinItemRequest(AppWidgetProviderInfo appWidgetInfo, IPinItemRequest inner) { - mRequestType = REQUEST_TYPE_APPWIDGET; - mShortcutInfo = null; - mAppWidgetInfo = appWidgetInfo; + public PinItemRequest(IPinItemRequest inner, int type) { mInner = inner; + mRequestType = type; } /** @@ -1330,7 +1339,11 @@ public class LauncherApps { */ @Nullable public ShortcutInfo getShortcutInfo() { - return mShortcutInfo; + try { + return mInner.getShortcutInfo(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } } /** @@ -1339,12 +1352,16 @@ public class LauncherApps { */ @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 +1398,12 @@ public class LauncherApps { 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 6646402ef62f..0482f5136b0c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -411,17 +411,6 @@ public abstract class PackageManager { */ public static final int MATCH_DIRECT_BOOT_AWARE = 0x00080000; - /** @removed */ - @Deprecated - public static final int MATCH_ENCRYPTION_UNAWARE = 0x00040000; - /** @removed */ - @Deprecated - public static final int MATCH_ENCRYPTION_AWARE = 0x00080000; - /** @removed */ - @Deprecated - public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE - | MATCH_ENCRYPTION_UNAWARE; - /** * Querying flag: include only components from applications that are marked * with {@link ApplicationInfo#FLAG_SYSTEM}. @@ -4473,6 +4462,27 @@ public abstract class PackageManager { String processName, int uid, @ComponentInfoFlags int flags); /** + * Same as {@link #queryContentProviders}, except when {@code metaDataKey} is not null, + * it only returns providers which have metadata with the {@code metaDataKey} key. + * + * <p>DO NOT USE the {@code metaDataKey} parameter, unless you're the contacts provider. + * You really shouldn't need it. Other apps should use {@link #queryIntentContentProviders} + * instead. + * + * <p>The {@code metaDataKey} parameter was added to allow the contacts provider to quickly + * scan the GAL providers on the device. Unfortunately the discovery protocol used metadata + * to mark GAL providers, rather than intent filters, so we can't use + * {@link #queryIntentContentProviders} for that. + * + * @hide + */ + public List<ProviderInfo> queryContentProviders( + String processName, int uid, @ComponentInfoFlags int flags, String metaDataKey) { + // Provide the default implementation for mocks. + return queryContentProviders(processName, uid, flags); + } + + /** * Retrieve all of the information we know about a particular * instrumentation class. * diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index f1f268306bb2..d3d3c66b20c0 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -21,8 +21,10 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.TaskStackBuilder; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -38,10 +40,12 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -95,6 +99,14 @@ public final class ShortcutInfo implements Parcelable { public static final int FLAG_MASKABLE_BITMAP = 1 << 9; /** @hide */ + public static final int FLAG_CHOOSER = 1 << 10; + + /** + * TODO: Add FLAG_CHOOSER_INFO_OMITTED to reflect that chooser info was omitted in the Shortcut + * due to the context in which it was retrieved. + * TODO: Add a FLAG_LAUNCHABLE to reflect whether or not the Shortcut has a launchable intent + * @hide + */ @IntDef(flag = true, value = { FLAG_DYNAMIC, @@ -107,6 +119,7 @@ public final class ShortcutInfo implements Parcelable { FLAG_STRINGS_RESOLVED, FLAG_IMMUTABLE, FLAG_MASKABLE_BITMAP, + FLAG_CHOOSER, }) @Retention(RetentionPolicy.SOURCE) public @interface ShortcutFlags {} @@ -201,6 +214,24 @@ public final class ShortcutInfo implements Parcelable { @Nullable private PersistableBundle[] mIntentPersistableExtrases; + /** + * If used in a chooser, extras that should be added into the intent passed through. + */ + @Nullable + private PersistableBundle mChooserExtras; + + /** + * Intent filters to be used if the shortcut is to be used in a chooser context. + */ + @Nullable + private IntentFilter[] mChooserIntentFilters; + + /** + * Component names corresponding to the above intent filters. + */ + @Nullable + private ComponentName[] mChooserComponentNames; + private int mRank; /** @@ -250,6 +281,13 @@ public final class ShortcutInfo implements Parcelable { mDisabledMessageResId = b.mDisabledMessageResId; mCategories = cloneCategories(b.mCategories); mIntents = cloneIntents(b.mIntents); + if (b.mChooserIntentFilters != null) { + mChooserIntentFilters = b.mChooserIntentFilters.toArray(new IntentFilter[0]); + } + if (b.mChooserComponentNames != null) { + mChooserComponentNames = b.mChooserComponentNames.toArray(new ComponentName[0]); + } + mChooserExtras = b.mChooserExtras; fixUpIntentExtras(); mRank = b.mRank; mExtras = b.mExtras; @@ -330,8 +368,28 @@ public final class ShortcutInfo implements Parcelable { if (mTitle == null && mTitleResId == 0) { throw new IllegalArgumentException("Short label must be provided"); } - Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided"); - Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided"); + + // For a shortcut to be valid, there should either be an Intent, or a non-empty set of + // intent filters. + if (mIntents == null || mIntents.length == 0) { + Preconditions.checkNotNull(mChooserIntentFilters, + "Intent must be provided if not a chooser target"); + Preconditions.checkNotNull(mChooserComponentNames, + "Intent must be provided if not a chooser target"); + } + + // If ChooserIntentFilter are provided, they should match the length of the provided + // component names. + if (mChooserIntentFilters != null) { + if (mChooserComponentNames == null + || mChooserIntentFilters.length != mChooserComponentNames.length) { + throw new IllegalArgumentException("Inconsistent intent filters and " + + "component names given"); + } + if (mChooserIntentFilters.length == 0 || mChooserComponentNames.length == 0) { + throw new IllegalArgumentException("Empty intent filter and component names given"); + } + } } /** @@ -376,6 +434,10 @@ public final class ShortcutInfo implements Parcelable { mDisabledMessageResName = source.mDisabledMessageResName; mIconResName = source.mIconResName; } + // TODO: Omit these by default and add a new clone flag. + mChooserIntentFilters = source.mChooserIntentFilters; + mChooserComponentNames = source.mChooserComponentNames; + mChooserExtras = source.mChooserExtras; } else { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; @@ -503,6 +565,25 @@ public final class ShortcutInfo implements Parcelable { } /** + * Whether the shortcut has any intentFilter matching the passed in one. + * @hide + */ + @VisibleForTesting + public boolean hasMatchingFilter(ContentResolver resolver, Intent intent) { + if (mChooserIntentFilters == null) { + return false; + } + for (IntentFilter filter : mChooserIntentFilters) { + int match = filter.match(resolver, intent, false, TAG); + if (match > 0) { + return true; + } + } + return false; + } + + + /** * Extract the entry name from a fully-donated resource name. * e.g. "com.android.app1:drawable/icon1" -> "icon1" * @hide @@ -685,6 +766,15 @@ public final class ShortcutInfo implements Parcelable { if (source.mExtras != null) { mExtras = source.mExtras; } + if (source.mChooserExtras != null) { + mChooserExtras = source.mChooserExtras; + } + if (source.mChooserIntentFilters != null) { + mChooserIntentFilters = source.mChooserIntentFilters; + } + if (source.mChooserComponentNames != null) { + mChooserComponentNames = source.mChooserComponentNames; + } } /** @@ -746,6 +836,12 @@ public final class ShortcutInfo implements Parcelable { private PersistableBundle mExtras; + private PersistableBundle mChooserExtras; + + private List<IntentFilter> mChooserIntentFilters; + + private List<ComponentName> mChooserComponentNames; + /** * Old style constructor. * @hide @@ -1032,6 +1128,40 @@ public final class ShortcutInfo implements Parcelable { } /** + * Extras that can be added which will be added to the Intent used to launch the app if + * launched from a chooser context. + */ + @NonNull + public Builder setChooserExtras(@NonNull PersistableBundle extras) { + mChooserExtras = extras; + return this; + } + + /** + * IntentFilters and the components that should resolve a match for a given chooser target. + * If multiple matches are found, the component corresponding to the closest match will be + * used. + * + * @param filter IntendFilter that if matched will have the intent forwarded to the given + * component + * @param name The component that an intent that passes this filter will resolve to. + */ + public Builder addChooserIntentFilter(@NonNull IntentFilter filter, + @NonNull ComponentName name) { + Preconditions.checkNotNull(filter, "intent filter cannot be null"); + Preconditions.checkNotNull(name, "component name cannot be null"); + + if (mChooserIntentFilters == null || mChooserComponentNames == null) { + mChooserIntentFilters = new ArrayList<>(); + mChooserComponentNames = new ArrayList<>(); + } + + mChooserIntentFilters.add(filter); + mChooserComponentNames.add(name); + return this; + } + + /** * Creates a {@link ShortcutInfo} instance. */ @NonNull @@ -1232,6 +1362,30 @@ public final class ShortcutInfo implements Parcelable { } /** + * Retrieve the extras that will be added in to any intent launched through the chooser. + */ + @NonNull + public PersistableBundle getChooserExtras() { + return mChooserExtras; + } + + /** + * Retrieve the list of intent filters for chooser targets. + */ + @NonNull + public IntentFilter[] getChooserIntentFilters() { + return mChooserIntentFilters; + } + + /** + * Retrieve the list of component names corresponding to the above intent filters. + */ + @NonNull + public ComponentName[] getChooserComponentNames() { + return mChooserComponentNames; + } + + /** * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each * {@link #getActivity} for each of the two types of shortcuts (static and dynamic). * @@ -1352,6 +1506,11 @@ public final class ShortcutInfo implements Parcelable { return hasFlags(FLAG_PINNED); } + /** Return whether a shortcut can be shown in the chooser. */ + public boolean isChooser() { + return hasFlags(FLAG_CHOOSER); + } + /** * Return whether a shortcut is static; that is, whether a shortcut is * published from AndroidManifest.xml. If {@code true}, the shortcut is @@ -1380,6 +1539,14 @@ public final class ShortcutInfo implements Parcelable { return isPinned() && !(isDynamic() || isManifestShortcut()); } + /** + * @return true if pinned but neither static nor dynamic. + * @hide + */ + public boolean isDynamicOrChooser() { + return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_CHOOSER); + } + /** @hide */ public boolean isOriginallyFromManifest() { return hasFlags(FLAG_IMMUTABLE); @@ -1661,6 +1828,19 @@ public final class ShortcutInfo implements Parcelable { mCategories.add(source.readString().intern()); } } + + // We put a placeholder empty array in to keep the parcelable order, but can do away with + // them at this point if they're empty. + mChooserComponentNames = source.readParcelableArray(cl, ComponentName.class); + if (mChooserComponentNames.length == 0) { + mChooserComponentNames = null; + } + + mChooserIntentFilters = source.readParcelableArray(cl, IntentFilter.class); + if (mChooserIntentFilters.length == 0) { + mChooserIntentFilters = null; + } + mChooserExtras = source.readPersistableBundle(cl); } @Override @@ -1707,6 +1887,17 @@ public final class ShortcutInfo implements Parcelable { } else { dest.writeInt(0); } + if (mChooserComponentNames != null) { + dest.writeParcelableArray(mChooserComponentNames, flags); + } else { + dest.writeParcelableArray(new ComponentName[0], flags); + } + if (mChooserIntentFilters != null) { + dest.writeParcelableArray(mChooserIntentFilters, flags); + } else { + dest.writeParcelableArray(new IntentFilter[0], flags); + } + dest.writePersistableBundle(mChooserExtras); } public static final Creator<ShortcutInfo> CREATOR = diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 87a6d4a5fa09..696fe81ba5a5 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -44,8 +44,8 @@ public abstract class ShortcutServiceInternal { getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags, - int userId); + @Nullable ComponentName componentName, @Nullable Intent intent, + @ShortcutQuery.QueryFlags int flags, int userId); public abstract boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, diff --git a/core/java/android/content/res/CompatResources.java b/core/java/android/content/res/CompatResources.java new file mode 100644 index 000000000000..15575fd10e83 --- /dev/null +++ b/core/java/android/content/res/CompatResources.java @@ -0,0 +1,63 @@ +/* + * 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.content.res; + +import android.annotation.ColorRes; +import android.annotation.DrawableRes; +import android.content.Context; +import android.graphics.drawable.Drawable; + +import java.lang.ref.WeakReference; + +/** + * Version of resources generated for apps targeting <26. + * @hide + */ +public class CompatResources extends Resources { + + private final WeakReference<Context> mContext; + + public CompatResources(Resources base, Context context) { + super(base.getClassLoader()); + setImpl(base.getImpl()); + mContext = new WeakReference<>(context); + } + + @Override + public Drawable getDrawable(@DrawableRes int id) throws NotFoundException { + return getDrawable(id, getTheme()); + } + + @Override + public Drawable getDrawableForDensity(@DrawableRes int id, int density) + throws NotFoundException { + return getDrawableForDensity(id, density, getTheme()); + } + + @Override + public int getColor(@ColorRes int id) throws NotFoundException { + return getColor(id, getTheme()); + } + + @Override + public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException { + return getColorStateList(id, getTheme()); + } + + private Theme getTheme() { + Context c = mContext.get(); + return c != null ? c.getTheme() : null; + } +} diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl index f249daf87626..362ea9d6bc54 100644 --- a/core/java/android/net/INetworkScoreService.aidl +++ b/core/java/android/net/INetworkScoreService.aidl @@ -18,7 +18,7 @@ package android.net; 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 @@ interface INetworkScoreService /** * 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/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index edfaee4ff546..815d4807a39e 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -18,7 +18,6 @@ package android.net; 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; 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 index d968343141b6..ee7f1d1efdf1 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 000000000000..fca0a2e10bcd --- /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 bbc1c79eca76..000000000000 --- 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/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 3a441c7cb1ca..9ffe2fe7a709 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -118,6 +118,13 @@ public class BatteryManager { */ public static final String EXTRA_CHARGE_COUNTER = "charge_counter"; + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * Current int sequence number of the update. + * {@hide} + */ + public static final String EXTRA_SEQUENCE = "seq"; + // values for "status" field in the ACTION_BATTERY_CHANGED Intent public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN; public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING; diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index d299672ea1d5..63d3e7ad2135 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -18,8 +18,10 @@ package android.os; 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 @@ public final class Looper { 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 8c758470c83a..d066db1fc4cc 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 @@ import android.util.TimeUtils; */ 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 @@ public final class Message implements Parcelable { * {@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 @@ public final class Message implements Parcelable { * 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 @@ public final class Message implements Parcelable { /*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 @@ public final class Message implements Parcelable { } /** - * 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 @@ public final class Message implements Parcelable { } /** - * 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 @@ public final class Message implements Parcelable { * @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 final class Message implements Parcelable { public long getWhen() { return when; } - + public void setTarget(Handler target) { this.target = target; } @@ -367,8 +369,8 @@ public final class Message implements Parcelable { 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 @@ public final class Message implements Parcelable { 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 @@ public final class Message implements Parcelable { /** * 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 @@ public final class Message implements Parcelable { 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 @@ public final class Message implements Parcelable { 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 4f2e968009a6..2a8c52e92c60 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -18,9 +18,11 @@ package android.os; 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 @@ import java.util.ArrayList; * 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 @@ public final class MessageQueue { } } + 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 21c70f9012d1..13a495e57d3b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1097,40 +1097,6 @@ public class UserManager { } } - /** @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 f8da87ab4702..ecec448aba4c 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 @@ public class WorkSource implements Parcelable { 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 @@ public class WorkSource implements Parcelable { 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/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java index a2f4db6e7522..756c3f482120 100644 --- a/core/java/android/preference/PreferenceManager.java +++ b/core/java/android/preference/PreferenceManager.java @@ -420,12 +420,6 @@ public class PreferenceManager { mSharedPreferences = null; } - /** @removed */ - @Deprecated - public void setStorageDeviceEncrypted() { - setStorageDeviceProtected(); - } - /** * Explicitly set the storage location used internally by this class to be * credential-protected storage. This is the default storage area for apps @@ -445,12 +439,6 @@ public class PreferenceManager { mSharedPreferences = null; } - /** @removed */ - @Deprecated - public void setStorageCredentialEncrypted() { - setStorageCredentialProtected(); - } - /** * Indicates if the storage location used internally by this class is the * default provided by the hosting {@link Context}. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 40de928b49bb..b55a349c14a9 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6905,6 +6905,12 @@ public final class Settings { 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 91c668ef32e2..ba75c8bee039 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -23,7 +23,6 @@ import android.content.IntentSender; 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 @@ import java.util.ArrayList; * * <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 @@ import java.util.ArrayList; * .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 @@ import java.util.ArrayList; * </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 @@ import java.util.ArrayList; 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_DATA_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 @@ public final class FillResponse implements Parcelable { } /** @hide */ - public @Nullable ArraySet<AutoFillId> getSavableIds() { - return mSavableIds; + public @Nullable SaveInfo getSaveInfo() { + return mSaveInfo; } /** @hide */ @@ -202,7 +236,10 @@ public final class FillResponse implements Parcelable { */ 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 @@ public final class FillResponse implements Parcelable { 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_DATA_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. - * @return This builder. + * <p>See {@link FillResponse} for more info. * - * @see FillResponse + * @return This builder. */ - public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) { + public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) { throwIfDestroyed(); - if (ids == null) { - return this; - } - for (AutoFillId id : ids) { - if (mSavableIds == null) { - mSavableIds = new ArraySet<>(); - } - mSavableIds.add(id); + if (mSaveInfoBuilder != null) { + throw new IllegalStateException("addSavableFields() already called"); } + mSaveInfo = saveInfo; return this; } @@ -340,9 +373,11 @@ public final class FillResponse implements Parcelable { */ 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 @@ public final class FillResponse implements Parcelable { @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 @@ public final class FillResponse implements Parcelable { @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 @@ public final class FillResponse implements Parcelable { 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/service/autofill/SaveInfo.aidl b/core/java/android/service/autofill/SaveInfo.aidl new file mode 100644 index 000000000000..8cda608e1814 --- /dev/null +++ b/core/java/android/service/autofill/SaveInfo.aidl @@ -0,0 +1,19 @@ +/** + * 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; + +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 000000000000..096f28b60687 --- /dev/null +++ b/core/java/android/service/autofill/SaveInfo.java @@ -0,0 +1,263 @@ +/* + * 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_DATA_TYPE_GENERIC = 0; + + /** + * Type used when the {@link FillResponse} represents user credentials that have a password. + */ + public static final int SAVE_DATA_TYPE_PASSWORD = 1; + + + /** + * Type used on when the {@link FillResponse} represents a physical address (such as street, + * city, state, etc). + */ + public static final int SAVE_DATA_TYPE_ADDRESS = 2; + + /** + * Type used when the {@link FillResponse} represents a credit card. + */ + public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; + + private final @SaveDataType int mType; + private ArraySet<AutoFillId> mSavableIds; + private final CharSequence mDescription; + + /** @hide */ + @IntDef({ + SAVE_DATA_TYPE_GENERIC, + SAVE_DATA_TYPE_PASSWORD, + SAVE_DATA_TYPE_ADDRESS, + SAVE_DATA_TYPE_CREDIT_CARD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SaveDataType { + } + + private SaveInfo(Builder builder) { + mType = builder.mType; + mSavableIds = builder.mSavableIds; + mDescription = builder.mDescription; + } + + /** @hide */ + public @Nullable ArraySet<AutoFillId> getSavableIds() { + return mSavableIds; + } + + /** @hide */ + public int getType() { + return mType; + } + + /** @hide */ + public CharSequence getDescription() { + return mDescription; + } + + /** @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 @SaveDataType int mType; + private ArraySet<AutoFillId> mSavableIds; + private CharSequence mDescription; + private boolean mDestroyed; + + /** + * Creates a new builder. + * + * @param type the type of information the associated {@link FillResponse} represents. Must + * be {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD}, + * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, or {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD}; + * otherwise it will assume {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}. + */ + public Builder(@SaveDataType int type) { + switch (type) { + case SAVE_DATA_TYPE_PASSWORD: + case SAVE_DATA_TYPE_ADDRESS: + case SAVE_DATA_TYPE_CREDIT_CARD: + mType = type; + break; + default: + mType = SAVE_DATA_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; + } + + /** + * Sets an optional description to be shown in the UI when the user is asked to save. + * + * <p>Typically, it describes how the data will be stored by the service, so it can help + * users to decide whether they can trust the service to save their data. + * + * @param description a succint description. + * @return This Builder. + */ + public @NonNull Builder setDescription(@Nullable CharSequence description) { + mDescription = description; + 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); + parcel.writeCharSequence(mDescription); + } + + 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)); + } + builder.setDescription(parcel.readCharSequence()); + return builder.build(); + } + + @Override + public SaveInfo[] newArray(int size) { + return new SaveInfo[size]; + } + }; +} diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index e4f8dd1b12b8..84c2e8302e89 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -17,9 +17,13 @@ package android.util; import android.annotation.HalfFloat; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import sun.misc.FloatingDecimal; /** - * <p>Half is a utility class to manipulate half-precision 16-bit + * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a> * floating point data types (also called fp16 or binary16). A half-precision float can be * created from or converted to single-precision floats, and is stored in a short data type. @@ -88,7 +92,7 @@ import android.annotation.HalfFloat; * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p> */ @SuppressWarnings("SimplifiableIfStatement") -public final class Half { +public final class Half extends Number implements Comparable<Half> { /** * The number of bits used to represent a half-precision float value. */ @@ -164,7 +168,332 @@ public final class Half { private static final int FP32_DENORMAL_MAGIC = 126 << 23; private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); - private Half() { + private final @HalfFloat short mValue; + + /** + * Constructs a newly allocated {@code Half} object that represents the + * half-precision float type argument. + * + * @param value The value to be represented by the {@code Half} + */ + public Half(@HalfFloat short value) { + mValue = value; + } + + /** + * Constructs a newly allocated {@code Half} object that represents the + * argument converted to a half-precision float. + * + * @param value The value to be represented by the {@code Half} + * + * @see #toHalf(float) + */ + public Half(float value) { + mValue = toHalf(value); + } + + /** + * Constructs a newly allocated {@code Half} object that + * represents the argument converted to a half-precision float. + * + * @param value The value to be represented by the {@code Half} + * + * @see #toHalf(float) + */ + public Half(double value) { + mValue = toHalf((float) value); + } + + /** + * <p>Constructs a newly allocated {@code Half} object that represents the + * half-precision float value represented by the string. + * The string is converted to a half-precision float value as if by the + * {@link #valueOf(String)} method.</p> + * + * <p>Calling this constructor is equivalent to calling:</p> + * <pre> + * new Half(Float.parseFloat(value)) + * </pre> + * + * @param value A string to be converted to a {@code Half} + * @throws NumberFormatException if the string does not contain a parsable number + * + * @see Float#valueOf(java.lang.String) + * @see #toHalf(float) + */ + public Half(@NonNull String value) throws NumberFormatException { + mValue = toHalf(Float.parseFloat(value)); + } + + /** + * Returns the half-precision value of this {@code Half} as a {@code short} + * containing the bit representation described in {@link Half}. + * + * @return The half-precision float value represented by this object + */ + public @HalfFloat short halfValue() { + return mValue; + } + + /** + * Returns the value of this {@code Half} as a {@code byte} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code byte} + */ + @Override + public byte byteValue() { + return (byte) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code short} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code short} + */ + @Override + public short shortValue() { + return (short) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code int} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code int} + */ + @Override + public int intValue() { + return (int) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code long} after + * a narrowing primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code long} + */ + @Override + public long longValue() { + return (long) toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code float} after + * a widening primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code float} + */ + @Override + public float floatValue() { + return toFloat(mValue); + } + + /** + * Returns the value of this {@code Half} as a {@code double} after + * a widening primitive conversion. + * + * @return The half-precision float value represented by this object + * converted to type {@code double} + */ + @Override + public double doubleValue() { + return toFloat(mValue); + } + + /** + * Returns true if this {@code Half} value represents a Not-a-Number, + * false otherwise. + * + * @return True if the value is a NaN, false otherwise + */ + public boolean isNaN() { + return isNaN(mValue); + } + + /** + * Compares this object against the specified object. The result is {@code true} + * if and only if the argument is not {@code null} and is a {@code Half} object + * that represents the same half-precision value as the this object. Two + * half-precision values are considered to be the same if and only if the method + * {@link #halfToIntBits(short)} returns an identical {@code int} value for both. + * + * @param o The object to compare + * @return True if the objects are the same, false otherwise + * + * @see #halfToIntBits(short) + */ + @Override + public boolean equals(@Nullable Object o) { + return (o instanceof Half) && + (halfToIntBits(((Half) o).mValue) == halfToIntBits(mValue)); + } + + /** + * Returns a hash code for this {@code Half} object. The result is the + * integer bit representation, exactly as produced by the method + * {@link #halfToIntBits(short)}, of the primitive half-precision float + * value represented by this {@code Half} object. + * + * @return A hash code value for this object + */ + @Override + public int hashCode() { + return hashCode(mValue); + } + + /** + * Returns a string representation of the specified half-precision + * float value. See {@link #toString(short)} for more information. + * + * @return A string representation of this {@code Half} object + */ + @NonNull + @Override + public String toString() { + return toString(mValue); + } + + /** + * <p>Compares the two specified half-precision float values. The following + * conditions apply during the comparison:</p> + * + * <ul> + * <li>{@link #NaN} is considered by this method to be equal to itself and greater + * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li> + * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than + * {@link #NEGATIVE_ZERO}.</li> + * </ul> + * + * @param h The half-precision float value to compare to the half-precision value + * represented by this {@code Half} object + * + * @return The value {@code 0} if {@code x} is numerically equal to {@code y}; a + * value less than {@code 0} if {@code x} is numerically less than {@code y}; + * and a value greater than {@code 0} if {@code x} is numerically greater + * than {@code y} + */ + @Override + public int compareTo(@NonNull Half h) { + return compare(mValue, h.mValue); + } + + /** + * Returns a hash code for a half-precision float value. + * + * @param h The value to hash + * + * @return A hash code value for a half-precision float value + */ + public static int hashCode(@HalfFloat short h) { + return halfToIntBits(h); + } + + /** + * <p>Compares the two specified half-precision float values. The following + * conditions apply during the comparison:</p> + * + * <ul> + * <li>{@link #NaN} is considered by this method to be equal to itself and greater + * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li> + * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than + * {@link #NEGATIVE_ZERO}.</li> + * </ul> + * + * @param x The first half-precision float value to compare. + * @param y The second half-precision float value to compare + * + * @return The value {@code 0} if {@code x} is numerically equal to {@code y}, a + * value less than {@code 0} if {@code x} is numerically less than {@code y}, + * and a value greater than {@code 0} if {@code x} is numerically greater + * than {@code y} + */ + public static int compare(@HalfFloat short x, @HalfFloat short y) { + if (less(x, y)) return -1; + if (greater(x, y)) return 1; + + // Collapse NaNs, akin to halfToIntBits(), but we want to keep + // (signed) short value types to preserve the ordering of -0.0 + // and +0.0 + short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x; + short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y; + + return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1)); + } + + /** + * <p>Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.</p> + * + * <p>Similar to {@link #halfToIntBits(short)}, this method collapses all + * possible Not-a-Number values to a single canonical Not-a-Number value + * defined by {@link #NaN}.</p> + * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToIntBits(short) + */ + public static @HalfFloat short halfToShortBits(@HalfFloat short h) { + return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h; + } + + /** + * <p>Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.</p> + * + * <p>Unlike {@link #halfToRawIntBits(short)}, this method collapses all + * possible Not-a-Number values to a single canonical Not-a-Number value + * defined by {@link #NaN}.</p> + * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToRawIntBits(short) + * @see #halfToShortBits(short) + * @see #intBitsToHalf(int) + */ + public static int halfToIntBits(@HalfFloat short h) { + return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff; + } + + /** + * <p>Returns a representation of the specified half-precision float value + * according to the bit layout described in {@link Half}.</p> + * + * <p>The argument is considered to be a representation of a half-precision + * float value according to the bit layout described in {@link Half}. The 16 + * most significant bits of the returned value are set to 0.</p> + * + * @param h A half-precision float value + * @return The bits that represent the half-precision float value + * + * @see #halfToIntBits(short) + * @see #intBitsToHalf(int) + */ + public static int halfToRawIntBits(@HalfFloat short h) { + return h & 0xffff; + } + + /** + * <p>Returns the half-precision float value corresponding to a given + * bit representation.</p> + * + * <p>The argument is considered to be a representation of a half-precision + * float value according to the bit layout described in {@link Half}. The 16 + * most significant bits of the argument are ignored.</p> + * + * @param bits An integer + * @return The half-precision float value with the same bit pattern + */ + public static @HalfFloat short intBitsToHalf(int bits) { + return (short) (bits & 0xffff); } /** @@ -509,7 +838,7 @@ public final class Half { * infinity, false otherwise. * * @param h A half-precision float value - * @return true if the value is positive infinity or negative infinity, + * @return True if the value is positive infinity or negative infinity, * false otherwise */ public static boolean isInfinite(@HalfFloat short h) { @@ -521,7 +850,7 @@ public final class Half { * a Not-a-Number, false otherwise. * * @param h A half-precision float value - * @return true if the value is a NaN, false otherwise + * @return True if the value is a NaN, false otherwise */ public static boolean isNaN(@HalfFloat short h) { return (h & FP16_COMBINED) > FP16_EXPONENT_MAX; @@ -535,7 +864,7 @@ public final class Half { * number, this method returns false. * * @param h A half-precision float value - * @return true if the value is normalized, false otherwise + * @return True if the value is normalized, false otherwise */ public static boolean isNormalized(@HalfFloat short h) { return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX; @@ -608,7 +937,7 @@ public final class Half { * @return A half-precision float value */ @SuppressWarnings("StatementWithEmptyBody") - public static @HalfFloat short valueOf(float f) { + public static @HalfFloat short toHalf(float f) { int bits = Float.floatToRawIntBits(f); int s = (bits >>> FP32_SIGN_SHIFT ); int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK; @@ -650,6 +979,57 @@ public final class Half { } /** + * Returns a {@code Half} instance representing the specified + * half-precision float value. + * + * @param h A half-precision float value + * @return a {@code Half} instance representing {@code h} + */ + public static @NonNull Half valueOf(@HalfFloat short h) { + return new Half(h); + } + + /** + * Returns a {@code Half} instance representing the specified float value. + * + * @param f A float value + * @return a {@code Half} instance representing {@code f} + */ + public static @NonNull Half valueOf(float f) { + return new Half(f); + } + + /** + * Returns a {@code Half} instance representing the specified string value. + * Calling this method is equivalent to calling + * <code>toHalf(Float.parseString(h))</code>. See {@link Float#valueOf(String)} + * for more information on the format of the string representation. + * + * @param s The string to be parsed + * @return a {@code Half} instance representing {@code h} + * @throws NumberFormatException if the string does not contain a parsable + * half-precision float value + */ + public static @NonNull Half valueOf(@NonNull String s) { + return new Half(s); + } + + /** + * Returns the half-precision float value represented by the specified string. + * Calling this method is equivalent to calling + * <code>toHalf(Float.parseString(h))</code>. See {@link Float#valueOf(String)} + * for more information on the format of the string representation. + * + * @param s The string to be parsed + * @return A half-precision float value represented by the string + * @throws NumberFormatException if the string does not contain a parsable + * half-precision float value + */ + public static @HalfFloat short parseHalf(@NonNull String s) throws NumberFormatException { + return toHalf(FloatingDecimal.parseFloat(s)); + } + + /** * Returns a string representation of the specified half-precision * float value. Calling this method is equivalent to calling * <code>Float.toString(toFloat(h))</code>. See {@link Float#toString(float)} @@ -658,6 +1038,7 @@ public final class Half { * @param h A half-precision float value * @return A string representation of the specified value */ + @NonNull public static String toString(@HalfFloat short h) { return Float.toString(toFloat(h)); } @@ -688,6 +1069,7 @@ public final class Half { * @param h A half-precision float value * @return A hexadecimal string representation of the specified value */ + @NonNull public static String toHexString(@HalfFloat short h) { StringBuilder o = new StringBuilder(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a88084272d80..2cfdf9024a2a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11730,7 +11730,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, && isOnScrollbar(event.getX(), event.getY())) { awakenScrollBars(); } - if (isHoverable()) { + + // If we consider ourself hoverable, or if we we're already hovered, + // handle changing state in response to ENTER and EXIT events. + if (isHoverable() || isHovered()) { switch (action) { case MotionEvent.ACTION_HOVER_ENTER: setHovered(true); @@ -20260,9 +20263,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the view of the specified id, null if cannot be found * @hide */ - protected View findViewTraversal(@IdRes int id) { + protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { - return this; + return (T) this; } return null; } @@ -20272,9 +20275,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the view of specified tag, null if cannot be found * @hide */ - protected View findViewWithTagTraversal(Object tag) { + protected <T extends View> T findViewWithTagTraversal(Object tag) { if (tag != null && tag.equals(mTag)) { - return this; + return (T) this; } return null; } @@ -20285,9 +20288,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The first view that matches the predicate or null. * @hide */ - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.test(this)) { - return this; + return (T) this; } return null; } @@ -20300,7 +20304,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The view that has the given id in the hierarchy or null */ @Nullable - public final View findViewById(@IdRes int id) { + public final <T extends View> T findViewById(@IdRes int id) { if (id < 0) { return null; } @@ -20313,11 +20317,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param accessibilityId The searched accessibility id. * @return The found view. */ - final View findViewByAccessibilityId(int accessibilityId) { + final <T extends View> T findViewByAccessibilityId(int accessibilityId) { if (accessibilityId < 0) { return null; } - View view = findViewByAccessibilityIdTraversal(accessibilityId); + T view = findViewByAccessibilityIdTraversal(accessibilityId); if (view != null) { return view.includeForAccessibility() ? view : null; } @@ -20336,12 +20340,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param accessibilityId The accessibility id. * @return The found view. - * * @hide */ - public View findViewByAccessibilityIdTraversal(int accessibilityId) { + public <T extends View> T findViewByAccessibilityIdTraversal(int accessibilityId) { if (getAccessibilityViewId() == accessibilityId) { - return this; + return (T) this; } return null; } @@ -20353,7 +20356,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param tag The tag to search for, using "tag.equals(getTag())". * @return The View that has the given tag in the hierarchy or null */ - public final View findViewWithTag(Object tag) { + public final <T extends View> T findViewWithTag(Object tag) { if (tag == null) { return null; } @@ -20368,7 +20371,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The first view that matches the predicate or null. * @hide */ - public final View findViewByPredicate(Predicate<View> predicate) { + public final <T extends View> T findViewByPredicate(Predicate<View> predicate) { return findViewByPredicateTraversal(predicate, null); } @@ -20388,10 +20391,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The first view that matches the predicate or null. * @hide */ - public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) { + public final <T extends View> T findViewByPredicateInsideOut( + View start, Predicate<View> predicate) { View childToSkip = null; for (;;) { - View view = start.findViewByPredicateTraversal(predicate, childToSkip); + T view = start.findViewByPredicateTraversal(predicate, childToSkip); if (view != null || start == this) { return view; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7aa21684b432..ffa79bde968b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4324,9 +4324,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewTraversal(@IdRes int id) { + protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4339,7 +4339,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewById(id); if (v != null) { - return v; + return (T) v; } } } @@ -4351,9 +4351,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewWithTagTraversal(Object tag) { + protected <T extends View> T findViewWithTagTraversal(Object tag) { if (tag != null && tag.equals(mTag)) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4366,7 +4366,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewWithTag(tag); if (v != null) { - return v; + return (T) v; } } } @@ -4378,9 +4378,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.test(this)) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4393,7 +4394,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b79f22fb2d7d..20d960fff661 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4305,11 +4305,11 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateEventInScreenToAppWindow(event); } - // Enter touch mode on down or scroll. + // Enter touch mode on down or scroll, if it is coming from a touch screen device, + // exit otherwise. final int action = event.getAction(); - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL)) { - ensureTouchMode(true); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { + ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); } if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) { diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index 51587a74987d..9a39a17ce223 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -250,7 +250,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button); mDefaultActivityButton.setOnClickListener(mCallbacks); mDefaultActivityButton.setOnLongClickListener(mCallbacks); - mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image); + mDefaultActivityButtonImage = mDefaultActivityButton.findViewById(R.id.image); final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button); expandButton.setOnClickListener(mCallbacks); @@ -282,7 +282,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mExpandActivityOverflowButton = expandButton; mExpandActivityOverflowButtonImage = - (ImageView) expandButton.findViewById(R.id.image); + expandButton.findViewById(R.id.image); mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable); mAdapter = new ActivityChooserViewAdapter(); @@ -760,7 +760,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod convertView = LayoutInflater.from(getContext()).inflate( R.layout.activity_chooser_view_list_item, parent, false); convertView.setId(ITEM_VIEW_TYPE_FOOTER); - TextView titleView = (TextView) convertView.findViewById(R.id.title); + TextView titleView = convertView.findViewById(R.id.title); titleView.setText(mContext.getString( R.string.activity_chooser_view_see_all)); } @@ -772,11 +772,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } PackageManager packageManager = mContext.getPackageManager(); // Set the icon - ImageView iconView = (ImageView) convertView.findViewById(R.id.icon); + ImageView iconView = convertView.findViewById(R.id.icon); ResolveInfo activity = (ResolveInfo) getItem(position); iconView.setImageDrawable(activity.loadIcon(packageManager)); // Set the title. - TextView titleView = (TextView) convertView.findViewById(R.id.title); + TextView titleView = convertView.findViewById(R.id.title); titleView.setText(activity.loadLabel(packageManager)); // Highlight the default. if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) { diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 68e6809a3f68..06d486813b1b 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -451,7 +451,7 @@ public class AppSecurityPermissions { private View getPermissionsView(int which, boolean showRevokeUI) { LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); - LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list); + LinearLayout displayList = permsView.findViewById(R.id.perms_list); View noPermsView = permsView.findViewById(R.id.no_permissions); displayPermissions(mPermGroupsList, displayList, which, showRevokeUI); @@ -517,8 +517,8 @@ public class AppSecurityPermissions { CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) { View permView = inflater.inflate(R.layout.app_permission_item_old, null); - TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); - TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); + TextView permGrpView = permView.findViewById(R.id.permission_group); + TextView permDescView = permView.findViewById(R.id.permission_list); ImageView imgView = (ImageView)permView.findViewById(R.id.perm_icon); imgView.setImageDrawable(icon); diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index bbc50dafa576..81f0d3d0a352 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -388,7 +388,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp text = (TextView) view; } else { // Otherwise, find the TextView field within the layout - text = (TextView) view.findViewById(mFieldId); + text = view.findViewById(mFieldId); if (text == null) { throw new RuntimeException("Failed to find view with ID " diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java index 557d41179921..1b899dbf6d03 100644 --- a/core/java/android/widget/CalendarViewLegacyDelegate.java +++ b/core/java/android/widget/CalendarViewLegacyDelegate.java @@ -316,9 +316,9 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega View content = layoutInflater.inflate(R.layout.calendar_view, null, false); mDelegator.addView(content); - mListView = (ListView) mDelegator.findViewById(R.id.list); - mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names); - mMonthName = (TextView) content.findViewById(R.id.month_name); + mListView = mDelegator.findViewById(R.id.list); + mDayNamesHeader = content.findViewById(R.id.day_names); + mMonthName = content.findViewById(R.id.month_name); setUpHeader(); setUpListView(); diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index f712685ec3e0..907250aa5598 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -115,10 +115,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mDelegator.addView(mContainer); // Set up header views. - final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header); - mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year); + final ViewGroup header = mContainer.findViewById(R.id.date_picker_header); + mHeaderYear = header.findViewById(R.id.date_picker_header_year); mHeaderYear.setOnClickListener(mOnHeaderClickListener); - mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date); + mHeaderMonthDay = header.findViewById(R.id.date_picker_header_date); mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener); // For the sake of backwards compatibility, attempt to extract the text @@ -154,10 +154,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { a.recycle(); // Set up picker container. - mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator); + mAnimator = mContainer.findViewById(R.id.animator); // Set up day picker view. - mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker); + mDayPickerView = mAnimator.findViewById(R.id.date_picker_day_picker); mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek); mDayPickerView.setMinDate(mMinDate.getTimeInMillis()); mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis()); @@ -165,7 +165,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener); // Set up year picker view. - mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker); + mYearPickerView = mAnimator.findViewById(R.id.date_picker_year_picker); mYearPickerView.setRange(mMinDate, mMaxDate); mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR)); mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener); diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java index 8d5bf8f7bce4..63621e123320 100644 --- a/core/java/android/widget/DayPickerPagerAdapter.java +++ b/core/java/android/widget/DayPickerPagerAdapter.java @@ -225,7 +225,7 @@ class DayPickerPagerAdapter extends PagerAdapter { public Object instantiateItem(ViewGroup container, int position) { final View itemView = mInflater.inflate(mLayoutResId, container, false); - final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId); + final SimpleMonthView v = itemView.findViewById(mCalendarViewId); v.setOnDayClickListener(mOnDayClickListener); v.setMonthTextAppearance(mMonthTextAppearance); v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance); diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java index a27e0225b54e..1704ed711e3f 100644 --- a/core/java/android/widget/DayPickerViewPager.java +++ b/core/java/android/widget/DayPickerViewPager.java @@ -137,9 +137,10 @@ class DayPickerViewPager extends ViewPager { } @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.test(this)) { - return this; + return (T) this; } // Always try the selected view first. @@ -148,7 +149,7 @@ class DayPickerViewPager extends ViewPager { if (current != childToSkip && current != null) { final View v = current.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } @@ -160,7 +161,7 @@ class DayPickerViewPager extends ViewPager { final View v = child.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index dd3b0549f175..ade03e1b7fb4 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2183,6 +2183,11 @@ public class Editor { } 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 @@ public class Editor { @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/ListView.java b/core/java/android/widget/ListView.java index 46e998af246b..1c0c4ef7cba7 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3740,20 +3740,21 @@ public class ListView extends AbsListView { * @removed For internal use only. This should have been hidden. */ @Override - protected View findViewTraversal(@IdRes int id) { - View v; - v = super.findViewTraversal(id); + protected <T extends View> T findViewTraversal(@IdRes int id) { + // First look in our children, then in any header and footer views that + // may be scrolled off. + View v = super.findViewTraversal(id); if (v == null) { v = findViewInHeadersOrFooters(mHeaderViewInfos, id); if (v != null) { - return v; + return (T) v; } v = findViewInHeadersOrFooters(mFooterViewInfos, id); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } View findViewInHeadersOrFooters(ArrayList<FixedViewInfo> where, int id) { @@ -3782,21 +3783,22 @@ public class ListView extends AbsListView { * @removed For internal use only. This should have been hidden. */ @Override - protected View findViewWithTagTraversal(Object tag) { - View v; - v = super.findViewWithTagTraversal(tag); + protected <T extends View> T findViewWithTagTraversal(Object tag) { + // First look in our children, then in any header and footer views that + // may be scrolled off. + View v = super.findViewWithTagTraversal(tag); if (v == null) { v = findViewWithTagInHeadersOrFooters(mHeaderViewInfos, tag); if (v != null) { - return v; + return (T) v; } v = findViewWithTagInHeadersOrFooters(mFooterViewInfos, tag); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } View findViewWithTagInHeadersOrFooters(ArrayList<FixedViewInfo> where, Object tag) { @@ -3829,21 +3831,21 @@ public class ListView extends AbsListView { * @hide */ @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { - View v; - v = super.findViewByPredicateTraversal(predicate, childToSkip); + protected <T extends View> T findViewByPredicateTraversal( + Predicate<View> predicate, View childToSkip) { + View v = super.findViewByPredicateTraversal(predicate, childToSkip); if (v == null) { v = findViewByPredicateInHeadersOrFooters(mHeaderViewInfos, predicate, childToSkip); if (v != null) { - return v; + return (T) v; } v = findViewByPredicateInHeadersOrFooters(mFooterViewInfos, predicate, childToSkip); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } /** diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 80086374ebd3..8e04f1cd9f5b 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -257,13 +257,13 @@ public class MediaController extends FrameLayout { .getText(com.android.internal.R.string.lockscreen_transport_play_description); mPauseDescription = res .getText(com.android.internal.R.string.lockscreen_transport_pause_description); - mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause); + mPauseButton = v.findViewById(com.android.internal.R.id.pause); if (mPauseButton != null) { mPauseButton.requestFocus(); mPauseButton.setOnClickListener(mPauseListener); } - mFfwdButton = (ImageButton) v.findViewById(com.android.internal.R.id.ffwd); + mFfwdButton = v.findViewById(com.android.internal.R.id.ffwd); if (mFfwdButton != null) { mFfwdButton.setOnClickListener(mFfwdListener); if (!mFromXml) { @@ -271,7 +271,7 @@ public class MediaController extends FrameLayout { } } - mRewButton = (ImageButton) v.findViewById(com.android.internal.R.id.rew); + mRewButton = v.findViewById(com.android.internal.R.id.rew); if (mRewButton != null) { mRewButton.setOnClickListener(mRewListener); if (!mFromXml) { @@ -280,16 +280,16 @@ public class MediaController extends FrameLayout { } // By default these are hidden. They will be enabled when setPrevNextListeners() is called - mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next); + mNextButton = v.findViewById(com.android.internal.R.id.next); if (mNextButton != null && !mFromXml && !mListenersSet) { mNextButton.setVisibility(View.GONE); } - mPrevButton = (ImageButton) v.findViewById(com.android.internal.R.id.prev); + mPrevButton = v.findViewById(com.android.internal.R.id.prev); if (mPrevButton != null && !mFromXml && !mListenersSet) { mPrevButton.setVisibility(View.GONE); } - mProgress = (ProgressBar) v.findViewById(com.android.internal.R.id.mediacontroller_progress); + mProgress = v.findViewById(com.android.internal.R.id.mediacontroller_progress); if (mProgress != null) { if (mProgress instanceof SeekBar) { SeekBar seeker = (SeekBar) mProgress; @@ -298,8 +298,8 @@ public class MediaController extends FrameLayout { mProgress.setMax(1000); } - mEndTime = (TextView) v.findViewById(com.android.internal.R.id.time); - mCurrentTime = (TextView) v.findViewById(com.android.internal.R.id.time_current); + mEndTime = v.findViewById(com.android.internal.R.id.time); + mCurrentTime = v.findViewById(com.android.internal.R.id.time_current); mFormatBuilder = new StringBuilder(); mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 359d04e71969..5505f2fef919 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1578,7 +1578,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final Context context = root.getContext(); - final ViewGroup target = (ViewGroup) root.findViewById(viewId); + final ViewGroup target = root.findViewById(viewId); if (target == null) return; if (nestedViews != null) { // Inflate nested views and add as children @@ -1757,7 +1757,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { if (isRelative) { @@ -1857,7 +1857,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); } @@ -2045,7 +2045,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = root.findViewById(viewId); if (target == null) return; Drawable[] drawables = isRelative ? target.getCompoundDrawablesRelative() diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 770d9eec792a..679053299666 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -54,6 +54,8 @@ final class SelectionActionModeHelper { 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 @@ final class SelectionActionModeHelper { } } - 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 @@ final class SelectionActionModeHelper { 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 @@ final class SelectionActionModeHelper { if (controller != null) { controller.show(); } + if (result != null) { + mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd); + } } mEditor.setRestartActionModeOnNextRefresh(false); mTextClassificationAsyncTask = null; @@ -135,6 +156,8 @@ final class SelectionActionModeHelper { if (actionMode != null) { actionMode.invalidate(); } + final TextView textView = mEditor.getTextView(); + mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd()); mTextClassificationAsyncTask = null; } @@ -145,6 +168,56 @@ final class SelectionActionModeHelper { } /** + * 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/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java index f833d1b4a9d6..fbb89930a035 100644 --- a/core/java/android/widget/SuggestionsAdapter.java +++ b/core/java/android/widget/SuggestionsAdapter.java @@ -286,7 +286,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene v.setTag(new ChildViewCache(v)); // Set up icon. - final ImageView iconRefine = (ImageView) v.findViewById(R.id.edit_query); + final ImageView iconRefine = v.findViewById(R.id.edit_query); iconRefine.setImageResource(mCommitIconResId); return v; @@ -304,11 +304,11 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene public final ImageView mIconRefine; public ChildViewCache(View v) { - mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1); - mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2); - mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1); - mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2); - mIconRefine = (ImageView) v.findViewById(com.android.internal.R.id.edit_query); + mText1 = v.findViewById(com.android.internal.R.id.text1); + mText2 = v.findViewById(com.android.internal.R.id.text2); + mIcon1 = v.findViewById(com.android.internal.R.id.icon1); + mIcon2 = v.findViewById(com.android.internal.R.id.icon2); + mIconRefine = v.findViewById(com.android.internal.R.id.edit_query); } } diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index 32418cdb8240..7e2cadfc8c45 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -619,7 +619,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); mTabWidget, // tab widget is the parent false); // no inflate params - final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); + final TextView tv = tabIndicator.findViewById(R.id.title); tv.setText(mLabel); if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) { @@ -653,8 +653,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); mTabWidget, // tab widget is the parent false); // no inflate params - final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); - final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon); + final TextView tv = tabIndicator.findViewById(R.id.title); + final ImageView iconView = tabIndicator.findViewById(R.id.icon); // when icon is gone by default, we're in exclusive mode final boolean exclusive = iconView.getVisibility() == View.GONE; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9078e613cf6b..adc6f725f42e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -266,7 +266,7 @@ import java.util.Locale; * @attr ref android.R.styleable#TextView_fontFeatureSettings * @attr ref android.R.styleable#TextView_breakStrategy * @attr ref android.R.styleable#TextView_hyphenationFrequency - * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeTextType * @attr ref android.R.styleable#TextView_autoSizeMinTextSize * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize * @attr ref android.R.styleable#TextView_autoSizeStepGranularity @@ -691,18 +691,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN; - // The TextView does not auto-size text (default). + /** + * The TextView does not auto-size text (default). + */ public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; - // The TextView performs uniform horizontal and vertical text size scaling to fit within the - // container. + + /** + * The TextView scales text size both horizontally and vertically to fit within the + * container. + */ public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; + /** @hide */ @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_UNIFORM}) @Retention(RetentionPolicy.SOURCE) public @interface AutoSizeTextType {} - // Default minimum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMinTextSize}. + // Default minimum size for auto-sizing text in scaled pixels. private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12; - // Default maximum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMaxTextSize}. + // Default maximum size for auto-sizing text in scaled pixels. private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112; // Default value for the step size in pixels. private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1; @@ -1308,7 +1314,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE); break; - case com.android.internal.R.styleable.TextView_autoSizeText: + case com.android.internal.R.styleable.TextView_autoSizeTextType: mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE); break; @@ -1660,7 +1666,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or * {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM} * - * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeTextType * * @see #getAutoSizeTextType() */ @@ -1709,7 +1715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @throws IllegalArgumentException if any of the configuration params are invalid. * - * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeTextType * @attr ref android.R.styleable#TextView_autoSizeMinTextSize * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize * @attr ref android.R.styleable#TextView_autoSizeStepGranularity @@ -1753,7 +1759,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid. * - * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeTextType * @attr ref android.R.styleable#TextView_autoSizePresetSizes * * @see #setAutoSizeTextTypeWithDefaults(int) @@ -1806,7 +1812,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or * {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM} * - * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeTextType * * @see #setAutoSizeTextTypeWithDefaults(int) * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 7ef54a5c69ca..20a55129a19d 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -86,7 +86,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { inflater.inflate(layoutResourceId, mDelegator, true); // hour - mHourSpinner = (NumberPicker) delegator.findViewById(R.id.hour); + mHourSpinner = delegator.findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { updateInputState(); @@ -100,17 +100,17 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input); + mHourSpinnerInput = mHourSpinner.findViewById(R.id.numberpicker_input); mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // divider (only for the new widget style) - mDivider = (TextView) mDelegator.findViewById(R.id.divider); + mDivider = mDelegator.findViewById(R.id.divider); if (mDivider != null) { setDividerText(); } // minute - mMinuteSpinner = (NumberPicker) mDelegator.findViewById(R.id.minute); + mMinuteSpinner = mDelegator.findViewById(R.id.minute); mMinuteSpinner.setMinValue(0); mMinuteSpinner.setMaxValue(59); mMinuteSpinner.setOnLongPressUpdateInterval(100); @@ -138,7 +138,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input); + mMinuteSpinnerInput = mMinuteSpinner.findViewById(R.id.numberpicker_input); mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // Get the localized am/pm strings and use them in the spinner. @@ -173,13 +173,13 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); + mAmPmSpinnerInput = mAmPmSpinner.findViewById(R.id.numberpicker_input); mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); } if (isAmPmAtStart()) { // Move the am/pm view to the beginning - ViewGroup amPmParent = (ViewGroup) delegator.findViewById(R.id.timePickerLayout); + ViewGroup amPmParent = delegator.findViewById(R.id.timePickerLayout); amPmParent.removeView(amPmView); amPmParent.addView(amPmView, 0); // Swap layout margins if needed. They may be not symmetrical (Old Standard Theme diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 789e60b62799..bf0601d2851d 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -299,7 +299,7 @@ public class Toast { if (mNextView == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } - TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message); + TextView tv = mNextView.findViewById(com.android.internal.R.id.message); if (tv == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index 69b799716f02..1a3ca8667e39 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -264,7 +264,7 @@ public class ZoomButtonsController implements View.OnTouchListener { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(com.android.internal.R.layout.zoom_container, container); - mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls); + mControls = container.findViewById(com.android.internal.R.id.zoomControls); mControls.setOnZoomInClickListener(new OnClickListener() { public void onClick(View v) { dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 84c8f7a22962..79301aa2997e 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -23,15 +23,21 @@ import android.app.usage.UsageStatsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.LabeledIntent; +import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -84,6 +90,14 @@ import java.util.List; public class ChooserActivity extends ResolverActivity { private static final String TAG = "ChooserActivity"; + /** + * Boolean extra to change the following behavior: Normally, ChooserActivity finishes itself + * in onStop when launched in a new task. If this extra is set to true, we do not finish + * ourselves when onStop gets called. + */ + public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP + = "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP"; + private static final boolean DEBUG = false; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; @@ -260,6 +274,7 @@ public class ChooserActivity extends ResolverActivity { } mPinnedSharedPrefs = getPinnedSharedPrefs(this); + setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false)); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); @@ -347,6 +362,7 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets)); } mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); + mChooserRowAdapter.updateRowScales(); mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { @@ -833,7 +849,9 @@ public class ChooserActivity extends ResolverActivity { return false; } intent.setComponent(mChooserTarget.getComponentName()); - intent.putExtras(mChooserTarget.getIntentExtras()); + if (mChooserTarget.getIntentExtras() != null) { + intent.putExtras(mChooserTarget.getIntentExtras()); + } // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package @@ -916,6 +934,8 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_SERVICE_TARGETS = 8; private static final int MAX_TARGETS_PER_SERVICE = 4; + private boolean mAreChooserShortcutsRetrieved; + private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); private final List<TargetInfo> mCallerTargets = new ArrayList<>(); private boolean mShowServiceTargets; @@ -1007,6 +1027,20 @@ public class ChooserActivity extends ResolverActivity { if (mServiceTargets != null) { pruneServiceTargets(); } + + if (DEBUG) Log.d(TAG, "Adding pushed chooser targets"); + + if (!mAreChooserShortcutsRetrieved) { + LauncherApps launcherApps = getLauncherApps(); + LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); + query.setIntent(getTargetIntent()); + query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_CHOOSER); + List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, UserHandle.SYSTEM); + if (DEBUG) Log.d(TAG, "Adding " + shortcuts.size() + " chooser shortcuts"); + addShortcuts(shortcuts); + mAreChooserShortcutsRetrieved = true; + } + if (DEBUG) Log.d(TAG, "List built querying services"); queryTargetServices(this); } @@ -1032,6 +1066,7 @@ public class ChooserActivity extends ResolverActivity { public int getServiceTargetCount() { if (!mShowServiceTargets) { + if (DEBUG) Log.d("TAG", "Hiding service targets"); return 0; } return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); @@ -1123,6 +1158,71 @@ public class ChooserActivity extends ResolverActivity { notifyDataSetChanged(); } + // TODO: Pushed targets need to be scored correctly + public void addShortcuts(List<ShortcutInfo> infos) { + for (ShortcutInfo info : infos) { + List<ChooserTarget> newTargets = new ArrayList<>(); + final ComponentName cn = info.getActivity(); + ActivityInfo ai; + ResolveInfo ri = new ResolveInfo(); + if (cn != null) { + try { + ai = getPackageManager().getActivityInfo(cn, 0); + ri.activityInfo = ai; + UserManager userManager = + (UserManager) getSystemService(Context.USER_SERVICE); + ri.iconResourceId = ai.icon; + ri.labelRes = ai.labelRes; + ri.resolvePackageName = ai.packageName; + ri.activityInfo.applicationInfo = new ApplicationInfo( + ri.activityInfo.applicationInfo); + ri.activityInfo.applicationInfo = ai.applicationInfo; + ri.activityInfo.applicationInfo.uid = getUserId(); + } catch (PackageManager.NameNotFoundException ignored) { + if (DEBUG) Log.d(TAG, "Package not found, skipping this shortcut"); + continue; + } + } + + DisplayResolveInfo resolveInfo = new DisplayResolveInfo(getTargetIntent(), + ri, + info.getShortLabel(), + info.getLongLabel(), + getTargetIntent()); + + int bestMatch = 0; + ComponentName bestComponent = null; + for (int i = 0; i < info.getChooserIntentFilters().length; i++) { + int newMatch = info.getChooserIntentFilters()[i] + .match(getContentResolver(), getTargetIntent(), false, TAG); + if (DEBUG) Log.d(TAG, "A match was found with value: " + newMatch); + if (newMatch > bestMatch) { + bestMatch = newMatch; + bestComponent = info.getChooserComponentNames()[i]; + } + } + if (bestMatch == 0) { + Log.e(TAG, "Unexpectedly, no match was found for the provided chooser intent"); + return; + } + + Bundle extrasToAdd = + info.getChooserExtras() == null ? null: new Bundle(info.getChooserExtras()); + if (DEBUG) Log.d(TAG, "Adding service target " + info.getShortLabel()); + newTargets.add(new ChooserTarget( + info.getShortLabel(), + info.getIcon(), + 1, + bestComponent, + extrasToAdd)); + addServiceResults(resolveInfo, newTargets); + } + if (mChooserRowAdapter != null) { + mChooserRowAdapter.updateRowScales(); + } + setShowServiceTargets(true); + } + /** * Set to true to reveal all service targets at once. */ @@ -1237,37 +1337,7 @@ public class ChooserActivity extends ResolverActivity { @Override public void onChanged() { super.onChanged(); - final int rcount = getServiceTargetRowCount(); - if (mServiceTargetScale == null - || mServiceTargetScale.length != rcount) { - RowScale[] old = mServiceTargetScale; - int oldRCount = old != null ? old.length : 0; - mServiceTargetScale = new RowScale[rcount]; - if (old != null && rcount > 0) { - System.arraycopy(old, 0, mServiceTargetScale, 0, - Math.min(old.length, rcount)); - } - - for (int i = rcount; i < oldRCount; i++) { - old[i].cancelAnimation(); - } - - for (int i = oldRCount; i < rcount; i++) { - final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) - .setInterpolator(mInterpolator); - mServiceTargetScale[i] = rs; - } - - // Start the animations in a separate loop. - // The process of starting animations will result in - // binding views to set up initial values, and we must - // have ALL of the new RowScale objects created above before - // we get started. - for (int i = oldRCount; i < rcount; i++) { - mServiceTargetScale[i].startAnimation(); - } - } - + updateRowScales(); notifyDataSetChanged(); } @@ -1284,6 +1354,40 @@ public class ChooserActivity extends ResolverActivity { }); } + void updateRowScales() { + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + if (DEBUG) Log.d(TAG, "Row scales need adjusting to " + rcount + " rows."); + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + } + + // Start the animations in a separate loop. + // The process of starting animations will result in + // binding views to set up initial values, and we must + // have ALL of the new RowScale objects created above before + // we get started. + for (int i = oldRCount; i < rcount; i++) { + mServiceTargetScale[i].startAnimation(); + } + } + } + private float getRowScale(int rowPosition) { final int start = getCallerTargetRowCount(); final int end = start + getServiceTargetRowCount(); @@ -1554,6 +1658,10 @@ public class ChooserActivity extends ResolverActivity { } } + public LauncherApps getLauncherApps() { + return (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE); + } + static class ServiceResultInfo { public final DisplayResolveInfo originalTarget; public final List<ChooserTarget> resultTargets; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 0b1f0aadf55a..9fb9cb6cb664 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -116,6 +116,10 @@ public class ResolverActivity extends Activity { private Runnable mPostListReadyRunnable; private boolean mRegistered; + + /** See {@link #setRetainInOnStop}. */ + private boolean mRetainInOnStop; + private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { mAdapter.handlePackagesChanged(); @@ -502,7 +506,7 @@ public class ResolverActivity extends Activity { } final Intent intent = getIntent(); if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction() - && !mResolvingHome) { + && !mResolvingHome && !mRetainInOnStop) { // This resolver is in the unusual situation where it has been // launched at the top of a new task. We don't let it be added // to the recent tasks shown to the user, and we need to make sure @@ -1029,6 +1033,14 @@ public class ResolverActivity extends Activity { } /** + * If {@code retainInOnStop} is set to true, we will not finish ourselves when onStop gets + * called and we are launched in a new task. + */ + protected void setRetainInOnStop(boolean retainInOnStop) { + mRetainInOnStop = retainInOnStop; + } + + /** * Check a simple match for the component of two ResolveInfos. */ static boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) { 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 000000000000..3ac5a72e087b --- /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/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 43005e6cac54..919cf991d433 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 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView 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 817b186b7428..79b0cd1aced8 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -1195,6 +1195,8 @@ public final class FloatingToolbar { } 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 @@ public final class FloatingToolbar { 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 ae2e0ac88b39..cc1c65ec24bb 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -278,7 +278,8 @@ public class LockPatternView extends View { 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 @@ public class LockPatternView extends View { 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/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java index 7e915374ca4f..06544543adc6 100644 --- a/core/java/com/android/internal/widget/WatchHeaderListView.java +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -91,13 +91,14 @@ public class WatchHeaderListView extends ListView { } @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal( + Predicate<View> predicate, View childToSkip) { View v = super.findViewByPredicateTraversal(predicate, childToSkip); if (v == null && mTopPanel != null && mTopPanel != childToSkip && !mTopPanel.isRootNamespace()) { - return mTopPanel.findViewByPredicate(predicate); + return (T) mTopPanel.findViewByPredicate(predicate); } - return v; + return (T) v; } @Override diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index c090a754ed57..49024b6a0b5f 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -63,27 +63,27 @@ static jlong FontFamily_create(jlong builderPtr) { return 0; } NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - minikin::FontFamily* family = new minikin::FontFamily( - builder->langId, builder->variant, std::move(builder->fonts)); + FontFamilyWrapper* family = new FontFamilyWrapper( + std::make_shared<minikin::FontFamily>( + builder->langId, builder->variant, std::move(builder->fonts))); delete builder; return reinterpret_cast<jlong>(family); } static void FontFamily_abort(jlong builderPtr) { NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - minikin::Font::clearElementsWithLock(&builder->fonts); delete builder; } static void FontFamily_unref(jlong familyPtr) { - minikin::FontFamily* fontFamily = reinterpret_cast<minikin::FontFamily*>(familyPtr); - fontFamily->Unref(); + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); + delete family; } static void addSkTypeface(jlong builderPtr, sk_sp<SkTypeface> face, const void* fontData, size_t fontSize, int ttcIndex, jint givenWeight, jboolean givenItalic) { - minikin::MinikinFont* minikinFont = - new MinikinFontSkia(std::move(face), fontData, fontSize, ttcIndex, + std::shared_ptr<minikin::MinikinFont> minikinFont = + std::make_shared<MinikinFontSkia>(std::move(face), fontData, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); int weight = givenWeight / 100; @@ -96,8 +96,8 @@ static void addSkTypeface(jlong builderPtr, sk_sp<SkTypeface> face, const void* } } - builder->fonts.push_back(minikin::Font(minikinFont, minikin::FontStyle(weight, italic))); - minikinFont->Unref(); + builder->fonts.push_back(minikin::Font( + std::move(minikinFont), minikin::FontStyle(weight, italic))); } static void release_global_ref(const void* /*data*/, void* context) { @@ -208,13 +208,12 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong ALOGE("addFont failed to create font, invalid request"); return false; } - minikin::MinikinFont* minikinFont = - new MinikinFontSkia(std::move(face), fontPtr, fontSize, ttcIndex, + std::shared_ptr<minikin::MinikinFont> minikinFont = + std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex, std::vector<minikin::FontVariation>()); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - builder->fonts.push_back(minikin::Font(minikinFont, + builder->fonts.push_back(minikin::Font(std::move(minikinFont), minikin::FontStyle(weight / 100, isItalic))); - minikinFont->Unref(); return true; } diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h index 8f44b1e82de8..9eaaa4964b66 100644 --- a/core/jni/android/graphics/FontUtils.h +++ b/core/jni/android/graphics/FontUtils.h @@ -18,9 +18,19 @@ #define _ANDROID_GRAPHICS_FONT_UTILS_H_ #include <jni.h> +#include <memory> + +namespace minikin { +class FontFamily; +} // namespace minikin namespace android { +struct FontFamilyWrapper { + FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {} + std::shared_ptr<minikin::FontFamily> family; +}; + // Utility wrapper for java.util.List class ListHelper { public: diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index bdf79d30aa09..18462376cd20 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -300,8 +300,8 @@ namespace PaintGlue { static void getTextPath(JNIEnv* env, Paint* paint, Typeface* typeface, const jchar* text, jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) { - minikin::Layout layout; - MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count); + minikin::Layout layout = MinikinUtils::doLayout( + paint, bidiFlags, typeface, text, 0, count, count); size_t nGlyphs = layout.nGlyphs(); uint16_t* glyphs = new uint16_t[nGlyphs]; SkPoint* pos = new SkPoint[nGlyphs]; @@ -344,8 +344,8 @@ namespace PaintGlue { SkRect r; SkIRect ir; - minikin::Layout layout; - MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, 0, count, count); + minikin::Layout layout = MinikinUtils::doLayout( + &paint, bidiFlags, typeface, text, 0, count, count); minikin::MinikinRect rect; layout.getBounds(&rect); r.fLeft = rect.mLeft; @@ -459,9 +459,8 @@ namespace PaintGlue { nChars++; prevCp = cp; } - minikin::Layout layout; - MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(), - str.size()); + minikin::Layout layout = MinikinUtils::doLayout( + paint, bidiFlags, typeface, str.get(), 0, str.size(), str.size()); size_t nGlyphs = countNonSpaceGlyphs(layout); if (nGlyphs != 1 && nChars > 1) { // multiple-character input, and was not a ligature @@ -480,8 +479,8 @@ namespace PaintGlue { // since ZZ is reserved for unknown or invalid territory. // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16. static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF }; - minikin::Layout zzLayout; - MinikinUtils::doLayout(&zzLayout, paint, bidiFlags, typeface, ZZ_FLAG_STR, 0, 4, 4); + minikin::Layout zzLayout = MinikinUtils::doLayout( + paint, bidiFlags, typeface, ZZ_FLAG_STR, 0, 4, 4); if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) { // The font collection doesn't have a glyph for unknown flag. Just return true. return true; diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index 0a0fce3ecfab..0cdc74fa92d8 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -67,9 +67,7 @@ static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle static void Typeface_unref(JNIEnv* env, jobject obj, jlong faceHandle) { Typeface* face = reinterpret_cast<Typeface*>(faceHandle); - if (face != NULL) { - face->unref(); - } + delete face; } static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) { @@ -79,12 +77,13 @@ static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) { static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray) { ScopedLongArrayRO families(env, familyArray); - std::vector<minikin::FontFamily*> familyVec; + std::vector<std::shared_ptr<minikin::FontFamily>> familyVec; + familyVec.reserve(families.size()); for (size_t i = 0; i < families.size(); i++) { - minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(families[i]); - familyVec.push_back(family); + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]); + familyVec.emplace_back(family->family); } - return reinterpret_cast<jlong>(Typeface::createFromFamilies(familyVec)); + return reinterpret_cast<jlong>(Typeface::createFromFamilies(std::move(familyVec))); } static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) { diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index c05ef26fd5ac..90ed6eb5e60e 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -159,11 +159,12 @@ static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); Paint* paint = reinterpret_cast<Paint*>(nativePaint); Typeface* typeface = reinterpret_cast<Typeface*>(nativeTypeface); - minikin::FontCollection *font; minikin::MinikinPaint minikinPaint; - minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, + Typeface* resolvedTypeface = Typeface::resolveDefault(typeface); + minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint, typeface); - return b->addStyleRun(&minikinPaint, font, style, start, end, isRtl); + return b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start, end, + isRtl); } // Accept width measurements for the run, passed in from Java diff --git a/core/proto/android/os/looper.proto b/core/proto/android/os/looper.proto new file mode 100644 index 000000000000..9fcc7819d431 --- /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 000000000000..604935d80fb8 --- /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 000000000000..9bff13eb6ce4 --- /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 000000000000..c2aa5cb942e9 --- /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 000000000000..326b0eb6c508 --- /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 000000000000..1830dbf5406c --- /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 000000000000..7ba7c174267a --- /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 b3ae891f4325..054fad2f0bca 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3489,7 +3489,7 @@ <receiver android:name="com.android.server.updates.ApnDbInstallReceiver" android:permission="android.permission.UPDATE_CONFIG"> <intent-filter> - <action android:name="android.intent.action.UPDATE_APN_DB" /> + <action android:name="com.android.internal.intent.action.UPDATE_APN_DB" /> <data android:scheme="content" android:host="*" android:mimeType="*/*" /> </intent-filter> </receiver> diff --git a/core/res/res/layout/app_permission_item_money.xml b/core/res/res/layout/app_permission_item_money.xml index 205628566170..764c883e0345 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_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml index 40cce7b04d87..9b90de600397 100644 --- a/core/res/res/layout/autofill_dataset_picker.xml +++ b/core/res/res/layout/autofill_dataset_picker.xml @@ -15,7 +15,7 @@ --> <ListView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/list" + android:id="@+id/autofill_dataset_picker" android:layout_width="wrap_content" android:layout_height="fill_parent" android:divider="?android:attr/listDivider" diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index 8453cd35a905..dad9aad876f9 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -16,6 +16,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/autofill_save" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingStart="16dip" @@ -39,6 +40,13 @@ android:singleLine="true"> </TextView> + <TextView + android:id="@+id/autofill_save_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="invisible"> + </TextView> + <Space android:layout_width="0dp" android:layout_height="0dp" 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 f17b80ee2f39..632a4c14ec1a 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 8031f1934088..be7760398062 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" /> @@ -4721,18 +4721,19 @@ </attr> <!-- Specify the type of auto-size. Note that this feature is not supported by EditText, works only for TextView --> - <attr name="autoSizeText" format="enum"> + <attr name="autoSizeTextType" format="enum"> <!-- No auto-sizing (default). --> <enum name="none" value="0" /> - <!-- Uniform horizontal and vertical scaling. --> + <!-- Uniform horizontal and vertical text size scaling to fit within the + container --> <enum name="uniform" value="1" /> </attr> - <!-- Specify the auto-size step size if <code>autoSizeText</code> is set to - <code>xy</code>. The default is 1px. Overwrites + <!-- Specify the auto-size step size if <code>autoSizeTextType</code> is set to + <code>uniform</code>. The default is 1px. Overwrites <code>autoSizePresetSizes</code> if set. --> <attr name="autoSizeStepGranularity" format="dimension" /> - <!-- Array of dimensions to be used in conjunction with - <code>autoSizeText</code> set to <code>xy</code>. Overwrites + <!-- Resource array of dimensions to be used in conjunction with + <code>autoSizeTextType</code> set to <code>uniform</code>. Overrides <code>autoSizeStepGranularity</code> if set. --> <attr name="autoSizePresetSizes"/> <!-- The minimum text size constraint to be used when auto-sizing text --> @@ -8531,4 +8532,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 b28c6f2d22e3..6015ed5259d8 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 0a24565049ab..e0cc5b51e25b 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 d233e24e475e..35aff80999f5 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 78549b52edd6..01737e73df93 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2765,7 +2765,7 @@ <public name="font" /> <public name="fontWeight" /> <public name="tooltipText" /> - <public name="autoSizeText" /> + <public name="autoSizeTextType" /> <public name="autoSizeStepGranularity" /> <public name="autoSizePresetSizes" /> <public name="autoSizeMinTextSize" /> @@ -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 6abc009402a4..b0c532ca72a9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4510,11 +4510,22 @@ <!-- 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] --> + <!-- Title for the auto-fill save dialog shown when the the contents of the activity can be saved + by an auto-fill service, but the service does not know what the activity represents [CHAR LIMIT=NONE] --> <string name="autofill_save_title">Save to <xliff:g id="label" example="MyPass">%1$s</xliff:g>?</string> + <!-- Title for the auto-fill save dialog shown when the the contents of the activity can be saved + by an auto-fill service, and the service does knows what the activity represents (for example, credit card info) [CHAR LIMIT=NONE] --> + <string name="autofill_save_title_with_type">Save <xliff:g id="type" example="Credit Card">%1$s</xliff:g> to <xliff:g id="label" example="MyPass">%2$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> + <!-- Label for the type of data being saved for auto-fill when it represent user credentials with a password [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_password">password</string> + <!-- Label for the type of data being saved for auto-fill when it represent an address (street, city, etc.) [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_address">address</string> + <!-- Label for the type of data being saved for auto-fill when it represents a credit card [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_credit_card">credit card</string> + </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index faf451bcec97..25873d2f8fd7 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1475,4 +1475,10 @@ please see styles_device_defaults.xml. <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 e7d3ec914767..6995d9a9e739 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" /> @@ -2840,12 +2838,17 @@ <java-symbol type="layout" name="autofill_save"/> <java-symbol type="layout" name="autofill_dataset_picker"/> <java-symbol type="id" name="autofill_save_title" /> + <java-symbol type="id" name="autofill_save_subtitle" /> <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_title_with_type" /> <java-symbol type="string" name="autofill_save_yes" /> <java-symbol type="string" name="autofill_save_no" /> + <java-symbol type="string" name="autofill_save_type_password" /> + <java-symbol type="string" name="autofill_save_type_address" /> + <java-symbol type="string" name="autofill_save_type_credit_card" /> <!-- Accessibility fingerprint gestures --> <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" /> @@ -2853,7 +2856,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"/> @@ -2880,6 +2883,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 e3576781ce62..d100c63d4ec1 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -63,6 +63,7 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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 400fb47ba9e3..008c817dd73c 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -51,6 +51,7 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index da6a294fe8bb..b71826304871 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -393,6 +393,7 @@ public class SettingsBackupTest { 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/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 1080a9fcfe71..3dfecc646169 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -16,6 +16,17 @@ package com.android.internal.app; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherApps; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.drawable.Icon; +import android.os.SystemClock; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; @@ -48,25 +59,31 @@ import static com.android.internal.app.ChooserWrapperActivity.sOverrides; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.isA; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Chooser activity instrumentation tests */ @RunWith(AndroidJUnit4.class) public class ChooserActivityTest { - @Rule - public ActivityTestRule<ChooserWrapperActivity> mActivityRule = - new ActivityTestRule<>(ChooserWrapperActivity.class, false, - false); + private Instrumentation instrumentation; @Before - public void cleanOverrideData() { + public void setUp() { + instrumentation = InstrumentationRegistry.getInstrumentation(); sOverrides.reset(); } + @Rule + public ActivityTestRule<ChooserWrapperActivity> mActivityRule = + new ActivityTestRule<>(ChooserWrapperActivity.class, false, + false); + @Test public void customTitle() throws InterruptedException { Intent sendIntent = createSendImageIntent(); @@ -235,7 +252,6 @@ public class ChooserActivityTest { chosen[0] = targetInfo.getResolveInfo(); return true; }; - // Make a stable copy of the components as the original list may be modified List<ResolvedComponentInfo> stableCopy = createResolvedComponentsForTestWithOtherProfile(2); @@ -324,6 +340,32 @@ public class ChooserActivityTest { assertThat(chosen[0], is(toChoose)); } + public void pushedChooserTarget() { + ResolveInfo[] chosen = new ResolveInfo[1]; + sOverrides.onSafelyStartCallback = targetInfo -> { + chosen[0] = targetInfo.getResolveInfo(); + return true; + }; + + setChooserShortcuts(1); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + Intent sendIntent = createSendImageIntent(); + final ChooserWrapperActivity activity = mActivityRule + .launchActivity(Intent.createChooser(sendIntent, null)); + + waitForIdle(); + + onView(withText("short chooser label 0")) + .perform(click()); + waitForIdle(); + assertThat(chosen[0].resolvePackageName, + is(ResolverDataProvider.createActivityInfo(0).packageName)); + } + private Intent createSendImageIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); @@ -371,4 +413,48 @@ public class ChooserActivityTest { } return packageStats.mChooserCounts.get(action).getOrDefault(annotation, 0); } + + private void setChooserShortcuts(int numShortcuts) { + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + for (int i = 0; i < numShortcuts; i++) { + shortcuts.add(makeShortcut(i)); + } + when(sOverrides.launcherApps.getShortcuts( + Mockito.isA(LauncherApps.ShortcutQuery.class), + Mockito.eq(UserHandle.SYSTEM))) + .thenReturn(shortcuts); + } + + private ShortcutInfo makeShortcut(int i) { + try { + IntentFilter filter = new IntentFilter(Intent.ACTION_SEND, "image/jpeg"); + + ComponentName component = new ComponentName("foo.bar", "foo.bar" + ".MainActivity"); + ShortcutInfo.Builder b = new ShortcutInfo.Builder(instrumentation.getContext(), "" + i) + .setActivity(component) + .setShortLabel("short chooser label " + i) + .setLongLabel("long chooser label" + i) + .setRank(i) + .setIntent(createSendImageIntent()) + .setIcon(Icon.createWithResource(instrumentation.getContext(), + android.R.drawable.ic_menu_add)) + .addChooserIntentFilter( + filter, + component); + + sOverrides.createPackageManager = pm -> { + final PackageManager spied = spy(pm); + try { + doAnswer(invocation -> ResolverDataProvider.createActivityInfo(i)) + .when(spied).getActivityInfo( + Mockito.isA(ComponentName.class), Mockito.anyInt()); + } catch (Exception e) { + // this is ok, just not found + e.printStackTrace(); + } + return spied; + }; + return b.build(); + } catch (Exception e) {return null;} + } }
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index c446f3c79ea8..0dac2602740f 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -18,6 +18,7 @@ package com.android.internal.app; import android.app.usage.UsageStatsManager; import android.content.Context; +import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import java.util.function.Function; @@ -74,6 +75,11 @@ public class ChooserWrapperActivity extends ChooserActivity { return super.getPackageManager(); } + @Override + public LauncherApps getLauncherApps() { + return sOverrides.launcherApps; + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -82,6 +88,7 @@ public class ChooserWrapperActivity extends ChooserActivity { static class OverrideData { @SuppressWarnings("Since15") public Function<PackageManager, PackageManager> createPackageManager; + public LauncherApps launcherApps; public Function<TargetInfo, Boolean> onSafelyStartCallback; public ResolverListController resolverListController; public Boolean isVoiceInteraction; @@ -90,6 +97,7 @@ public class ChooserWrapperActivity extends ChooserActivity { onSafelyStartCallback = null; isVoiceInteraction = null; createPackageManager = null; + launcherApps = mock(LauncherApps.class); resolverListController = mock(ResolverListController.class); } } diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index ff21cac85a23..33d19d4063f5 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -1023,9 +1023,9 @@ public class Color { "The color space must use a color model with at most 3 components"); } - @HalfFloat short r = Half.valueOf(red); - @HalfFloat short g = Half.valueOf(green); - @HalfFloat short b = Half.valueOf(blue); + @HalfFloat short r = Half.toHalf(red); + @HalfFloat short g = Half.toHalf(green); + @HalfFloat short b = Half.toHalf(blue); int a = (int) (Math.max(0.0f, Math.min(alpha, 1.0f)) * 1023.0f + 0.5f); diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index ec00c453c6d4..b1a433cbaaeb 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -805,7 +805,8 @@ public abstract class ColorSpace { */ 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 @@ public abstract class ColorSpace { -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/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index fe82a93cf5b5..a24b9701c9c2 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -16,9 +16,11 @@ package android.graphics.drawable; +import android.annotation.NonNull; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; @@ -31,10 +33,10 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.shapes.Shape; -import android.content.res.Resources.Theme; import android.util.AttributeSet; import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -69,7 +71,7 @@ import java.io.IOException; * @attr ref android.R.styleable#ShapeDrawable_height */ public class ShapeDrawable extends Drawable { - private ShapeState mShapeState; + private @NonNull ShapeState mShapeState; private PorterDuffColorFilter mTintFilter; private boolean mMutated; @@ -77,7 +79,7 @@ public class ShapeDrawable extends Drawable { * ShapeDrawable constructor. */ public ShapeDrawable() { - this(new ShapeState(null), null); + this(new ShapeState(), null); } /** @@ -86,7 +88,7 @@ public class ShapeDrawable extends Drawable { * @param s the Shape that this ShapeDrawable should be */ public ShapeDrawable(Shape s) { - this(new ShapeState(null), null); + this(new ShapeState(), null); mShapeState.mShape = s; } @@ -402,7 +404,7 @@ public class ShapeDrawable extends Drawable { } // Update local properties. - updateLocalState(r); + updateLocalState(); } @Override @@ -426,7 +428,7 @@ public class ShapeDrawable extends Drawable { } // Update local properties. - updateLocalState(t.getResources()); + updateLocalState(); } private void updateStateFromTypedArray(TypedArray a) { @@ -447,10 +449,10 @@ public class ShapeDrawable extends Drawable { dither = a.getBoolean(R.styleable.ShapeDrawable_dither, dither); paint.setDither(dither); - setIntrinsicWidth((int) a.getDimension( - R.styleable.ShapeDrawable_width, state.mIntrinsicWidth)); - setIntrinsicHeight((int) a.getDimension( - R.styleable.ShapeDrawable_height, state.mIntrinsicHeight)); + state.mIntrinsicWidth = (int) a.getDimension( + R.styleable.ShapeDrawable_width, state.mIntrinsicWidth); + state.mIntrinsicHeight = (int) a.getDimension( + R.styleable.ShapeDrawable_height, state.mIntrinsicHeight); final int tintMode = a.getInt(R.styleable.ShapeDrawable_tintMode, -1); if (tintMode != -1) { @@ -494,21 +496,8 @@ public class ShapeDrawable extends Drawable { @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { - if (mShapeState.mPaint != null) { - mShapeState.mPaint = new Paint(mShapeState.mPaint); - } else { - mShapeState.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - } - if (mShapeState.mPadding != null) { - mShapeState.mPadding = new Rect(mShapeState.mPadding); - } else { - mShapeState.mPadding = new Rect(); - } - try { - mShapeState.mShape = mShapeState.mShape.clone(); - } catch (CloneNotSupportedException e) { - return null; - } + mShapeState = new ShapeState(mShapeState); + updateLocalState(); mMutated = true; } return this; @@ -525,12 +514,13 @@ public class ShapeDrawable extends Drawable { /** * Defines the intrinsic properties of this ShapeDrawable's Shape. */ - final static class ShapeState extends ConstantState { - int[] mThemeAttrs; + static final class ShapeState extends ConstantState { + final @NonNull Paint mPaint; + @Config int mChangingConfigurations; - Paint mPaint; + int[] mThemeAttrs; Shape mShape; - ColorStateList mTint = null; + ColorStateList mTint; Mode mTintMode = DEFAULT_TINT_MODE; Rect mPadding; int mIntrinsicWidth; @@ -538,21 +528,43 @@ public class ShapeDrawable extends Drawable { int mAlpha = 255; ShaderFactory mShaderFactory; - ShapeState(ShapeState orig) { - if (orig != null) { - mThemeAttrs = orig.mThemeAttrs; - mPaint = orig.mPaint; - mShape = orig.mShape; - mTint = orig.mTint; - mTintMode = orig.mTintMode; - mPadding = orig.mPadding; - mIntrinsicWidth = orig.mIntrinsicWidth; - mIntrinsicHeight = orig.mIntrinsicHeight; - mAlpha = orig.mAlpha; - mShaderFactory = orig.mShaderFactory; - } else { - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + /** + * Constructs a new ShapeState. + */ + ShapeState() { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + + /** + * Constructs a new ShapeState that contains a deep copy of the + * specified ShapeState. + * + * @param orig the state to create a deep copy of + */ + ShapeState(@NonNull ShapeState orig) { + mChangingConfigurations = orig.mChangingConfigurations; + mPaint = new Paint(orig.mPaint); + mThemeAttrs = orig.mThemeAttrs; + if (mShape != null) { + try { + mShape = orig.mShape.clone(); + } catch (CloneNotSupportedException e) { + // Well, at least we tried. + mShape = orig.mShape; + } } + mTint = orig.mTint; + mTintMode = orig.mTintMode; + if (orig.mPadding != null) { + mPadding = new Rect(orig.mPadding); + } + mIntrinsicWidth = orig.mIntrinsicWidth; + mIntrinsicHeight = orig.mIntrinsicHeight; + mAlpha = orig.mAlpha; + + // We don't have any way to clone a shader factory, so hopefully + // this class doesn't contain any local state. + mShaderFactory = orig.mShaderFactory; } @Override @@ -585,7 +597,7 @@ public class ShapeDrawable extends Drawable { private ShapeDrawable(ShapeState state, Resources res) { mShapeState = state; - updateLocalState(res); + updateLocalState(); } /** @@ -593,7 +605,7 @@ public class ShapeDrawable extends Drawable { * after significant state changes, e.g. from the One True Constructor and * after inflating or applying a theme. */ - private void updateLocalState(Resources res) { + private void updateLocalState() { mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode); } @@ -617,8 +629,4 @@ public class ShapeDrawable extends Drawable { */ public abstract Shader resize(int width, int height); } - - // other subclass could wack the Shader's localmatrix based on the - // resize params (e.g. scaletofit, etc.). This could be used to scale - // a bitmap to fill the bounds without needing any other special casing. } diff --git a/graphics/java/android/graphics/drawable/shapes/ArcShape.java b/graphics/java/android/graphics/drawable/shapes/ArcShape.java index c4b239f20da4..85ba0a9e9f6f 100644 --- a/graphics/java/android/graphics/drawable/shapes/ArcShape.java +++ b/graphics/java/android/graphics/drawable/shapes/ArcShape.java @@ -21,31 +21,47 @@ import android.graphics.Outline; import android.graphics.Paint; /** - * Creates an arc shape. The arc shape starts at a specified - * angle and sweeps clockwise, drawing slices of pie. - * The arc can be drawn to a Canvas with its own draw() method, - * but more graphical control is available if you instead pass - * the ArcShape to a {@link android.graphics.drawable.ShapeDrawable}. + * Creates an arc shape. The arc shape starts at a specified angle and sweeps + * clockwise, drawing slices of pie. + * <p> + * The arc can be drawn to a {@link Canvas} with its own + * {@link #draw(Canvas, Paint)} method, but more graphical control is available + * if you instead pass the ArcShape to a + * {@link android.graphics.drawable.ShapeDrawable}. */ public class ArcShape extends RectShape { - private float mStart; - private float mSweep; - + private final float mStartAngle; + private final float mSweepAngle; + /** - * ArcShape constructor. - * + * ArcShape constructor. + * * @param startAngle the angle (in degrees) where the arc begins - * @param sweepAngle the sweep angle (in degrees). Anything equal to or + * @param sweepAngle the sweep angle (in degrees). Anything equal to or * greater than 360 results in a complete circle/oval. */ public ArcShape(float startAngle, float sweepAngle) { - mStart = startAngle; - mSweep = sweepAngle; + mStartAngle = startAngle; + mSweepAngle = sweepAngle; } - + + /** + * @return the angle (in degrees) where the arc begins + */ + public final float getStartAngle() { + return mStartAngle; + } + + /** + * @return the sweep angle (in degrees) + */ + public final float getSweepAngle() { + return mSweepAngle; + } + @Override public void draw(Canvas canvas, Paint paint) { - canvas.drawArc(rect(), mStart, mSweep, true, paint); + canvas.drawArc(rect(), mStartAngle, mSweepAngle, true, paint); } @Override @@ -53,5 +69,10 @@ public class ArcShape extends RectShape { // Since we don't support concave outlines, arc shape does not attempt // to provide an outline. } + + @Override + public ArcShape clone() throws CloneNotSupportedException { + return (ArcShape) super.clone(); + } } diff --git a/graphics/java/android/graphics/drawable/shapes/OvalShape.java b/graphics/java/android/graphics/drawable/shapes/OvalShape.java index c9473f0d08b6..fb87d28405dc 100644 --- a/graphics/java/android/graphics/drawable/shapes/OvalShape.java +++ b/graphics/java/android/graphics/drawable/shapes/OvalShape.java @@ -22,7 +22,8 @@ import android.graphics.Paint; import android.graphics.RectF; /** - * Defines an oval shape. + * Defines an oval shape. + * <p> * The oval can be drawn to a Canvas with its own draw() method, * but more graphical control is available if you instead pass * the OvalShape to a {@link android.graphics.drawable.ShapeDrawable}. @@ -42,5 +43,10 @@ public class OvalShape extends RectShape { outline.setOval((int) Math.ceil(rect.left), (int) Math.ceil(rect.top), (int) Math.floor(rect.right), (int) Math.floor(rect.bottom)); } + + @Override + public OvalShape clone() throws CloneNotSupportedException { + return (OvalShape) super.clone(); + } } diff --git a/graphics/java/android/graphics/drawable/shapes/PathShape.java b/graphics/java/android/graphics/drawable/shapes/PathShape.java index 30b73479a98c..ce5552b7a1bb 100644 --- a/graphics/java/android/graphics/drawable/shapes/PathShape.java +++ b/graphics/java/android/graphics/drawable/shapes/PathShape.java @@ -16,41 +16,44 @@ package android.graphics.drawable.shapes; +import android.annotation.NonNull; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; /** * Creates geometric paths, utilizing the {@link android.graphics.Path} class. + * <p> * The path can be drawn to a Canvas with its own draw() method, * but more graphical control is available if you instead pass * the PathShape to a {@link android.graphics.drawable.ShapeDrawable}. */ public class PathShape extends Shape { - private Path mPath; - private float mStdWidth; - private float mStdHeight; - - private float mScaleX; // cached from onResize - private float mScaleY; // cached from onResize - + private final float mStdWidth; + private final float mStdHeight; + + private Path mPath; + + private float mScaleX; // cached from onResize + private float mScaleY; // cached from onResize + /** * PathShape constructor. - * - * @param path a Path that defines the geometric paths for this shape - * @param stdWidth the standard width for the shape. Any changes to the - * width with resize() will result in a width scaled based - * on the new width divided by this width. - * @param stdHeight the standard height for the shape. Any changes to the - * height with resize() will result in a height scaled based - * on the new height divided by this height. + * + * @param path a Path that defines the geometric paths for this shape + * @param stdWidth the standard width for the shape. Any changes to the + * width with resize() will result in a width scaled based + * on the new width divided by this width. + * @param stdHeight the standard height for the shape. Any changes to the + * height with resize() will result in a height scaled based + * on the new height divided by this height. */ - public PathShape(Path path, float stdWidth, float stdHeight) { + public PathShape(@NonNull Path path, float stdWidth, float stdHeight) { mPath = path; mStdWidth = stdWidth; mStdHeight = stdHeight; } - + @Override public void draw(Canvas canvas, Paint paint) { canvas.save(); @@ -58,7 +61,7 @@ public class PathShape extends Shape { canvas.drawPath(mPath, paint); canvas.restore(); } - + @Override protected void onResize(float width, float height) { mScaleX = width / mStdWidth; @@ -67,7 +70,7 @@ public class PathShape extends Shape { @Override public PathShape clone() throws CloneNotSupportedException { - PathShape shape = (PathShape) super.clone(); + final PathShape shape = (PathShape) super.clone(); shape.mPath = new Path(mPath); return shape; } diff --git a/graphics/java/android/graphics/drawable/shapes/RectShape.java b/graphics/java/android/graphics/drawable/shapes/RectShape.java index 04cf29334764..e339a212a794 100644 --- a/graphics/java/android/graphics/drawable/shapes/RectShape.java +++ b/graphics/java/android/graphics/drawable/shapes/RectShape.java @@ -23,6 +23,7 @@ import android.graphics.RectF; /** * Defines a rectangle shape. + * <p> * The rectangle can be drawn to a Canvas with its own draw() method, * but more graphical control is available if you instead pass * the RectShape to a {@link android.graphics.drawable.ShapeDrawable}. diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java index e5253b85ac3c..f5cbb24a53ef 100644 --- a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java +++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java @@ -16,6 +16,7 @@ package android.graphics.drawable.shapes; +import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Paint; @@ -24,40 +25,41 @@ import android.graphics.RectF; /** * Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle - * can be included (to make a sort of "O" shape). + * can be included (to make a sort of "O" shape). + * <p> * The rounded rectangle can be drawn to a Canvas with its own draw() method, * but more graphical control is available if you instead pass * the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}. */ public class RoundRectShape extends RectShape { private float[] mOuterRadii; - private RectF mInset; + private RectF mInset; private float[] mInnerRadii; private RectF mInnerRect; - private Path mPath; // this is what we actually draw + private Path mPath; // this is what we actually draw /** * RoundRectShape constructor. + * <p> * Specifies an outer (round)rect and an optional inner (round)rect. * * @param outerRadii An array of 8 radius values, for the outer roundrect. - * The first two floats are for the - * top-left corner (remaining pairs correspond clockwise). - * For no rounded corners on the outer rectangle, - * pass null. - * @param inset A RectF that specifies the distance from the inner - * rect to each side of the outer rect. - * For no inner, pass null. + * The first two floats are for the top-left corner + * (remaining pairs correspond clockwise). For no rounded + * corners on the outer rectangle, pass {@code null}. + * @param inset A RectF that specifies the distance from the inner + * rect to each side of the outer rect. For no inner, pass + * {@code null}. * @param innerRadii An array of 8 radius values, for the inner roundrect. - * The first two floats are for the - * top-left corner (remaining pairs correspond clockwise). - * For no rounded corners on the inner rectangle, - * pass null. - * If inset parameter is null, this parameter is ignored. + * The first two floats are for the top-left corner + * (remaining pairs correspond clockwise). For no rounded + * corners on the inner rectangle, pass {@code null}. If + * inset parameter is {@code null}, this parameter is + * ignored. */ - public RoundRectShape(float[] outerRadii, RectF inset, - float[] innerRadii) { + public RoundRectShape(@Nullable float[] outerRadii, @Nullable RectF inset, + @Nullable float[] innerRadii) { if (outerRadii != null && outerRadii.length < 8) { throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values"); } @@ -97,8 +99,7 @@ public class RoundRectShape extends RectShape { final RectF rect = rect(); outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top), - (int) Math.floor(rect.right), (int) Math.floor(rect.bottom), - radius); + (int) Math.floor(rect.right), (int) Math.floor(rect.bottom), radius); } @Override @@ -128,7 +129,7 @@ public class RoundRectShape extends RectShape { @Override public RoundRectShape clone() throws CloneNotSupportedException { - RoundRectShape shape = (RoundRectShape) super.clone(); + final RoundRectShape shape = (RoundRectShape) super.clone(); shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null; shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null; shape.mInset = new RectF(mInset); diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java index eab86662b18e..30b28f3c2242 100644 --- a/graphics/java/android/graphics/drawable/shapes/Shape.java +++ b/graphics/java/android/graphics/drawable/shapes/Shape.java @@ -23,21 +23,25 @@ import android.graphics.Paint; /** * Defines a generic graphical "shape." - * Any Shape can be drawn to a Canvas with its own draw() method, - * but more graphical control is available if you instead pass - * it to a {@link android.graphics.drawable.ShapeDrawable}. + * <p> + * Any Shape can be drawn to a Canvas with its own draw() method, but more + * graphical control is available if you instead pass it to a + * {@link android.graphics.drawable.ShapeDrawable}. + * <p> + * Custom Shape classes must implement {@link #clone()} and return an instance + * of the custom Shape class. */ public abstract class Shape implements Cloneable { private float mWidth; private float mHeight; - + /** * Returns the width of the Shape. */ public final float getWidth() { return mWidth; } - + /** * Returns the height of the Shape. */ @@ -46,9 +50,10 @@ public abstract class Shape implements Cloneable { } /** - * Draw this shape into the provided Canvas, with the provided Paint. + * Draws this shape into the provided Canvas, with the provided Paint. + * <p> * Before calling this, you must call {@link #resize(float,float)}. - * + * * @param canvas the Canvas within which this shape should be drawn * @param paint the Paint object that defines this shape's characteristics */ @@ -56,8 +61,9 @@ public abstract class Shape implements Cloneable { /** * Resizes the dimensions of this shape. + * <p> * Must be called before {@link #draw(Canvas,Paint)}. - * + * * @param width the width of the shape (in pixels) * @param height the height of the shape (in pixels) */ @@ -74,30 +80,34 @@ public abstract class Shape implements Cloneable { onResize(width, height); } } - + /** * Checks whether the Shape is opaque. - * Default impl returns true. Override if your subclass can be opaque. - * - * @return true if any part of the drawable is <em>not</em> opaque. + * <p> + * Default impl returns {@code true}. Override if your subclass can be + * opaque. + * + * @return true if any part of the drawable is <em>not</em> opaque. */ public boolean hasAlpha() { return true; } - + /** * Callback method called when {@link #resize(float,float)} is executed. - * + * * @param width the new width of the Shape * @param height the new height of the Shape */ protected void onResize(float width, float height) {} /** - * Compute the Outline of the shape and return it in the supplied Outline - * parameter. The default implementation does nothing and {@code outline} is not changed. + * Computes the Outline of the shape and return it in the supplied Outline + * parameter. The default implementation does nothing and {@code outline} + * is not changed. * - * @param outline The Outline to be populated with the result. Should not be null. + * @param outline the Outline to be populated with the result. Must be + * non-{@code null}. */ public void getOutline(@NonNull Outline outline) {} @@ -105,5 +115,4 @@ public abstract class Shape implements Cloneable { public Shape clone() throws CloneNotSupportedException { return (Shape) super.clone(); } - } diff --git a/keystore/java/android/security/IKeyChainAliasCallback.aidl b/keystore/java/android/security/IKeyChainAliasCallback.aidl index 1ea952166009..b9d37533de80 100644 --- a/keystore/java/android/security/IKeyChainAliasCallback.aidl +++ b/keystore/java/android/security/IKeyChainAliasCallback.aidl @@ -20,7 +20,7 @@ package android.security; * * @hide */ -interface IKeyChainAliasCallback { +oneway interface IKeyChainAliasCallback { void alias(String alias); } diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index c365b5d8e604..c64a89de7296 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -153,8 +153,8 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou // minikin may modify the original paint Paint paint(origPaint); - minikin::Layout layout; - MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); + minikin::Layout layout = MinikinUtils::doLayout( + &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); @@ -205,8 +205,8 @@ private: void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { Paint paintCopy(paint); - minikin::Layout layout; - MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count); + minikin::Layout layout = MinikinUtils::doLayout( + &paintCopy, bidiFlags, typeface, text, 0, count, count); hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); // Set align to left for drawing, as we don't want individual diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index cbae0a0dbf4a..ba4e3a4df578 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -91,7 +91,7 @@ const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const { return mAxes; } -minikin::MinikinFont* MinikinFontSkia::createFontWithVariation( +std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( const std::vector<minikin::FontVariation>& variations) const { SkFontMgr::FontParameters params; @@ -110,7 +110,8 @@ minikin::MinikinFont* MinikinFontSkia::createFontWithVariation( sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); sk_sp<SkTypeface> face(fm->createFromStream(stream, params)); - return new MinikinFontSkia(std::move(face), mFontData, mFontSize, ttcIndex, variations); + return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, ttcIndex, + variations); } uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index db59fe520068..6c12485845fd 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -45,7 +45,7 @@ public: size_t GetFontSize() const; int GetFontIndex() const; const std::vector<minikin::FontVariation>& GetAxes() const; - minikin::MinikinFont* createFontWithVariation( + std::shared_ptr<minikin::MinikinFont> createFontWithVariation( const std::vector<minikin::FontVariation>&) const; static uint32_t packPaintFlags(const SkPaint* paint); diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 713e5099da26..d1871ffaf92b 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -27,9 +27,8 @@ namespace android { minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, - minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface) { + const Paint* paint, Typeface* typeface) { const Typeface* resolvedFace = Typeface::resolveDefault(typeface); - *pFont = resolvedFace->fFontCollection; minikin::FontStyle resolved = resolvedFace->fStyle; /* Prepare minikin FontStyle */ @@ -54,23 +53,23 @@ minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* mini return minikinStyle; } -void MinikinUtils::doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags, +minikin::Layout MinikinUtils::doLayout(const Paint* paint, int bidiFlags, Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize) { - minikin::FontCollection *font; minikin::MinikinPaint minikinPaint; - minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); - layout->setFontCollection(font); - layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint); + minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface); + minikin::Layout layout(Typeface::resolveDefault(typeface)->fFontCollection); + layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint); + return layout; } float MinikinUtils::measureText(const Paint* paint, int bidiFlags, Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances) { - minikin::FontCollection *font; minikin::MinikinPaint minikinPaint; - minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); + minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface); + Typeface* resolvedTypeface = Typeface::resolveDefault(typeface); return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, - minikinPaint, font, advances); + minikinPaint, resolvedTypeface->fFontCollection, advances); } bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs) { diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index d6f64d2418d5..0f22adc5d42b 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -35,9 +35,9 @@ namespace android { class MinikinUtils { public: ANDROID_API static minikin::FontStyle prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, - minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface); + const Paint* paint, Typeface* typeface); - ANDROID_API static void doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags, + ANDROID_API static minikin::Layout doLayout(const Paint* paint, int bidiFlags, Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize); @@ -56,11 +56,11 @@ public: ANDROID_API static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { float saveSkewX = paint->getTextSkewX(); bool savefakeBold = paint->isFakeBoldText(); - minikin::MinikinFont* curFont = NULL; + const minikin::MinikinFont* curFont = nullptr; size_t start = 0; size_t nGlyphs = layout.nGlyphs(); for (size_t i = 0; i < nGlyphs; i++) { - minikin::MinikinFont* nextFont = layout.getFont(i); + const minikin::MinikinFont* nextFont = layout.getFont(i); if (i > 0 && nextFont != curFont) { MinikinFontSkia::populateSkPaint(paint, curFont, layout.getFakery(start)); f(start, i); diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 9f9fac61fecc..4b8575adb32b 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -64,7 +64,6 @@ Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; - result->fFontCollection->Ref(); result->fSkiaStyle = style; result->fBaseWeight = resolvedFace->fBaseWeight; resolveStyle(result); @@ -83,7 +82,6 @@ Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, // None of passed axes are supported by this collection. // So we will reuse the same collection with incrementing reference count. result->fFontCollection = resolvedFace->fFontCollection; - result->fFontCollection->Ref(); } result->fSkiaStyle = resolvedFace->fSkiaStyle; result->fBaseWeight = resolvedFace->fBaseWeight; @@ -97,7 +95,6 @@ Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; - result->fFontCollection->Ref(); result->fSkiaStyle = resolvedFace->fSkiaStyle; result->fBaseWeight = weight; resolveStyle(result); @@ -105,18 +102,19 @@ Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { return result; } -Typeface* Typeface::createFromFamilies(const std::vector<minikin::FontFamily*>& families) { +Typeface* Typeface::createFromFamilies( + std::vector<std::shared_ptr<minikin::FontFamily>>&& families) { Typeface* result = new Typeface; - result->fFontCollection = new minikin::FontCollection(families); + result->fFontCollection.reset(new minikin::FontCollection(families)); if (families.empty()) { ALOGW("createFromFamilies creating empty collection"); result->fSkiaStyle = SkTypeface::kNormal; } else { const minikin::FontStyle defaultStyle; - minikin::FontFamily* firstFamily = reinterpret_cast<minikin::FontFamily*>(families[0]); - minikin::MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font; - if (mf != NULL) { - SkTypeface* skTypeface = reinterpret_cast<MinikinFontSkia*>(mf)->GetSkTypeface(); + const std::shared_ptr<minikin::FontFamily>& firstFamily = families[0]; + const minikin::MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font; + if (mf != nullptr) { + SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface(); // TODO: probably better to query more precise style from family, will be important // when we open up API to access 100..900 weights result->fSkiaStyle = skTypeface->style(); @@ -129,11 +127,6 @@ Typeface* Typeface::createFromFamilies(const std::vector<minikin::FontFamily*>& return result; } -void Typeface::unref() { - fFontCollection->Unref(); - delete this; -} - void Typeface::setDefault(Typeface* face) { gDefaultTypeface = face; } @@ -150,15 +143,12 @@ void Typeface::setRobotoTypefaceForTest() { sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release()); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont); - minikin::MinikinFont* font = new MinikinFontSkia(std::move(typeface), data, st.st_size, 0, - std::vector<minikin::FontVariation>()); - minikin::FontFamily* family = new minikin::FontFamily( - std::vector<minikin::Font>({ minikin::Font(font, minikin::FontStyle()) })); - font->Unref(); - - std::vector<minikin::FontFamily*> typefaces = { family }; - minikin::FontCollection *collection = new minikin::FontCollection(typefaces); - family->Unref(); + std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( + std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>()); + std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( + std::vector<minikin::Font>({ minikin::Font(std::move(font), minikin::FontStyle()) })); + std::shared_ptr<minikin::FontCollection> collection = + std::make_shared<minikin::FontCollection>(std::move(family)); Typeface* hwTypeface = new Typeface(); hwTypeface->fFontCollection = collection; diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index 4392ebc36bad..19a4f6c5acd5 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -23,11 +23,12 @@ #include <cutils/compiler.h> #include <minikin/FontCollection.h> #include <vector> +#include <memory> namespace android { struct ANDROID_API Typeface { - minikin::FontCollection *fFontCollection; + std::shared_ptr<minikin::FontCollection> fFontCollection; // style used for constructing and querying Typeface objects SkTypeface::Style fSkiaStyle; @@ -37,8 +38,6 @@ struct ANDROID_API Typeface { // resolved style actually used for rendering minikin::FontStyle fStyle; - void unref(); - static Typeface* resolveDefault(Typeface* src); static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style); @@ -48,7 +47,8 @@ struct ANDROID_API Typeface { static Typeface* createWeightAlias(Typeface* src, int baseweight); - static Typeface* createFromFamilies(const std::vector<minikin::FontFamily*>& families); + static Typeface* createFromFamilies( + std::vector<std::shared_ptr<minikin::FontFamily>>&& families); static void setDefault(Typeface* face); diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index fc31f326354f..8f341a8fa6ee 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -113,5 +113,6 @@ interface ILocationManager // 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 ddd8a657f75b..7f5d3a084cb4 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -79,6 +79,7 @@ import com.android.internal.annotations.GuardedBy; */ public class AudioTrack extends PlayerBase implements AudioRouting + , VolumeAutomation { //--------------------------------------------------------- // Constants @@ -1753,6 +1754,12 @@ public class AudioTrack extends PlayerBase 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/MediaFormat.java b/media/java/android/media/MediaFormat.java index 4791cf08dee2..e77c00b17365 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -53,6 +53,7 @@ import java.util.Map; * time-interval between key frames. * Float support added in {@link android.os.Build.VERSION_CODES#N_MR1}</td></tr> * <tr><td>{@link #KEY_INTRA_REFRESH_PERIOD}</td><td>Integer</td><td><b>encoder-only</b>, optional</td></tr> + * <tr><td>{@link #KEY_LATENCY}</td><td>Integer</td><td><b>encoder-only</b>, optional</td></tr> * <tr><td>{@link #KEY_MAX_WIDTH}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution width</td></tr> * <tr><td>{@link #KEY_MAX_HEIGHT}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution height</td></tr> * <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>encoder in surface-mode @@ -577,6 +578,18 @@ public final class MediaFormat { public static final String KEY_LEVEL = "level"; /** + * An optional key describing the desired encoder latency in frames. This is an optional + * parameter that applies only to video encoders. If encoder supports it, it should ouput + * at least one output frame after being queued the specified number of frames. This key + * is ignored if the video encoder does not support the latency feature. Use the output + * format to verify that this feature was enabled and the actual value used by the encoder. + * <p> + * If the key is not specified, the default latency will be implenmentation specific. + * The associated value is an integer. + */ + public static final String KEY_LATENCY = "latency"; + + /** * A key describing the desired clockwise rotation on an output surface. * This key is only used when the codec is configured using an output surface. * The associated value is an integer, representing degrees. Supported values diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index aacce917d910..832b2974f53c 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -68,44 +68,46 @@ import java.util.Map; <h4>Metadata Track</h4> <p> - Metadata is usefule in carrying extra information that correlated with video or audio to - facilate offline processing, e.g. gyro signals from the sensor. Meatadata track is only - supported in MP4 format. When adding a metadata track, track's mime format must start with - prefix "application/", e.g. "applicaton/gyro". Metadata's format/layout will be defined by - the application. The generated MP4 file uses TextMetaDataSampleEntry defined in section 12.3.3.2 - of the ISOBMFF to signal the metadata's mime format. When using {@link android.media.MediaExtractor} - to extract the file with metadata track, the mime format of the metadata will be extracted into - {@link android.media.MediaFormat}. + Per-frame metadata is useful in carrying extra information that correlated with video or audio to + facilitate offline processing, e.g. gyro signals from the sensor could help video stabilization when + doing offline processing. Metaadata track is only supported in MP4 container. When adding a new + metadata track, track's mime format must start with prefix "application/", e.g. "applicaton/gyro". + Metadata's format/layout will be defined by the application. Writing metadata is nearly the same as + writing video/audio data except that the data will not be from mediacodec. Application just needs + to pass the bytebuffer that contains the metadata and also the associated timestamp to the + {@link #writeSampleData} api. The timestamp must be in the same time base as video and audio. The + generated MP4 file uses TextMetaDataSampleEntry defined in section 12.3.3.2 of the ISOBMFF to signal + the metadata's mime format. When using{@link android.media.MediaExtractor} to extract the file with + metadata track, the mime format of the metadata will be extracted into {@link android.media.MediaFormat}. <pre class=prettyprint> MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); - // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() - // or MediaExtractor.getTrackFormat(). + // SetUp Video/Audio Tracks. MediaFormat audioFormat = new MediaFormat(...); MediaFormat videoFormat = new MediaFormat(...); + int audioTrackIndex = muxer.addTrack(audioFormat); + int videoTrackIndex = muxer.addTrack(videoFormat); // Setup Metadata Track MediaFormat metadataFormat = new MediaFormat(...); metadataFormat.setString(KEY_MIME, "application/gyro"); - - int audioTrackIndex = muxer.addTrack(audioFormat); - int videoTrackIndex = muxer.addTrack(videoFormat); int metadataTrackIndex = muxer.addTrack(metadataFormat); - ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); - boolean finished = false; - BufferInfo bufferInfo = new BufferInfo(); muxer.start(); - while(!finished) { - // getInputBuffer() will fill the inputBuffer with one frame of encoded - // sample from either MediaCodec or MediaExtractor, set isAudioSample to - // true when the sample is audio data, set up all the fields of bufferInfo, - // and return true if there are no more samples. - finished = getInputBuffer(inputBuffer, sampleType, bufferInfo); - if (!finished) { - int currentTrackIndex = getTrackIndex(sampleType); - muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); - } + while(..) { + // Allocate bytebuffer and write gyro data(x,y,z) into it. + ByteBuffer metaData = ByteBuffer.allocate(bufferSize); + metaData.putFloat(x); + metaData.putFloat(y); + metaData.putFloat(z); + BufferInfo metaInfo = new BufferInfo(); + // Associate this metadata with the video frame by setting + // the same timestamp as the video frame. + metaInfo.presentationTimeUs = currentVideoTrackTimeUs; + metaInfo.offset = 0; + metaInfo.flags = 0; + metaInfo.size = bufferSize; + muxer.writeSampleData(metadataTrackIndex, metaData, metaInfo); }; muxer.stop(); muxer.release(); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 5008a5f8c5c2..1ebbe855a43f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -579,6 +579,7 @@ import java.util.Vector; */ public class MediaPlayer extends PlayerBase implements SubtitleController.Listener + , VolumeAutomation { /** Constant to retrieve only the new metadata since the last @@ -1373,6 +1374,12 @@ public class MediaPlayer extends PlayerBase 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/VolumeAutomation.java b/media/java/android/media/VolumeAutomation.java new file mode 100644 index 000000000000..dff8801f95f2 --- /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 4a2c4d88c50e..cb27d1089aae 100644 --- a/media/java/android/media/VolumeShaper.java +++ b/media/java/android/media/VolumeShaper.java @@ -23,78 +23,25 @@ import android.os.Parcelable; 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { } /** - * 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 @@ public final class VolumeShaper { 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 @@ public final class VolumeShaper { @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 @@ public final class VolumeShaper { */ 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 @@ public final class VolumeShaper { } /** - * 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 @@ public final class VolumeShaper { /** * 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 final class VolumeShaper { 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 final class VolumeShaper { 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { .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 @@ public final class VolumeShaper { }; /** + * @hide * Constructs a volume shaper from an id. * * This is an opaque handle for controlling a {@code VolumeShaper} that has @@ -522,7 +463,7 @@ public final class VolumeShaper { * @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 @@ public final class VolumeShaper { } /** + * @hide * Returns the {@code VolumeShaper} type. */ public @Type int getType() { @@ -579,6 +521,7 @@ public final class VolumeShaper { } /** + * @hide * Returns the option flags */ public @OptionFlag int getOptionFlags() { @@ -590,7 +533,7 @@ public final class VolumeShaper { } /** - * 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 @@ public final class VolumeShaper { 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 @@ public final class VolumeShaper { /** * @hide - * TODO make SystemApi - * * Set the id for system defined shapers. * @param id * @return @@ -757,7 +699,11 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { } /** + * @hide * Sets the optional flags * * If omitted, flags are 0. If {@link #OPTION_FLAG_VOLUME_IN_DBFS} has @@ -783,7 +730,7 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 @@ public final class VolumeShaper { * 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 final class VolumeShaper { 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { */ 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 @@ public final class VolumeShaper { } /** - * 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { /** * 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 @@ public final class VolumeShaper { /** * Reverses direction. - * @return the same Builder instance. + * @return the same {@code Builder} instance. */ public @NonNull Builder reverse() { mFlags ^= FLAG_REVERSE; @@ -1175,11 +1138,22 @@ public final class VolumeShaper { } /** + * 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 6d8296af0c4e..1b53d72d6e55 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -25,6 +25,7 @@ import android.content.ContentResolver; 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 @@ public final class TvContract { 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/native/android/Android.mk b/native/android/Android.mk index 1c1ff82f0192..57f996cf17ec 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES:= \ net.c \ obb.cpp \ sensor.cpp \ + sharedmem.cpp \ storage_manager.cpp \ trace.cpp \ diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index f9e8fda81c09..e2623d4fd9ee 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -186,6 +186,9 @@ LIBANDROID { ASensor_getType; ASensor_getVendor; ASensor_isWakeUpSensor; # introduced=21 + ASharedMemory_create; # introduced=26 + ASharedMemory_getSize; # introduced=26 + ASharedMemory_setProt; # introduced=26 AStorageManager_delete; AStorageManager_getMountedObbPath; AStorageManager_isObbMounted; diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp new file mode 100644 index 000000000000..9d029dfad41a --- /dev/null +++ b/native/android/sharedmem.cpp @@ -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. + */ + +#include <android/sharedmem.h> +#include <cutils/ashmem.h> +#include <utils/Errors.h> + +int ASharedMemory_create(const char *name, size_t size) { + if (size == 0) { + return android::BAD_VALUE; + } + return ashmem_create_region(name, size); +} + +size_t ASharedMemory_getSize(int fd) { + return ashmem_valid(fd) ? ashmem_get_size_region(fd) : 0; +} + +int ASharedMemory_setProt(int fd, int prot) { + return ashmem_set_prot_region(fd, prot); +} diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 22a5b7ffd520..3cc9f65ecd85 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.util.Pair; 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.FileDescriptor; 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 @@ public class ExternalStorageProvider extends DocumentsProvider { 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 @@ public class ExternalStorageProvider extends DocumentsProvider { 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 @@ public class ExternalStorageProvider extends DocumentsProvider { 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 @@ public class ExternalStorageProvider extends DocumentsProvider { 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 @@ public class ExternalStorageProvider extends DocumentsProvider { } @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 @@ public class ExternalStorageProvider extends DocumentsProvider { ? 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 @@ public class ExternalStorageProvider extends DocumentsProvider { } @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 @@ public class ExternalStorageProvider extends DocumentsProvider { } @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 @@ public class ExternalStorageProvider extends DocumentsProvider { } 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 d207c35732c8..9ff22c79b358 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/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 961d0e560c50..f77d466ef87e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -537,6 +537,11 @@ <string name="wait_for_debugger_summary">Debugged application waits for debugger to attach before executing</string> + <!-- UI debug setting: title for Telephonymonitor switch [CHAR LIMIT=50] --> + <string name="telephony_monitor_switch">Telephony Monitor</string> + <!-- UI debug setting: summary for switch of Telephonymonitor [CHAR LIMIT=500] --> + <string name="telephony_monitor_switch_summary">TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug</string> + <!-- Preference category for input debugging development settings. [CHAR LIMIT=25] --> <string name="debug_input_category">Input</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 7a4514a0af5e..7e7b391ffd1a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -184,10 +184,7 @@ public class Utils { @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/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java index fd2e7cad5c86..6764a6bb7f47 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.Path; 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 @@ public class BatteryMeterDrawableBase extends Drawable { 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 2dcbf90c74b7..6fe581eab86d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -89,15 +89,15 @@ public class AccessPoint implements Comparable<AccessPoint> { new ConcurrentHashMap<String, ScanResult>(32); private static final long MAX_SCAN_RESULT_AGE_MS = 15000; - private static final String KEY_NETWORKINFO = "key_networkinfo"; - private static final String KEY_WIFIINFO = "key_wifiinfo"; - private static final String KEY_SCANRESULT = "key_scanresult"; - private static final String KEY_SSID = "key_ssid"; - private static final String KEY_SECURITY = "key_security"; - private static final String KEY_PSKTYPE = "key_psktype"; - private static final String KEY_SCANRESULTCACHE = "key_scanresultcache"; - private static final String KEY_CONFIG = "key_config"; - private static final AtomicInteger sLastId = new AtomicInteger(0); + static final String KEY_NETWORKINFO = "key_networkinfo"; + static final String KEY_WIFIINFO = "key_wifiinfo"; + static final String KEY_SCANRESULT = "key_scanresult"; + static final String KEY_SSID = "key_ssid"; + static final String KEY_SECURITY = "key_security"; + static final String KEY_PSKTYPE = "key_psktype"; + static final String KEY_SCANRESULTCACHE = "key_scanresultcache"; + static final String KEY_CONFIG = "key_config"; + static final AtomicInteger sLastId = new AtomicInteger(0); /** * These values are matched in string arrays -- changes must be kept in sync @@ -114,6 +114,8 @@ public class AccessPoint implements Comparable<AccessPoint> { public static final int SIGNAL_LEVELS = 4; + static final int UNREACHABLE_RSSI = Integer.MAX_VALUE; + private final Context mContext; private String ssid; @@ -125,7 +127,7 @@ public class AccessPoint implements Comparable<AccessPoint> { private WifiConfiguration mConfig; - private int mRssi = Integer.MAX_VALUE; + private int mRssi = UNREACHABLE_RSSI; private long mSeen = 0; private WifiInfo mInfo; @@ -214,6 +216,21 @@ public class AccessPoint implements Comparable<AccessPoint> { this.mRankingScore = that.mRankingScore; } + /** + * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than, + * equal to, or greater than the other AccessPoint. + * + * Sort order rules for AccessPoints: + * 1. Active before inactive + * 2. Reachable before unreachable + * 3. Saved before unsaved + * 4. (Internal only) Network ranking score + * 5. Stronger signal before weaker signal + * 6. SSID alphabetically + * + * Note that AccessPoints with a signal are usually also Reachable, + * and will thus appear before unreachable saved AccessPoints. + */ @Override public int compareTo(@NonNull AccessPoint other) { // Active one goes first. @@ -221,18 +238,16 @@ public class AccessPoint implements Comparable<AccessPoint> { if (!isActive() && other.isActive()) return 1; // Reachable one goes before unreachable one. - if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1; - if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1; + if (isReachable() && !other.isReachable()) return -1; + if (!isReachable() && other.isReachable()) return 1; // Configured (saved) one goes before unconfigured one. - if (networkId != WifiConfiguration.INVALID_NETWORK_ID - && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1; - if (networkId == WifiConfiguration.INVALID_NETWORK_ID - && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1; + if (isSaved() && !other.isSaved()) return -1; + if (!isSaved() && other.isSaved()) return 1; // Higher scores go before lower scores - if (mRankingScore != other.mRankingScore) { - return (mRankingScore > other.mRankingScore) ? -1 : 1; + if (getRankingScore() != other.getRankingScore()) { + return (getRankingScore() > other.getRankingScore()) ? -1 : 1; } // Sort by signal strength, bucketed by level @@ -242,7 +257,7 @@ public class AccessPoint implements Comparable<AccessPoint> { return difference; } // Sort by ssid. - return ssid.compareToIgnoreCase(other.ssid); + return getSsidStr().compareToIgnoreCase(other.getSsidStr()); } @Override @@ -265,6 +280,9 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 +298,7 @@ public class AccessPoint implements Comparable<AccessPoint> { 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); @@ -351,7 +370,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } public int getLevel() { - if (mRssi == Integer.MAX_VALUE) { + if (!isReachable()) { return -1; } return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS); @@ -527,7 +546,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); - } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range + } else if (!isReachable()) { // Wifi out of range summary.append(mContext.getString(R.string.wifi_not_in_range)); } else { // In range, not disabled. if (config != null) { // Is saved network @@ -870,6 +889,11 @@ public class AccessPoint implements Comparable<AccessPoint> { return mBadge; } + /** Return true if the current RSSI is reachable, and false otherwise. */ + boolean isReachable() { + return mRssi != UNREACHABLE_RSSI; + } + public static String getSummary(Context context, String ssid, DetailedState state, boolean isEphemeral, String passpointProvider) { if (state == DetailedState.CONNECTED && ssid == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 11bcdcad40cf..8421c2c9dee3 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 @@ public class WifiTracker { if (mWifiManager.isWifiEnabled()) { mScanner.resume(); } - mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); } /** @@ -715,9 +714,9 @@ public class WifiTracker { 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/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 6481f4d6531d..ec0190cfa538 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -15,9 +15,8 @@ */ package com.android.settingslib.wifi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.net.ConnectivityManager; @@ -39,6 +38,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -59,12 +59,12 @@ public class AccessPointTest { final AccessPoint ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle); final CharSequence ssid = ap.getSsid(); - assertTrue(ssid instanceof SpannableString); + assertThat(ssid instanceof SpannableString).isTrue(); TtsSpan[] spans = ((SpannableString) ssid).getSpans(0, TEST_SSID.length(), TtsSpan.class); - assertEquals(1, spans.length); - assertEquals(TtsSpan.TYPE_TELEPHONE, spans[0].getType()); + assertThat(spans.length).isEqualTo(1); + assertThat(spans[0].getType()).isEqualTo(TtsSpan.TYPE_TELEPHONE); } @Test @@ -80,11 +80,11 @@ public class AccessPointTest { originalAccessPoint.update(configuration, wifiInfo, networkInfo); AccessPoint copy = new AccessPoint(mContext, originalAccessPoint); - assertEquals(originalAccessPoint.getSsid().toString(), copy.getSsid().toString()); - assertEquals(originalAccessPoint.getBssid(), copy.getBssid()); - assertSame(originalAccessPoint.getConfig(), copy.getConfig()); - assertEquals(originalAccessPoint.getSecurity(), copy.getSecurity()); - assertTrue(originalAccessPoint.compareTo(copy) == 0); + assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString()); + assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid()); + assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig()); + assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity()); + assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue(); } @Test @@ -101,11 +101,93 @@ public class AccessPointTest { bundle.putParcelableArrayList("key_scanresultcache", scanResults); AccessPoint original = new AccessPoint(mContext, bundle); - assertEquals(4, original.getRssi()); + assertThat(original.getRssi()).isEqualTo(4); AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration()); - assertEquals(Integer.MIN_VALUE, copy.getRssi()); + assertThat(copy.getRssi()).isEqualTo(Integer.MIN_VALUE); copy.copyFrom(original); - assertEquals(original.getRssi(), copy.getRssi()); + assertThat(original.getRssi()).isEqualTo(copy.getRssi()); + } + + @Test + public void testCompareTo_GivesActiveBeforeInactive() { + AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); + AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build(); + + assertSortingWorks(activeAp, inactiveAp); + } + + @Test + public void testCompareTo_GivesReachableBeforeUnreachable() { + AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build(); + AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build(); + + assertSortingWorks(nearAp, farAp); + } + + @Test + public void testCompareTo_GivesSavedBeforeUnsaved() { + AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build(); + AccessPoint notSavedAp = new TestAccessPointBuilder(mContext).setSaved(false).build(); + + assertSortingWorks(savedAp, notSavedAp); + } + + //TODO: add tests for mRankingScore sort order if ranking is exposed + + @Test + public void testCompareTo_GivesHighLevelBeforeLowLevel() { + final int highLevel = AccessPoint.SIGNAL_LEVELS - 1; + final int lowLevel = 1; + assertThat(highLevel).isGreaterThan(lowLevel); + + AccessPoint strongAp = new TestAccessPointBuilder(mContext).setLevel(highLevel).build(); + AccessPoint weakAp = new TestAccessPointBuilder(mContext).setLevel(lowLevel).build(); + + assertSortingWorks(strongAp, weakAp); + } + + @Test + public void testCompareTo_GivesSsidAlphabetically() { + + final String firstName = "AAAAAA"; + final String secondName = "zzzzzz"; + + AccessPoint firstAp = new TestAccessPointBuilder(mContext).setSsid(firstName).build(); + AccessPoint secondAp = new TestAccessPointBuilder(mContext).setSsid(secondName).build(); + + assertThat(firstAp.getSsidStr().compareToIgnoreCase(secondAp.getSsidStr()) < 0).isTrue(); + assertSortingWorks(firstAp, secondAp); + } + + @Test + public void testCompareTo_AllSortingRulesCombined() { + + AccessPoint active = new TestAccessPointBuilder(mContext).setActive(true).build(); + AccessPoint reachableAndMinLevel = new TestAccessPointBuilder(mContext) + .setReachable(true).build(); + AccessPoint saved = new TestAccessPointBuilder(mContext).setSaved(true).build(); + AccessPoint highLevelAndReachable = new TestAccessPointBuilder(mContext) + .setLevel(AccessPoint.SIGNAL_LEVELS - 1).build(); + AccessPoint firstName = new TestAccessPointBuilder(mContext).setSsid("a").build(); + AccessPoint lastname = new TestAccessPointBuilder(mContext).setSsid("z").build(); + + ArrayList<AccessPoint> points = new ArrayList<AccessPoint>(); + points.add(lastname); + points.add(firstName); + points.add(highLevelAndReachable); + points.add(saved); + points.add(reachableAndMinLevel); + points.add(active); + + Collections.sort(points); + assertThat(points.indexOf(active)).isLessThan(points.indexOf(reachableAndMinLevel)); + assertThat(points.indexOf(reachableAndMinLevel)).isLessThan(points.indexOf(saved)); + // note: the saved AP will not appear before highLevelAndReachable, + // because all APs with a signal level are reachable, + // and isReachable() takes higher sorting precedence than isSaved(). + assertThat(points.indexOf(saved)).isLessThan(points.indexOf(firstName)); + assertThat(points.indexOf(highLevelAndReachable)).isLessThan(points.indexOf(firstName)); + assertThat(points.indexOf(firstName)).isLessThan(points.indexOf(lastname)); } private WifiConfiguration createWifiConfiguration() { @@ -115,4 +197,85 @@ public class AccessPointTest { configuration.networkId = 123; return configuration; } + + /** + * Assert that the first AccessPoint appears after the second AccessPoint + * once sorting has been completed. + */ + private void assertSortingWorks(AccessPoint first, AccessPoint second) { + + ArrayList<AccessPoint> points = new ArrayList<AccessPoint>(); + + // add in reverse order so we can tell that sorting actually changed something + points.add(second); + points.add(first); + Collections.sort(points); + assertWithMessage( + String.format("After sorting: second AccessPoint should have higher array index " + + "than the first, but found indicies second '%s' and first '%s'.", + points.indexOf(second), points.indexOf(first))) + .that(points.indexOf(second)).isGreaterThan(points.indexOf(first)); + } + + @Test + public void testBuilder_setActive() { + AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); + assertThat(activeAp.isActive()).isTrue(); + + AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build(); + assertThat(inactiveAp.isActive()).isFalse(); + } + + @Test + public void testBuilder_setReachable() { + AccessPoint nearAp = new TestAccessPointBuilder(mContext).setReachable(true).build(); + assertThat(nearAp.isReachable()).isTrue(); + + AccessPoint farAp = new TestAccessPointBuilder(mContext).setReachable(false).build(); + assertThat(farAp.isReachable()).isFalse(); + } + + @Test + public void testBuilder_setSaved() { + AccessPoint savedAp = new TestAccessPointBuilder(mContext).setSaved(true).build(); + assertThat(savedAp.isSaved()).isTrue(); + + AccessPoint newAp = new TestAccessPointBuilder(mContext).setSaved(false).build(); + assertThat(newAp.isSaved()).isFalse(); + } + + @Test + public void testBuilder_setLevel() { + AccessPoint testAp; + + for (int i = 0; i < AccessPoint.SIGNAL_LEVELS; i++) { + testAp = new TestAccessPointBuilder(mContext).setLevel(i).build(); + assertThat(testAp.getLevel()).isEqualTo(i); + } + + // numbers larger than the max level should be set to max + testAp = new TestAccessPointBuilder(mContext).setLevel(AccessPoint.SIGNAL_LEVELS).build(); + assertThat(testAp.getLevel()).isEqualTo(AccessPoint.SIGNAL_LEVELS - 1); + + // numbers less than 0 should give level 0 + testAp = new TestAccessPointBuilder(mContext).setLevel(-100).build(); + assertThat(testAp.getLevel()).isEqualTo(0); + } + + @Test + public void testBuilder_settingReachableAfterLevelDoesNotAffectLevel() { + int level = 1; + assertThat(level).isLessThan(AccessPoint.SIGNAL_LEVELS - 1); + + AccessPoint testAp = + new TestAccessPointBuilder(mContext).setLevel(level).setReachable(true).build(); + assertThat(testAp.getLevel()).isEqualTo(level); + } + + @Test + public void testBuilder_setSsid() { + String name = "AmazingSsid!"; + AccessPoint namedAp = new TestAccessPointBuilder(mContext).setSsid(name).build(); + assertThat(namedAp.getSsidStr()).isEqualTo(name); + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java new file mode 100644 index 000000000000..665c439c3d6d --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java @@ -0,0 +1,128 @@ +/* + * 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.settingslib.wifi; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiConfiguration; +import android.os.Bundle; + +/** +* Build and return a valid AccessPoint. +* +* Only intended for testing the AccessPoint class; +* AccessPoints were designed to only be populated +* by the mechanisms of scan results and wifi configurations. +*/ +public class TestAccessPointBuilder { + // match the private values in WifiManager + private static final int MIN_RSSI = -100; + private static final int MAX_RSSI = -55; + + // set some sensible defaults + private int mRssi = AccessPoint.UNREACHABLE_RSSI; + private int networkId = WifiConfiguration.INVALID_NETWORK_ID; + private String ssid = "TestSsid"; + private NetworkInfo mNetworkInfo = null; + + Context mContext; + + public TestAccessPointBuilder(Context context) { + mContext = context; + } + + public AccessPoint build() { + Bundle bundle = new Bundle(); + + WifiConfiguration wifiConig = new WifiConfiguration(); + wifiConig.networkId = networkId; + + bundle.putString(AccessPoint.KEY_SSID, ssid); + bundle.putParcelable(AccessPoint.KEY_CONFIG, wifiConig); + bundle.putParcelable(AccessPoint.KEY_NETWORKINFO, mNetworkInfo); + AccessPoint ap = new AccessPoint(mContext, bundle); + ap.setRssi(mRssi); + return ap; + } + + public TestAccessPointBuilder setActive(boolean active) { + if (active) { + mNetworkInfo = new NetworkInfo( + ConnectivityManager.TYPE_DUMMY, + ConnectivityManager.TYPE_DUMMY, + "TestNetwork", + "TestNetwork"); + } else { + mNetworkInfo = null; + } + return this; + } + + /** + * Set the signal level. + * Side effect: if this AccessPoint was previously unreachable, + * setting the level will also make it reachable. + */ + public TestAccessPointBuilder setLevel(int level) { + int outputRange = AccessPoint.SIGNAL_LEVELS - 1; + + if (level > outputRange) { + level = outputRange; + } else if (level < 0) { + level = 0; + } + + int inputRange = MAX_RSSI - MIN_RSSI; + + // calculate the rssi required to get the level we want. + // this is a rearrangement of the formula from WifiManager.calculateSignalLevel() + mRssi = (int)((float)(level * inputRange) / (float)outputRange) + MIN_RSSI; + return this; + } + + /** + * Set whether the AccessPoint is reachable. + * Side effect: if the signal level was not previously set, + * making an AccessPoint reachable will set the signal to the minimum level. + */ + public TestAccessPointBuilder setReachable(boolean reachable) { + if (reachable) { + // only override the mRssi if it hasn't been set yet + if (mRssi == AccessPoint.UNREACHABLE_RSSI) { + mRssi = MIN_RSSI; + } + } else { + mRssi = AccessPoint.UNREACHABLE_RSSI; + } + return this; + } + + public TestAccessPointBuilder setSaved(boolean saved){ + if (saved) { + networkId = 1; + } else { + networkId = WifiConfiguration.INVALID_NETWORK_ID; + } + return this; + } + + public TestAccessPointBuilder setSsid(String newSsid) { + ssid = newSsid; + return this; + } +} 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 2018c13a622d..e10088486910 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.Context; 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.RssiCurve; 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.List; 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 @@ public class WifiTrackerTest { @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 @@ public class WifiTrackerTest { 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 @@ public class WifiTrackerTest { 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 @@ public class WifiTrackerTest { 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 @@ public class WifiTrackerTest { } @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 @@ public class WifiTrackerTest { 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 @@ public class WifiTrackerTest { @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 @@ public class WifiTrackerTest { 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 @@ public class WifiTrackerTest { @Test public void scoreCacheUpdateScoresShouldInsertBadgeIntoAccessPoint() throws InterruptedException { - WifiTracker tracker = createTrackerAndInjectInitialScanResults(); + WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); updateScoresAndWaitForAccessPointsChangedCallback(); List<AccessPoint> aps = tracker.getAccessPoints(); @@ -454,7 +500,7 @@ public class WifiTrackerTest { Settings.Global.NETWORK_SCORING_UI_ENABLED, 0 /* disabled */); - WifiTracker tracker = createTrackerAndInjectInitialScanResults(); + WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); updateScoresAndWaitForAccessPointsChangedCallback(); List<AccessPoint> aps = tracker.getAccessPoints(); @@ -473,7 +519,7 @@ public class WifiTrackerTest { // 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 @@ public class WifiTrackerTest { @Test public void scoreCacheAndListenerShouldBeUnregisteredWhenStopTrackingIsCalled() throws Exception { - WifiTracker tracker = createTrackerAndInjectInitialScanResults(); + WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue(); tracker.stopTracking(); diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 12d0c0306e61..1df626ff9a2f 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -44,6 +44,7 @@ import java.util.zip.ZipOutputStream; import libcore.io.Streams; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ChooserActivity; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FastPrintWriter; @@ -943,8 +944,13 @@ public class BugreportProgressService extends Service { } static void sendShareIntent(Context context, Intent intent) { - context.startActivity(Intent.createChooser(intent, - context.getResources().getText(R.string.bugreport_intent_chooser_title))); + final Intent chooserIntent = Intent.createChooser(intent, + context.getResources().getText(R.string.bugreport_intent_chooser_title)); + + // Since we may be launched behind lockscreen, make sure that ChooserActivity doesn't finish + // itself in onStop. + chooserIntent.putExtra(ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP, true); + context.startActivity(chooserIntent); } /** diff --git a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml index 11e83a19da75..a7fb1a136295 100644 --- a/packages/SystemUI/res/drawable/ic_fingerprint_error.xml +++ b/packages/SystemUI/res/drawable/ic_fingerprint_error.xml @@ -19,12 +19,12 @@ Copyright (C) 2015 The Android Open Source Project 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/ic_qs_network_logging.xml b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml index 87b5a14153f7..7bdf50c74bed 100644 --- a/packages/SystemUI/res/drawable/ic_qs_network_logging.xml +++ b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml @@ -20,8 +20,7 @@ Copyright (C) 2016 The Android Open Source Project android:width="12.0dp" android:height="12.0dp" android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="#4DFFFFFF" > + android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" android:pathData="M2,24v-4h12v4H2z M2,16v-4h20v4H2z M5,7 12,0 19,7 14,7 14,15 10,15 10,7z"/> 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 e207cb3808c0..3af2f7fbbc3f 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 2b4babce277b..a577afc75d9e 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/qs_ic_wifi_lock.xml b/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml index b7da30b03576..bf405fa30d75 100644 --- a/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml +++ b/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml @@ -20,10 +20,10 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="72.0" android:tint="?android:attr/textColorPrimary"> <group - android:translateX="52.0" - android:translateY="42.0" > + android:translateX="28.0" + android:translateY="10.5" > <path - android:fillColor="#FFFFFFFF" - android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.76 -2.24,-5.0 -5.0,-5.0S7.0,3.24 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.0 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-6.0,9.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0 2.0,0.9 2.0,2.0 -0.9,2.0 -2.0,2.0zm3.1,-9.0L8.9,8.0L8.9,6.0c0.0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0.0 3.1,1.39 3.1,3.1l0.0,2.0z"/> + android:pathData="M36 16l-2 0 0 -4C34 6.48 29.52 2 24 2 18.48 2 14 6.48 14 12l0 4 -2 0c-2.21 0 -4 1.79 -4 4l0 20c0 2.21 1.79 4 4 4l24 0c2.21 0 4 -1.79 4 -4l0 -20c0 -2.21 -1.79 -4 -4 -4zM24 34c-2.21 0 -4 -1.79 -4 -4 0 -2.21 1.79 -4 4 -4 2.21 0 4 1.79 4 4 0 2.21 -1.79 4 -4 4zm6.2 -18l-12.4 0 0 -4c0 -3.42 2.78 -6.2 6.2 -6.2 3.42 0 6.2 2.78 6.2 6.2l0 4z" + android:fillColor="#FFFFFF" /> </group> </vector> diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml index 6edf13566ad0..6544f0d780df 100644 --- a/packages/SystemUI/res/layout/qs_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_detail_item.xml @@ -63,7 +63,6 @@ android:focusable="true" android:scaleType="center" android:contentDescription="@*android:string/media_route_controller_disconnect" - android:tint="?android:attr/textColorPrimary" - android:src="@drawable/ic_qs_cancel" /> + android:tint="?android:attr/textColorPrimary" /> </LinearLayout> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 407cddf54b77..f461bb3ffedf 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 48b76648e7a3..9168256eca89 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/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 20866c06b16b..8f7a81c2fd76 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -337,12 +337,11 @@ public class PipManager implements BasePipManager { * Moves the PIPed activity to the fullscreen and closes PIP system UI. */ void movePipToFullscreen() { - mState = STATE_NO_PIP; mPipTaskId = TASK_ID_NO_PIP; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onMoveToFullscreen(); } - resizePinnedStack(mState); + resizePinnedStack(STATE_NO_PIP); updatePipVisibility(false); } @@ -388,6 +387,7 @@ public class PipManager implements BasePipManager { if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state); boolean wasRecentsShown = (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED); + boolean wasStateNoPip = (mState == STATE_NO_PIP); mState = state; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onPipResizeAboutToStart(); @@ -401,6 +401,11 @@ public class PipManager implements BasePipManager { switch (mState) { case STATE_NO_PIP: mCurrentPipBounds = null; + // If the state was already STATE_NO_PIP, then do not resize the stack below as it + // will not exist + if (wasStateNoPip) { + return; + } break; case STATE_PIP_MENU: mCurrentPipBounds = mMenuModePipBounds; @@ -418,18 +423,16 @@ public class PipManager implements BasePipManager { mCurrentPipBounds = mPipBounds; break; } - if (mCurrentPipBounds != null) { - try { - int animationDurationMs = -1; - if (wasRecentsShown - && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) { - animationDurationMs = mRecentsFocusChangedAnimationDurationMs; - } - mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, - true, true, true, animationDurationMs); - } catch (RemoteException e) { - Log.e(TAG, "resizeStack failed", e); + try { + int animationDurationMs = -1; + if (wasRecentsShown + && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) { + animationDurationMs = mRecentsFocusChangedAnimationDurationMs; } + mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, + true, true, true, animationDurationMs); + } catch (RemoteException e) { + Log.e(TAG, "resizeStack failed", e); } } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 09ce2ada11e0..daf0622fa7a6 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.provider.Settings; 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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java index dad8bead4092..65238b118a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java @@ -203,16 +203,28 @@ public class QSDetailItems extends FrameLayout { } } }); - final ImageView disconnect = (ImageView) view.findViewById(android.R.id.icon2); - disconnect.setVisibility(item.canDisconnect ? VISIBLE : GONE); - disconnect.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mCallback != null) { - mCallback.onDetailItemDisconnect(item); + + final ImageView icon2 = (ImageView) view.findViewById(android.R.id.icon2); + if (item.canDisconnect) { + icon2.setImageResource(R.drawable.ic_qs_cancel); + icon2.setVisibility(VISIBLE); + icon2.setClickable(true); + icon2.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mCallback != null) { + mCallback.onDetailItemDisconnect(item); + } } - } - }); + }); + } else if (item.icon2 != -1) { + icon2.setVisibility(VISIBLE); + icon2.setImageResource(item.icon2); + icon2.setClickable(false); + } else { + icon2.setVisibility(GONE); + } + return view; } }; @@ -245,6 +257,7 @@ public class QSDetailItems extends FrameLayout { public CharSequence line2; public Object tag; public boolean canDisconnect; + public int icon2 = -1; } public interface Callback { diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index dea56aa11fde..cdde6ea0e85a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -87,7 +87,7 @@ public class CustomTile extends QSTile<QSTile.State> implements TileChangeListen private void setTileIcon() { try { PackageManager pm = mContext.getPackageManager(); - int flags = PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE; + int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE; if (isSystemApp(pm)) { flags |= PackageManager.MATCH_DISABLED_COMPONENTS; } 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 c7b6aea9c392..9cd79f84cb89 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 @@ public class DataUsageDetailView extends LinearLayout { 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/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 90a9db6b189f..796967c877c7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -372,9 +372,9 @@ public class WifiTile extends QSTile<QSTile.SignalState> { item.icon = mWifiController.getIcon(ap); item.line1 = ap.getSsid(); item.line2 = ap.isActive() ? ap.getSummary() : null; - item.overlay = ap.getSecurity() != AccessPoint.SECURITY_NONE - ? mContext.getDrawable(R.drawable.qs_ic_wifi_lock) - : null; + item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE + ? R.drawable.qs_ic_wifi_lock + : -1; items[i] = item; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 02b01136f352..e0dac090ad6e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -197,6 +197,7 @@ public class TaskViewThumbnail extends View { bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom); mThumbnailData = thumbnailData; updateThumbnailMatrix(); + updateThumbnailPaintFilter(); } else { mBitmapShader = null; mDrawPaint.setShader(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index bc992d821998..fb92a67c6aac 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 android.view.ViewGroup; 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 @@ public class KeyguardIndicationController { 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 @@ public class KeyguardIndicationController { || 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 @@ public class KeyguardIndicationController { @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/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index ed71e5708ce1..f3c2bc56b409 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.os.SystemClock; 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 @@ public class BarTransitions { 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/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index c7f518c15525..c1c385aa8207 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3466,6 +3466,12 @@ message MetricsEvent { // 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/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index f7cb0104d3a2..bfc6e836c3b0 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.AutoFillServiceInfo; 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; @@ -719,18 +720,19 @@ final class AutoFillManagerServiceImpl { } return; } - final ArraySet<AutoFillId> savableIds = mCurrentResponse.getSavableIds(); + final SaveInfo saveInfo = mCurrentResponse.getSaveInfo(); if (DEBUG) { - Slog.d(TAG, "showSaveLocked(): savableIds=" + savableIds); + Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo); } - if (savableIds == null || savableIds.isEmpty()) { + 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); @@ -741,8 +743,9 @@ final class AutoFillManagerServiceImpl { Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": " + state.mAutoFillValue); } - getUiForShowing().showSaveUi(mInfo.getServiceInfo() - .loadLabel(mContext.getPackageManager())); + getUiForShowing().showSaveUi( + mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()), + saveInfo); return; } } diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index cc0baa319307..949a80c7bfdc 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.IBinder; import android.service.autofill.Dataset; import android.service.autofill.FillResponse; +import android.service.autofill.SaveInfo; import android.text.TextUtils; import android.view.autofill.AutoFillId; import android.widget.Toast; @@ -172,13 +173,13 @@ public final class AutoFillUI { /** * Shows the UI asking the user to save for auto-fill. */ - public void showSaveUi(@NonNull CharSequence providerLabel) { + public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info) { mHandler.post(() -> { if (!hasCallback()) { return; } hideAllUiThread(); - mSaveUi = new SaveUi(mContext, providerLabel, + mSaveUi = new SaveUi(mContext, providerLabel, info, new SaveUi.OnSaveListener() { @Override public void onSave() { diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index b7215d66dacc..afe93c7d6e06 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.app.Dialog; import android.content.Context; import android.os.Handler; +import android.service.autofill.SaveInfo; import android.text.format.DateUtils; import android.view.Gravity; import android.view.Window; @@ -50,15 +51,41 @@ final class SaveUi { private boolean mDestroyed; - SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, + SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info, @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 TextView titleView = (TextView) view.findViewById(R.id.autofill_save_title); + final String type; + + switch(info.getType()) { + case SaveInfo.SAVE_DATA_TYPE_PASSWORD: + type = context.getString(R.string.autofill_save_type_password); + break; + case SaveInfo.SAVE_DATA_TYPE_ADDRESS: + type = context.getString(R.string.autofill_save_type_address); + break; + case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD: + type = context.getString(R.string.autofill_save_type_credit_card); + break; + default: + type = null; + } + + final String title = (type == null) + ? context.getString(R.string.autofill_save_title, providerLabel) + : context.getString(R.string.autofill_save_title_with_type, type, providerLabel); + + titleView.setText(title); + final CharSequence subTitle = info.getDescription(); + if (subTitle != null) { + final TextView subTitleView = (TextView) view.findViewById(R.id.autofill_save_subtitle); + subTitleView.setText(subTitle); + subTitleView.setVisibility(View.VISIBLE); + } final View noButton = view.findViewById(R.id.autofill_save_no); noButton.setOnClickListener((v) -> mListener.onCancel()); diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 6248cab0ce10..81f137edaa32 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -130,6 +130,8 @@ public final class BatteryService extends SystemService { private int mLastMaxChargingVoltage; private int mLastChargeCounter; + private int mSequence = 1; + private int mInvalidCharger; private int mLastInvalidCharger; @@ -448,27 +450,29 @@ public final class BatteryService extends SystemService { } } - sendIntentLocked(); + mSequence++; // Separate broadcast is sent for power connected / not connected // since the standard intent will not wake any applications and some // applications may want to have smart behavior based on this. if (mPlugType != 0 && mLastPlugType == 0) { + final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); mHandler.post(new Runnable() { @Override public void run() { - Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); - statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); } }); } else if (mPlugType == 0 && mLastPlugType != 0) { + final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); mHandler.post(new Runnable() { @Override public void run() { - Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); - statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); } }); @@ -476,26 +480,33 @@ public final class BatteryService extends SystemService { if (shouldSendBatteryLowLocked()) { mSentLowBatteryBroadcast = true; + final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); mHandler.post(new Runnable() { @Override public void run() { - Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); - statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); } }); } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) { mSentLowBatteryBroadcast = false; + final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); mHandler.post(new Runnable() { @Override public void run() { - Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); - statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); } }); } + // We are doing this after sending the above broadcasts, so anything processing + // them will get the new sequence number at that point. (See for example how testing + // of JobScheduler's BatteryController works.) + sendIntentLocked(); + // Update the battery LED mLed.updateLightsLocked(); @@ -527,6 +538,7 @@ public final class BatteryService extends SystemService { int icon = getIconLocked(mBatteryProps.batteryLevel); + intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus); intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth); intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent); @@ -666,12 +678,28 @@ public final class BatteryService extends SystemService { pw.println("Battery service (battery) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" set [ac|usb|wireless|status|level|invalid] <value>"); + pw.println(" set [-f] [ac|usb|wireless|status|level|invalid] <value>"); pw.println(" Force a battery property value, freezing battery state."); - pw.println(" unplug"); + pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); + pw.println(" unplug [-f]"); pw.println(" Force battery unplugged, freezing battery state."); - pw.println(" reset"); + pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); + pw.println(" reset [-f]"); pw.println(" Unfreeze battery state, returning to current hardware values."); + pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); + } + + static final int OPTION_FORCE_UPDATE = 1<<0; + + int parseOptions(Shell shell) { + String opt; + int opts = 0; + while ((opt = shell.getNextOption()) != null) { + if ("-f".equals(opt)) { + opts |= OPTION_FORCE_UPDATE; + } + } + return opts; } int onShellCommand(Shell shell, String cmd) { @@ -681,6 +709,7 @@ public final class BatteryService extends SystemService { PrintWriter pw = shell.getOutPrintWriter(); switch (cmd) { case "unplug": { + int opts = parseOptions(shell); getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); if (!mUpdatesStopped) { @@ -692,12 +721,13 @@ public final class BatteryService extends SystemService { long ident = Binder.clearCallingIdentity(); try { mUpdatesStopped = true; - processValuesLocked(false); + processValuesFromShellLocked(pw, opts); } finally { Binder.restoreCallingIdentity(ident); } } break; case "set": { + int opts = parseOptions(shell); getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); final String key = shell.getNextArg(); @@ -745,7 +775,7 @@ public final class BatteryService extends SystemService { long ident = Binder.clearCallingIdentity(); try { mUpdatesStopped = true; - processValuesLocked(false); + processValuesFromShellLocked(pw, opts); } finally { Binder.restoreCallingIdentity(ident); } @@ -756,6 +786,7 @@ public final class BatteryService extends SystemService { } } break; case "reset": { + int opts = parseOptions(shell); getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); @@ -763,7 +794,7 @@ public final class BatteryService extends SystemService { if (mUpdatesStopped) { mUpdatesStopped = false; mBatteryProps.set(mLastBatteryProps); - processValuesLocked(false); + processValuesFromShellLocked(pw, opts); } } finally { Binder.restoreCallingIdentity(ident); @@ -775,6 +806,13 @@ public final class BatteryService extends SystemService { return 0; } + private void processValuesFromShellLocked(PrintWriter pw, int opts) { + processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0); + if ((opts & OPTION_FORCE_UPDATE) != 0) { + pw.println(mSequence); + } + } + private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mLock) { if (args == null || args.length == 0 || "-a".equals(args[0])) { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index ef7780c79126..979096e158fe 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -19,6 +19,7 @@ package com.android.server; 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.HashMap; 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); updateUserProfiles(mCurrentUserId); - updateThrottlingWhitelistLocked(); + updateBackgroundThrottlingWhitelistLocked(); // prepare providers loadProvidersLocked(); @@ -381,7 +416,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onChange(boolean selfChange) { synchronized (mLock) { - updateThrottlingWhitelistLocked(); + updateBackgroundThrottlingWhitelistLocked(); updateProvidersLocked(); } } @@ -721,14 +756,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } + 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { @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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 b33538cbcb07..d54ebaafb5bb 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.INetworkScoreCache; 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { * 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { synchronized (mServiceConnectionLock) { if (mServiceConnection != null) { mServiceConnection.disconnect(mContext); + if (DBG) Log.d(TAG, "Disconnected from: " + + mServiceConnection.mAppData.getRecommendationServiceComponent()); } mServiceConnection = null; } @@ -653,17 +662,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return null; } - /** * Returns metadata about the active scorer or <code>null</code> if there is no active scorer. */ @@ -727,7 +731,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub { */ @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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 000000000000..2f4485a4ea4c --- /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/SystemService.java b/services/core/java/com/android/server/SystemService.java index ce3166d006f9..421d5a6ab964 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -19,6 +19,7 @@ package com.android.server; 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 abstract class SystemService { 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d287d8522c00..d9b6fd73905e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3023,15 +3023,24 @@ public class ActivityManagerService extends IActivityManager.Stub startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid); } else { finishRunningVoiceLocked(); - IVoiceInteractionSession session; - if (mLastResumedActivity != null - && ((session = mLastResumedActivity.task.voiceSession) != null - || (session = mLastResumedActivity.voiceSession) != null)) { - // We had been in a voice interaction session, but now focused has - // move to something different. Just finish the session, we can't - // return to it and retain the proper state and synchronization with - // the voice interaction service. - finishVoiceTask(session); + + if (mLastResumedActivity != null) { + final IVoiceInteractionSession session; + + if (mLastResumedActivity.task != null + && mLastResumedActivity.task.voiceSession != null) { + session = mLastResumedActivity.task.voiceSession; + } else { + session = mLastResumedActivity.voiceSession; + } + + if (session != null) { + // We had been in a voice interaction session, but now focused has + // move to something different. Just finish the session, we can't + // return to it and retain the proper state and synchronization with + // the voice interaction service. + finishVoiceTask(session); + } } } @@ -10636,7 +10645,7 @@ public class ActivityManagerService extends IActivityManager.Stub providers = AppGlobals.getPackageManager() .queryContentProviders(app.processName, app.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS - | MATCH_DEBUG_TRIAGED_MISSING) + | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null) .getList(); } catch (RemoteException ex) { } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index aef429e2e28f..10055c8d98f7 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -802,10 +802,7 @@ final class ActivityRecord implements AppWindowContainerListener { // Remove the activity from the old task and add it to the new task prevTask.removeActivity(this); - // TODO(b/34179495): This should really be set to null in removeActivity() call above, - // but really bad things that I can't track down right now happen when I do that. - // So, setting it here now and will change later when there is time for investigation. - task = null; + newTask.addActivityAtIndex(position, this); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6f79bc271198..ab1559d8afb4 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -177,7 +177,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; @@ -4902,7 +4901,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D setResizingDuringAnimation(task); } - mService.mActivityStarter.postStartActivityUncheckedProcessing(task.getTopActivity(), + mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT, sourceRecord != null ? sourceRecord.task.getStackId() : INVALID_STACK_ID, sourceRecord, task.getStack()); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 83d43db20671..d8c5533811cb 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -17,6 +17,7 @@ package com.android.server.am; import static android.app.Activity.RESULT_CANCELED; +import static android.app.ActivityManager.START_CANCELED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED; @@ -88,12 +89,10 @@ import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityContainer; import android.app.IApplicationThread; -import android.app.KeyguardManager; import android.app.PendingIntent; import android.app.ProfilerInfo; import android.app.WaitResult; import android.content.ComponentName; -import android.content.Context; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; @@ -509,33 +508,26 @@ class ActivityStarter { doPendingActivityLaunchesLocked(false); - try { - mService.mWindowManager.deferSurfaceLayout(); - err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, - true, options, inTask); - } finally { - mService.mWindowManager.continueSurfaceLayout(); - } - postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack); - return err; + return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, + options, inTask); } /** 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) { + Intent originalIntent, String callingPackage, + String resolvedType, int userId) { if (auxiliaryResponse.needsPhaseTwo) { // request phase two resolution mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo( - auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId); + auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId); } return EphemeralResolver.buildEphemeralInstallerIntent(originalIntent, - callingPackage, resolvedType, userId, auxiliaryResponse.packageName, - auxiliaryResponse.splitName, auxiliaryResponse.versionCode, - auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); + callingPackage, resolvedType, userId, auxiliaryResponse.packageName, + auxiliaryResponse.splitName, auxiliaryResponse.versionCode, + auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); } - void postStartActivityUncheckedProcessing( + void postStartActivityProcessing( ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord, ActivityStack targetStack) { @@ -937,6 +929,32 @@ class ActivityStarter { } } + private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) { + int result = START_CANCELED; + try { + mService.mWindowManager.deferSurfaceLayout(); + result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, + startFlags, + doResume, options, inTask); + } finally { + // If we are not able to proceed, disassociate the activity from the task. Leaving an + // activity in an incomplete state can lead to issues, such as performing operations + // without a window container. + if (result != START_SUCCESS && mStartActivity.task != null) { + mStartActivity.task.removeActivity(mStartActivity); + } + mService.mWindowManager.continueSurfaceLayout(); + } + + postStartActivityProcessing(r, result, mSupervisor.mFocusedStack.mStackId, + mSourceRecord, mTargetStack); + + return result; + } + + // Note: This method should only be called from {@link startActivity}. private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) { @@ -1843,11 +1861,8 @@ class ActivityStarter { final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); final boolean resume = doResume && mPendingActivityLaunches.isEmpty(); try { - final int result = startActivityUnchecked( - pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, null); - postStartActivityUncheckedProcessing( - pal.r, result, mSupervisor.mFocusedStack.mStackId, mSourceRecord, - mTargetStack); + startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, + null); } catch (Exception e) { Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e); pal.sendErrorResult(e.getMessage()); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index f8645d65fd92..b0513049ff3a 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1083,6 +1083,13 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta /** @return true if this was the last activity in the task */ boolean removeActivity(ActivityRecord r) { + if (r.task != this) { + throw new IllegalArgumentException( + "Activity=" + r + " does not belong to task=" + this); + } + + r.task = null; + if (mActivities.remove(r) && r.fullscreen) { // Was previously in list. numFullscreen--; diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index e8ff510242ff..d648dd82dc9a 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 @@ public final class PlaybackActivityMonitor 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 @@ public final class PlaybackActivityMonitor 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 @@ public final class PlaybackActivityMonitor 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 @@ public final class PlaybackActivityMonitor } 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 @@ public final class PlaybackActivityMonitor 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/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java index 82e2eb425da0..0a6d8a4e2474 100644 --- a/services/core/java/com/android/server/job/JobPackageTracker.java +++ b/services/core/java/com/android/server/job/JobPackageTracker.java @@ -437,7 +437,7 @@ public final class JobPackageTracker { for (int i=0; i<size; i++) { final int index = mEventIndices.indexOf(i); final int uid = mEventUids[index]; - if (filterUid != -1 && filterUid != UserHandle.getAppId(filterUid)) { + if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { continue; } final int cmd = mEventCmds[index]; diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index a7480133972a..c9739110c03a 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -131,6 +131,8 @@ public final class JobSchedulerService extends com.android.server.SystemService final List<JobServiceContext> mActiveServices = new ArrayList<>(); /** List of controllers that will notify this service of updates to jobs. */ List<StateController> mControllers; + /** Need direct access to this for testing. */ + BatteryController mBatteryController; /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list * when ready to execute them. @@ -194,6 +196,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // Key names stored in the settings value. private static final String KEY_MIN_IDLE_COUNT = "min_idle_count"; private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count"; + private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count"; private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count"; private static final String KEY_MIN_CONTENT_COUNT = "min_content_count"; private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; @@ -207,6 +210,7 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_MIN_IDLE_COUNT = 1; private static final int DEFAULT_MIN_CHARGING_COUNT = 1; + private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1; private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1; private static final int DEFAULT_MIN_CONTENT_COUNT = 1; private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1; @@ -229,6 +233,11 @@ public final class JobSchedulerService extends com.android.server.SystemService */ int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT; /** + * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to + * schedule things early. + */ + int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT; + /** * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule * things early. 1 == Run connectivity jobs as soon as ready. */ @@ -312,6 +321,8 @@ public final class JobSchedulerService extends com.android.server.SystemService DEFAULT_MIN_IDLE_COUNT); MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, DEFAULT_MIN_CHARGING_COUNT); + MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT, + DEFAULT_MIN_BATTERY_NOT_LOW_COUNT); MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, DEFAULT_MIN_CONNECTIVITY_COUNT); MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, @@ -356,6 +367,9 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("="); pw.print(MIN_CHARGING_COUNT); pw.println(); + pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("="); + pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println(); + pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("="); pw.print(MIN_CONNECTIVITY_COUNT); pw.println(); @@ -785,7 +799,8 @@ public final class JobSchedulerService extends com.android.server.SystemService mControllers.add(ConnectivityController.get(this)); mControllers.add(TimeController.get(this)); mControllers.add(IdleController.get(this)); - mControllers.add(BatteryController.get(this)); + mBatteryController = BatteryController.get(this); + mControllers.add(mBatteryController); mControllers.add(AppIdleController.get(this)); mControllers.add(ContentObserverController.get(this)); mControllers.add(DeviceIdleJobsController.get(this)); @@ -1186,6 +1201,7 @@ public final class JobSchedulerService extends com.android.server.SystemService */ class MaybeReadyJobQueueFunctor implements JobStatusFunctor { int chargingCount; + int batteryNotLowCount; int idleCount; int backoffCount; int connectivityCount; @@ -1223,6 +1239,9 @@ public final class JobSchedulerService extends com.android.server.SystemService if (job.hasChargingConstraint()) { chargingCount++; } + if (job.hasBatteryNotLowConstraint()) { + batteryNotLowCount++; + } if (job.hasContentTriggerConstraint()) { contentCount++; } @@ -1241,6 +1260,7 @@ public final class JobSchedulerService extends com.android.server.SystemService idleCount >= mConstants.MIN_IDLE_COUNT || connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT || chargingCount >= mConstants.MIN_CHARGING_COUNT || + batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT || contentCount >= mConstants.MIN_CONTENT_COUNT || (runnableJobs != null && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) { @@ -1264,6 +1284,7 @@ public final class JobSchedulerService extends com.android.server.SystemService idleCount = 0; backoffCount = 0; connectivityCount = 0; + batteryNotLowCount = 0; contentCount = 0; runnableJobs = null; } @@ -1764,6 +1785,34 @@ public final class JobSchedulerService extends com.android.server.SystemService return 0; } + void setMonitorBattery(boolean enabled) { + synchronized (mLock) { + if (mBatteryController != null) { + mBatteryController.getTracker().setMonitorBatteryLocked(enabled); + } + } + } + + int getBatterySeq() { + synchronized (mLock) { + return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1; + } + } + + boolean getBatteryCharging() { + synchronized (mLock) { + return mBatteryController != null + ? mBatteryController.getTracker().isOnStablePower() : false; + } + } + + boolean getBatteryNotLow() { + synchronized (mLock) { + return mBatteryController != null + ? mBatteryController.getTracker().isBatteryNotLow() : false; + } + } + private String printContextIdToJobMap(JobStatus[] map, String initial) { StringBuilder s = new StringBuilder(initial + ": "); for (int i=0; i<map.length; i++) { diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java index 2d62c1c4e828..ec2340791555 100644 --- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java @@ -43,10 +43,19 @@ public class JobSchedulerShellCommand extends ShellCommand { public int onCommand(String cmd) { final PrintWriter pw = getOutPrintWriter(); try { - if ("run".equals(cmd)) { - return runJob(); - } else { - return handleDefaultCommands(cmd); + switch (cmd != null ? cmd : "") { + case "run": + return runJob(pw); + case "monitor-battery": + return runMonitorBattery(pw); + case "get-battery-seq": + return runGetBatterySeq(pw); + case "get-battery-charging": + return runGetBatteryCharging(pw); + case "get-battery-not-low": + return runGetBatteryNotLow(pw); + default: + return handleDefaultCommands(cmd); } } catch (Exception e) { pw.println("Exception: " + e); @@ -54,20 +63,23 @@ public class JobSchedulerShellCommand extends ShellCommand { return -1; } - private int runJob() { - try { - final int uid = Binder.getCallingUid(); - final int perm = mPM.checkUidPermission( - "android.permission.CHANGE_APP_IDLE_STATE", uid); - if (perm != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Uid " + uid - + " not permitted to force scheduled jobs"); - } - } catch (RemoteException e) { - // Can't happen + private void checkPermission(String operation) throws Exception { + final int uid = Binder.getCallingUid(); + if (uid == 0) { + // Root can do anything. + return; } + final int perm = mPM.checkUidPermission( + "android.permission.CHANGE_APP_IDLE_STATE", uid); + if (perm != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Uid " + uid + + " not permitted to " + operation); + } + } + + private int runJob(PrintWriter pw) throws Exception { + checkPermission("force scheduled jobs"); - final PrintWriter pw = getOutPrintWriter(); boolean force = false; int userId = UserHandle.USER_SYSTEM; @@ -133,6 +145,42 @@ public class JobSchedulerShellCommand extends ShellCommand { return ret; } + private int runMonitorBattery(PrintWriter pw) throws Exception { + checkPermission("change battery monitoring"); + String opt = getNextArgRequired(); + boolean enabled; + if ("on".equals(opt)) { + enabled = true; + } else if ("off".equals(opt)) { + enabled = false; + } else { + getErrPrintWriter().println("Error: unknown option " + opt); + return 1; + } + mInternal.setMonitorBattery(enabled); + if (enabled) pw.println("Battery monitoring enabled"); + else pw.println("Battery monitoring disabled"); + return 0; + } + + private int runGetBatterySeq(PrintWriter pw) { + int seq = mInternal.getBatterySeq(); + pw.println(seq); + return 0; + } + + private int runGetBatteryCharging(PrintWriter pw) { + boolean val = mInternal.getBatteryCharging(); + pw.println(val); + return 0; + } + + private int runGetBatteryNotLow(PrintWriter pw) { + boolean val = mInternal.getBatteryNotLow(); + pw.println(val); + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -140,7 +188,6 @@ public class JobSchedulerShellCommand extends ShellCommand { pw.println("Job scheduler (jobscheduler) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(); pw.println(" run [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID"); pw.println(" Trigger immediate execution of a specific scheduled job."); pw.println(" Options:"); @@ -148,6 +195,15 @@ public class JobSchedulerShellCommand extends ShellCommand { pw.println(" connectivity are not currently met"); pw.println(" -u or --user: specify which user's job is to be run; the default is"); pw.println(" the primary or system user"); + pw.println(" monitor-battery [on|off]"); + pw.println(" Control monitoring of all battery changes. Off by default. Turning"); + pw.println(" on makes get-battery-seq useful."); + pw.println(" get-battery-seq"); + pw.println(" Return the last battery update sequence number that was received."); + pw.println(" get-battery-charging"); + pw.println(" Return whether the battery is currently considered to be charging."); + pw.println(" get-battery-not-low"); + pw.println(" Return whether the battery is currently considered to not be low."); pw.println(); } diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index dd3f85f935dd..ccfc287f6057 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -381,6 +381,9 @@ public class JobStore { if (jobStatus.hasChargingConstraint()) { out.attribute(null, "charging", Boolean.toString(true)); } + if (jobStatus.hasBatteryNotLowConstraint()) { + out.attribute(null, "battery-not-low", Boolean.toString(true)); + } out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS); } diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index f6b8ef4387b8..05527bec67c4 100644 --- a/services/core/java/com/android/server/job/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -80,32 +80,38 @@ public class BatteryController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { - final boolean isOnStablePower = mChargeTracker.isOnStablePower(); - if (taskStatus.hasChargingConstraint()) { + if (taskStatus.hasPowerConstraint()) { mTrackedTasks.add(taskStatus); - taskStatus.setChargingConstraintSatisfied(isOnStablePower); + taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower()); + taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow()); } } @Override public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { - if (taskStatus.hasChargingConstraint()) { + if (taskStatus.hasPowerConstraint()) { mTrackedTasks.remove(taskStatus); } } private void maybeReportNewChargingState() { final boolean stablePower = mChargeTracker.isOnStablePower(); + final boolean batteryNotLow = mChargeTracker.isBatteryNotLow(); if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingState: " + stablePower); } boolean reportChange = false; synchronized (mLock) { - for (JobStatus ts : mTrackedTasks) { + for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { + final JobStatus ts = mTrackedTasks.get(i); boolean previous = ts.setChargingConstraintSatisfied(stablePower); if (previous != stablePower) { reportChange = true; } + previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow); + if (previous != batteryNotLow) { + reportChange = true; + } } } // Let the scheduler know that state has changed. This may or may not result in an @@ -127,6 +133,10 @@ public class BatteryController extends StateController { private boolean mCharging; /** Keep track of whether the battery is charged enough that we want to do work. */ private boolean mBatteryHealthy; + /** Sequence number of last broadcast. */ + private int mLastBatterySeq = -1; + + private BroadcastReceiver mMonitor; public ChargingTracker() { } @@ -149,10 +159,42 @@ public class BatteryController extends StateController { mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); } - boolean isOnStablePower() { + public void setMonitorBatteryLocked(boolean enabled) { + if (enabled) { + if (mMonitor == null) { + mMonitor = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + ChargingTracker.this.onReceive(context, intent); + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mContext.registerReceiver(mMonitor, filter); + } + } else { + if (mMonitor != null) { + mContext.unregisterReceiver(mMonitor); + mMonitor = null; + } + } + } + + public boolean isOnStablePower() { return mCharging && mBatteryHealthy; } + public boolean isBatteryNotLow() { + return mBatteryHealthy; + } + + public boolean isMonitoring() { + return mMonitor != null; + } + + public int getSeq() { + return mLastBatterySeq; + } + @Override public void onReceive(Context context, Intent intent) { onReceiveInternal(intent); @@ -191,13 +233,20 @@ public class BatteryController extends StateController { mCharging = false; maybeReportNewChargingState(); } + mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq); } } @Override public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { pw.print("Battery: stable power = "); - pw.println(mChargeTracker.isOnStablePower()); + pw.print(mChargeTracker.isOnStablePower()); + pw.print(", not low = "); + pw.println(mChargeTracker.isBatteryNotLow()); + if (mChargeTracker.isMonitoring()) { + pw.print("MONITORING: seq="); + pw.println(mChargeTracker.getSeq()); + } pw.print("Tracking "); pw.print(mTrackedTasks.size()); pw.println(":"); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 4b39bf96478c..9a55fed059f4 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -46,16 +46,17 @@ public final class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; - static final int CONSTRAINT_CHARGING = 1<<0; - static final int CONSTRAINT_TIMING_DELAY = 1<<1; - static final int CONSTRAINT_DEADLINE = 1<<2; - static final int CONSTRAINT_IDLE = 1<<3; - static final int CONSTRAINT_UNMETERED = 1<<4; - static final int CONSTRAINT_CONNECTIVITY = 1<<5; - static final int CONSTRAINT_APP_NOT_IDLE = 1<<6; - static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7; - static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<8; - static final int CONSTRAINT_NOT_ROAMING = 1<<9; + static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; + static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; + static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; + static final int CONSTRAINT_TIMING_DELAY = 1<<31; + static final int CONSTRAINT_DEADLINE = 1<<30; + static final int CONSTRAINT_UNMETERED = 1<<29; + static final int CONSTRAINT_CONNECTIVITY = 1<<28; + static final int CONSTRAINT_APP_NOT_IDLE = 1<<27; + static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; + static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25; + static final int CONSTRAINT_NOT_ROAMING = 1<<24; // Soft override: ignore constraints like time that don't affect API availability public static final int OVERRIDE_SOFT = 1; @@ -163,7 +164,7 @@ public final class JobStatus { this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; - int requiredConstraints = 0; + int requiredConstraints = job.getConstraintFlags(); if (job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY) { requiredConstraints |= CONSTRAINT_CONNECTIVITY; } @@ -173,18 +174,12 @@ public final class JobStatus { if (job.getNetworkType() == JobInfo.NETWORK_TYPE_NOT_ROAMING) { requiredConstraints |= CONSTRAINT_NOT_ROAMING; } - if (job.isRequireCharging()) { - requiredConstraints |= CONSTRAINT_CHARGING; - } if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { requiredConstraints |= CONSTRAINT_TIMING_DELAY; } if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { requiredConstraints |= CONSTRAINT_DEADLINE; } - if (job.isRequireDeviceIdle()) { - requiredConstraints |= CONSTRAINT_IDLE; - } if (job.getTriggerContentUris() != null) { requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; } @@ -331,6 +326,14 @@ public final class JobStatus { return (requiredConstraints&CONSTRAINT_CHARGING) != 0; } + public boolean hasBatteryNotLowConstraint() { + return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0; + } + + public boolean hasPowerConstraint() { + return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0; + } + public boolean hasTimingDelayConstraint() { return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0; } @@ -379,6 +382,10 @@ public final class JobStatus { return setConstraintSatisfied(CONSTRAINT_CHARGING, state); } + boolean setBatteryNotLowConstraintSatisfied(boolean state) { + return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state); + } + boolean setTimingDelayConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state); } @@ -453,13 +460,14 @@ public final class JobStatus { } static final int CONSTRAINTS_OF_INTEREST = - CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | + CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | CONSTRAINT_NOT_ROAMING | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; // Soft override covers all non-"functional" constraints static final int SOFT_OVERRIDE_CONSTRAINTS = - CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE; + CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW + | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE; /** * @return Whether the constraints set on this job are satisfied. @@ -495,6 +503,7 @@ public final class JobStatus { + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME) + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")" + ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging() + + ",BL=" + job.isRequireBatteryNotLow() + ",I=" + job.isRequireDeviceIdle() + ",U=" + (job.getTriggerContentUris() != null) + ",F=" + numFailures + ",P=" + job.isPersisted() @@ -550,6 +559,9 @@ public final class JobStatus { if ((constraints&CONSTRAINT_CHARGING) != 0) { pw.print(" CHARGING"); } + if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) { + pw.print(" BATTERY_NOT_LOW"); + } if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) { pw.print(" TIMING_DELAY"); } @@ -607,7 +619,8 @@ public final class JobStatus { pw.println(Integer.toHexString(job.getFlags())); } pw.print(prefix); pw.print(" Requires: charging="); - pw.print(job.isRequireCharging()); pw.print(" deviceIdle="); + pw.print(job.isRequireCharging()); pw.print(" batteryNotLow="); + pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle="); pw.println(job.isRequireDeviceIdle()); if (job.getTriggerContentUris() != null) { pw.print(prefix); pw.println(" Trigger content URIs:"); diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java index 4795fbf0ba3c..e6edaf1d3079 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 @@ public class BadgeExtractor implements NotificationSignalExtractor { 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/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index c11131a2af7e..71bfa648bd5f 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -442,8 +442,8 @@ public class LauncherAppsService extends SystemService { @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, - String packageName, List shortcutIds, ComponentName componentName, int flags, - UserHandle targetUser) { + String packageName, List shortcutIds, ComponentName componentName, Intent intent, + int flags, UserHandle targetUser) { ensureShortcutPermission(callingPackage); if (!canAccessProfile(callingPackage, targetUser, "Cannot get shortcuts") || !isUserEnabled(targetUser)) { @@ -454,11 +454,17 @@ public class LauncherAppsService extends SystemService { "To query by shortcut ID, package name must also be set"); } + if ((flags & ShortcutQuery.FLAG_MATCH_CHOOSER) == 0 + && intent != null) { + throw new IllegalArgumentException("Supplied an intent in the query, but did " + + "not request chooser targets"); + } + // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below. return new ParceledListSlice<>((List<ShortcutInfo>) mShortcutServiceInternal.getShortcuts(getCallingUserId(), callingPackage, changedSince, packageName, shortcutIds, - componentName, flags, targetUser.getIdentifier())); + componentName, intent, flags, targetUser.getIdentifier())); } @Override @@ -906,6 +912,7 @@ public class LauncherAppsService extends SystemService { cookie.packageName, /* changedSince= */ 0, packageName, /* shortcutIds=*/ null, /* component= */ null, + /* intent= */ null, ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY | ShortcutQuery.FLAG_GET_ALL_KINDS , userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4562bcbfb613..116c0a353643 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -128,7 +128,6 @@ import android.content.pm.ApplicationInfo; 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.AuxiliaryResolveInfo; @@ -143,6 +142,7 @@ import android.content.pm.IPackageInstaller; 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.UserHandle; 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.SparseBooleanArray; 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.Collection; 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; @@ -5682,6 +5684,11 @@ public class PackageManagerService extends IPackageManager.Stub { false, false, false, userId); } + /** + * Returns whether or not instant apps have been disabled remotely. + * <p><em>IMPORTANT</em> This should not be called with the package manager lock + * held. Otherwise we run the risk of deadlock. + */ private boolean isEphemeralDisabled() { // ephemeral apps have been disabled across the board if (DISABLE_EPHEMERAL_APPS) { @@ -5702,10 +5709,6 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean isEphemeralAllowed( Intent intent, List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck) { - // Short circuit and return early if possible. - if (isEphemeralDisabled()) { - return false; - } final int callingUser = UserHandle.getCallingUserId(); if (callingUser != UserHandle.USER_SYSTEM) { return false; @@ -6209,6 +6212,7 @@ public class PackageManagerService extends IPackageManager.Stub { boolean addEphemeral = false; List<ResolveInfo> result; final String pkgName = intent.getPackage(); + final boolean ephemeralDisabled = isEphemeralDisabled(); synchronized (mPackages) { if (pkgName == null) { List<CrossProfileIntentFilter> matchingFilters = @@ -6226,8 +6230,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Check for results in the current profile. result = filterIfNotSystemUser(mActivities.queryIntent( intent, resolvedType, flags, userId), userId); - addEphemeral = - isEphemeralAllowed(intent, result, userId, false /*skipPackageCheck*/); + addEphemeral = !ephemeralDisabled + && isEphemeralAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); @@ -6284,8 +6288,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result - addEphemeral = isEphemeralAllowed( - intent, null /*result*/, userId, true /*skipPackageCheck*/); + addEphemeral = !ephemeralDisabled + && isEphemeralAllowed( + intent, null /*result*/, userId, true /*skipPackageCheck*/); result = new ArrayList<ResolveInfo>(); } } @@ -7467,7 +7472,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public @NonNull ParceledListSlice<ProviderInfo> queryContentProviders(String processName, - int uid, int flags) { + int uid, int flags, String metaDataKey) { final int userId = processName != null ? UserHandle.getUserId(uid) : UserHandle.getCallingUserId(); if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); @@ -7485,6 +7490,14 @@ public class PackageManagerService extends IPackageManager.Stub { || (p.info.processName.equals(processName) && UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) { + + // See PM.queryContentProviders()'s javadoc for why we have the metaData + // parameter. + if (metaDataKey != null + && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) { + continue; + } + if (finalList == null) { finalList = new ArrayList<ProviderInfo>(3); } @@ -11698,7 +11711,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (!whitelisted) { Slog.w(TAG, "Privileged permission " + perm + " for package " + pkg.packageName + " - not in privapp-permissions whitelist"); - if (!mSystemReady) { + // Only report violations for apps on system image + if (!mSystemReady && !pkg.isUpdatedSystemApp()) { if (mPrivappPermissionsViolations == null) { mPrivappPermissionsViolations = new ArraySet<>(); } @@ -18708,7 +18722,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Shame on you for calling a hidden API. Shame!"); try { observer.onGetStatsCompleted(null, false); - } catch (RemoteException ignored) { + } catch (Throwable ignored) { } } @@ -20305,6 +20319,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); 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"); } @@ -20842,6 +20859,98 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } + 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(); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 5f348abd3b8d..b4bba88eaf84 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 @@ package com.android.server.pm; 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 @@ final class PackageSetting extends PackageSettingBase { } 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 601377d6f8d2..b9c43da77916 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.IntentFilterVerificationInfo; 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 @@ abstract class PackageSettingBase extends SettingBase { 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 79e1c2af9788..570b31ffa7b5 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.UserHandle; 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.SparseBooleanArray; 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; @@ -4876,6 +4878,16 @@ final class Settings { } } + 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 @@ final class Settings { } } + 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/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 570259ba5615..ac98ab96f9ca 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; @@ -31,6 +32,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.ShortcutOperation; @@ -68,6 +70,9 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String TAG_EXTRAS = "extras"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_CATEGORIES = "categories"; + private static final String TAG_CHOOSER_EXTRAS = "chooser-extras"; + private static final String TAG_CHOOSER_INTENT_FILTERS = "chooser-intent-filters"; + private static final String TAG_CHOOSER_COMPONENT_NAMES = "chooser-component-names"; private static final String ATTR_NAME = "name"; private static final String ATTR_CALL_COUNT = "call-count"; @@ -91,6 +96,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ICON_RES_ID = "icon-res"; private static final String ATTR_ICON_RES_NAME = "icon-resname"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; + private static final String ATTR_COMPONENT_NAMES = "component-names"; private static final String NAME_CATEGORIES = "categories"; @@ -200,7 +206,7 @@ class ShortcutPackage extends ShortcutPackageItem { if (shortcut != null) { mShortcutUser.mService.removeIcon(getPackageUserId(), shortcut); shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED - | ShortcutInfo.FLAG_MANIFEST); + | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CHOOSER); } return shortcut; } @@ -226,7 +232,7 @@ class ShortcutPackage extends ShortcutPackageItem { Preconditions.checkArgument(newShortcut.isEnabled(), "add/setDynamicShortcuts() cannot publish disabled shortcuts"); - newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); + addCorrectDynamicFlags(newShortcut); final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); @@ -250,6 +256,17 @@ class ShortcutPackage extends ShortcutPackageItem { addShortcutInner(newShortcut); } + // TODO: Sample code & JavaDoc for ShortcutManager needs updating to reflect the fact that + // Chooser shortcuts are not always dynamic. + public void addCorrectDynamicFlags(@NonNull ShortcutInfo shortcut) { + if (shortcut.getIntent() != null) { + shortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); + } + if (!ArrayUtils.isEmpty(shortcut.getChooserIntentFilters())) { + shortcut.addFlags(ShortcutInfo.FLAG_CHOOSER); + } + } + /** * Remove all shortcuts that aren't pinned nor dynamic. */ @@ -282,11 +299,11 @@ class ShortcutPackage extends ShortcutPackageItem { boolean changed = false; for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - if (si.isDynamic()) { + if (si.isDynamic() || si.isChooser()) { changed = true; si.setTimestamp(now); - si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); + si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER); si.setRank(0); // It may still be pinned, so clear the rank. } } @@ -355,7 +372,8 @@ class ShortcutPackage extends ShortcutPackageItem { if (oldShortcut.isPinned()) { oldShortcut.setRank(0); - oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST); + oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST + | ShortcutInfo.FLAG_CHOOSER); if (disable) { oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED); } @@ -1116,8 +1134,8 @@ class ShortcutPackage extends ShortcutPackageItem { // Don't adjust ranks for manifest shortcuts. continue; } - // At this point, it must be dynamic. - if (!si.isDynamic()) { + // At this point, it must be dynamic or a chooser. + if (!si.isDynamicOrChooser()) { s.wtf("Non-dynamic shortcut found."); continue; } @@ -1294,7 +1312,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags() & ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES - | ShortcutInfo.FLAG_DYNAMIC)); + | ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_CHOOSER)); } else { // When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored // as dynamic. @@ -1317,16 +1335,37 @@ class ShortcutPackage extends ShortcutPackageItem { } final Intent[] intentsNoExtras = si.getIntentsNoExtras(); final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases(); - final int numIntents = intentsNoExtras.length; - for (int i = 0; i < numIntents; i++) { - out.startTag(null, TAG_INTENT); - ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]); - ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]); - out.endTag(null, TAG_INTENT); + if (intentsNoExtras != null) { + final int numIntents = intentsNoExtras.length; + for (int i = 0; i < numIntents; i++) { + out.startTag(null, TAG_INTENT); + ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]); + ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]); + out.endTag(null, TAG_INTENT); + } } - ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); + ShortcutService.writeTagExtra(out, TAG_CHOOSER_EXTRAS, si.getChooserExtras()); + + final IntentFilter[] intentFilters = si.getChooserIntentFilters(); + if (intentFilters != null) { + for (int i = 0; i < intentFilters.length; i++) { + out.startTag(null, TAG_CHOOSER_INTENT_FILTERS); + intentFilters[i].writeToXml(out); + out.endTag(null, TAG_CHOOSER_INTENT_FILTERS); + } + } + + final ComponentName[] componentNames = si.getChooserComponentNames(); + if (componentNames != null) { + for (int i = 0; i < componentNames.length; i++) { + out.startTag(null, TAG_CHOOSER_COMPONENT_NAMES); + ShortcutService.writeAttr(out, ATTR_COMPONENT_NAMES, componentNames[i]); + out.endTag(null, TAG_CHOOSER_COMPONENT_NAMES); + } + } + out.endTag(null, TAG_SHORTCUT); } @@ -1398,6 +1437,9 @@ class ShortcutPackage extends ShortcutPackageItem { String iconResName; String bitmapPath; ArraySet<String> categories = null; + PersistableBundle chooserExtras; + List<IntentFilter> chooserIntentFilters = new ArrayList<>(); + List<ComponentName> chooserComponentNames = new ArrayList<>(); id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, @@ -1458,6 +1500,18 @@ class ShortcutPackage extends ShortcutPackageItem { } } continue; + case TAG_CHOOSER_EXTRAS: + chooserExtras = PersistableBundle.restoreFromXml(parser); + continue; + case TAG_CHOOSER_COMPONENT_NAMES: + chooserComponentNames.add(ShortcutService.parseComponentNameAttribute(parser, + ATTR_ACTIVITY)); + continue; + case TAG_CHOOSER_INTENT_FILTERS: + IntentFilter toAdd = new IntentFilter(); + toAdd.readFromXml(parser); + chooserIntentFilters.add(toAdd); + continue; } throw ShortcutService.throwForInvalidTag(depth, tag); } @@ -1551,10 +1605,10 @@ class ShortcutPackage extends ShortcutPackageItem { // Verify each shortcut's status. for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - if (!(si.isDeclaredInManifest() || si.isDynamic() || si.isPinned())) { + if (!(si.isDeclaredInManifest() || si.isDynamicOrChooser() || si.isPinned())) { failed = true; Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() - + " is not manifest, dynamic or pinned."); + + " is not manifest, dynamic, chooser or pinned."); } if (si.isDeclaredInManifest() && si.isDynamic()) { failed = true; @@ -1596,6 +1650,11 @@ class ShortcutPackage extends ShortcutPackageItem { Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " has a dummy target activity"); } + if (si.getIntent() == null && !si.isChooser()) { + failed = true; + Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + + " has a null intent, but is not a chooser"); + } } if (failed) { diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java index 6eac5e39cc63..b0689b8aab6a 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 @@ class ShortcutRequestPinProcessor { /** * 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 @@ class ShortcutRequestPinProcessor { 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 @@ class ShortcutRequestPinProcessor { /** * 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 @@ class ShortcutRequestPinProcessor { } @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 @@ class ShortcutRequestPinProcessor { } 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 @@ class ShortcutRequestPinProcessor { 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/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 057e781e76dd..74eb340b23e3 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -27,6 +27,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -64,6 +65,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.LocaleList; import android.os.Looper; +import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; @@ -1750,6 +1752,7 @@ public class ShortcutService extends IShortcutService.Stub { ps.clearAllImplicitRanks(); assignImplicitRanks(newShortcuts); + // TODO: Consider removing Chooser fields. If so, the FLAG_CHOOSER should be removed for (int i = 0; i < size; i++) { final ShortcutInfo source = newShortcuts.get(i); fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); @@ -1789,6 +1792,13 @@ public class ShortcutService extends IShortcutService.Stub { if (replacingIcon || source.hasStringResources()) { fixUpShortcutResourceNamesAndValues(target); } + + // While updating, we keep the dynamic flag as it previously was, but refresh the + // chooser flag. + // TODO: If we support clearing Chooser fields, we should also remove the flag. + if (target.getChooserIntentFilters() != null) { + target.addFlags(ShortcutInfo.FLAG_CHOOSER); + } } // Lastly, adjust the ranks. @@ -1852,6 +1862,7 @@ public class ShortcutService extends IShortcutService.Stub { return true; } + // TODO: Ensure non-launchable shortcuts can not be pinned @Override public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, IntentSender resultIntent, int userId) { @@ -2007,7 +2018,7 @@ public class ShortcutService extends IShortcutService.Stub { return getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, - ShortcutInfo::isDynamic); + ShortcutInfo::isDynamicOrChooser); } } @@ -2200,6 +2211,14 @@ public class ShortcutService extends IShortcutService.Stub { synchronized (mLock) { throwIfUserLockedL(userId); + // For the chooser, we just check is the system is calling. + // STOPSHIP: We need to implement a new permission here rather than this terrible check. + // The packageName check is to try to distinguish between when an actual + // launcher is making the call, and when it's the system. + if (isCallerSystem() && packageName.equals("android")) { + return true; + } + final ShortcutUser user = getUserShortcutsLocked(userId); // Always trust the cached component. @@ -2372,7 +2391,7 @@ public class ShortcutService extends IShortcutService.Stub { public List<ShortcutInfo> getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, + @Nullable ComponentName componentName, @Nullable Intent intent, int queryFlags, int userId) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); @@ -2394,13 +2413,13 @@ public class ShortcutService extends IShortcutService.Stub { if (packageName != null) { getShortcutsInnerLocked(launcherUserId, callingPackage, packageName, shortcutIds, changedSince, - componentName, queryFlags, userId, ret, cloneFlag); + componentName, intent, queryFlags, userId, ret, cloneFlag); } else { final List<String> shortcutIdsF = shortcutIds; getUserShortcutsLocked(userId).forAllPackages(p -> { getShortcutsInnerLocked(launcherUserId, callingPackage, p.getPackageName(), shortcutIdsF, changedSince, - componentName, queryFlags, userId, ret, cloneFlag); + componentName, intent, queryFlags, userId, ret, cloneFlag); }); } } @@ -2409,7 +2428,7 @@ public class ShortcutService extends IShortcutService.Stub { private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, - @Nullable ComponentName componentName, int queryFlags, + @Nullable ComponentName componentName, Intent intent, int queryFlags, int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { final ArraySet<String> ids = shortcutIds == null ? null : new ArraySet<>(shortcutIds); @@ -2434,6 +2453,15 @@ public class ShortcutService extends IShortcutService.Stub { return false; } } + if (intent != null + && !si.hasMatchingFilter(mContext.getContentResolver(), intent)) { + return false; + } + + if (((queryFlags & ShortcutQuery.FLAG_MATCH_CHOOSER) != 0) + && si.isChooser()) { + return true; + } if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0) && si.isDynamic()) { return true; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b76a24902f4e..d3931fba8283 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.Trace; 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.Watchdog; 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 @@ public final class PowerManagerService extends SystemService 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 @@ public final class PowerManagerService extends SystemService } } + 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 @@ public final class PowerManagerService extends SystemService 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 @@ public final class PowerManagerService extends SystemService 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 @@ public final class PowerManagerService extends SystemService } 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 70b278a6f2df..30b35f0b7605 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 @@ interface SuspendBlocker { * 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 38f5d7740842..6ee9dcd3cfdb 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.BatteryManager; 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 @@ final class WirelessChargerDetector { } } + 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 d179ea703d1e..3df4d2432bf9 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 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 474610bc6331..2f1aab66fe08 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5172,7 +5172,7 @@ public class WindowManagerService extends IWindowManager.Stub // Async Handler // ------------------------------------------------------------- - final class H extends Handler { + final class H extends android.os.Handler { public static final int REPORT_FOCUS_CHANGE = 2; public static final int REPORT_LOSING_FOCUS = 3; public static final int DO_TRAVERSAL = 4; diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 4c897058fad8..20b70a6344b4 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 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, } // TODO(b/31632518) - gnssHal = IGnss::getService("gnss"); + gnssHal = IGnss::getService(); if (gnssHal != nullptr) { auto gnssXtra = gnssHal->getExtensionXtra(); if (!gnssXtra.isOk()) { diff --git a/services/core/jni/com_android_server_vr_VrManagerService.cpp b/services/core/jni/com_android_server_vr_VrManagerService.cpp index e06e051cc870..905269792fef 100644 --- a/services/core/jni/com_android_server_vr_VrManagerService.cpp +++ b/services/core/jni/com_android_server_vr_VrManagerService.cpp @@ -38,7 +38,7 @@ static void init_native(JNIEnv* /* env */, jclass /* clazz */) { return; } - gVr = IVr::getService("vr"); + gVr = IVr::getService(); if (gVr == nullptr) { ALOGW("%s: Could not open IVr interface", __FUNCTION__); return; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7b6b94168842..17e6bba5f1c7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4851,19 +4851,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void sendPrivateKeyAliasResponse(final String alias, final IBinder responseBinder) { final IKeyChainAliasCallback keyChainAliasResponse = IKeyChainAliasCallback.Stub.asInterface(responseBinder); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... unused) { - try { - keyChainAliasResponse.alias(alias); - } catch (Exception e) { - // Catch everything (not just RemoteException): caller could throw a - // RuntimeException back across processes. - Log.e(LOG_TAG, "error while responding to callback", e); - } - return null; - } - }.execute(); + // Send the response. It's OK to do this from the main thread because IKeyChainAliasCallback + // is oneway, which means it won't block if the recipient lives in another process. + try { + keyChainAliasResponse.alias(alias); + } catch (Exception e) { + // Caller could throw RuntimeException or RemoteException back across processes. Catch + // everything just to be sure. + Log.e(LOG_TAG, "error while responding to callback", e); + } } /** 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 b26bac3188eb..0cf4994ebbce 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 @@ public class BadgeExtractorTest { 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 @@ public class BadgeExtractorTest { 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 @@ public class BadgeExtractorTest { 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 @@ public class BadgeExtractorTest { 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/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index 665f01f88b4a..4141f2f722d6 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.INetworkRecommendationProvider; 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 @@ public class NetworkScoreServiceTest { } @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 @@ public class NetworkScoreServiceTest { } @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 @@ public class NetworkScoreServiceTest { @Test public void testIsCallerActiveScorer_noBoundService() throws Exception { - mNetworkScoreService.systemRunning(); + mNetworkScoreService.onUserUnlocked(0); assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); } @@ -651,7 +650,7 @@ public class NetworkScoreServiceTest { @Test public void testGetActiveScorerPackage_notActive() throws Exception { - mNetworkScoreService.systemRunning(); + mNetworkScoreService.onUserUnlocked(0); assertNull(mNetworkScoreService.getActiveScorerPackage()); } @@ -659,7 +658,7 @@ public class NetworkScoreServiceTest { @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 @@ public class NetworkScoreServiceTest { return true; } }); - mNetworkScoreService.systemRunning(); + mNetworkScoreService.onUserUnlocked(0); } private void bindToScorer(boolean callerIsScorer) { @@ -974,7 +973,7 @@ public class NetworkScoreServiceTest { 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 index 347024d6a29d..e9a2d34b63ae 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.PackageManager; 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); - } - - 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()); + mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext, mSettingsFacade); } - 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 { + @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 */); - NetworkScorerAppData appData = - mNetworkScorerAppManager.getNetworkRecommendationProviderData(); - assertNotNull(appData); - assertEquals(recoComponent, appData.getRecommendationServiceComponent()); - assertEquals(924, appData.packageUid); + final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer(); + assertNotNull(activeScorer); + assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent()); + assertEquals(924, activeScorer.packageUid); } - public void testGetActiveScorer_providerAvailable() throws Exception { + @Test + public void testGetActiveScorer_permissionMissing() throws Exception { final ComponentName recoComponent = new ComponentName("package1", "class1"); - setNetworkRecommendationPackageNames(recoComponent.getPackageName()); - mockScoreNetworksGranted(recoComponent.getPackageName()); + setNetworkRecoPackageSetting(recoComponent.getPackageName()); + mockScoreNetworksDenied(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); + 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 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { 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 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { 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 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { 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 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { && 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/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index f615bf32911e..33e1a1652b22 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -10,7 +10,6 @@ import android.os.SystemClock; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.util.Log; -import android.util.ArraySet; import com.android.server.job.JobStore.JobSet; import com.android.server.job.controllers.JobStatus; @@ -278,6 +277,8 @@ public class JobStoreTest extends AndroidTestCase { assertEquals("Invalid charging constraint.", first.isRequireCharging(), second.isRequireCharging()); + assertEquals("Invalid battery not low constraint.", first.isRequireBatteryNotLow(), + second.isRequireBatteryNotLow()); assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(), second.isRequireDeviceIdle()); assertEquals("Invalid unmetered constraint.", diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 100338e7bfbf..1b59d7231f98 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; @@ -1324,20 +1325,23 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcut(String id) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); } @Deprecated // Title was renamed to short label. protected ShortcutInfo makeShortcutWithTitle(String id, String title) { return makeShortcut( id, title, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); } protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) { return makeShortcut( id, shortLabel, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); } /** @@ -1346,7 +1350,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) { final ShortcutInfo s = makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); s.setTimestamp(timestamp); return s; } @@ -1358,7 +1363,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { ComponentName activity) { final ShortcutInfo s = makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); s.setTimestamp(timestamp); return s; } @@ -1369,7 +1375,27 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, icon, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); + } + + protected ShortcutInfo makeChooserShortcut(String id, int i, boolean includeIntent) { + List<IntentFilter> filters = new ArrayList<>(); + List<ComponentName> componentNames = new ArrayList<>(); + for(int j = 0; j < i; j++) { + final IntentFilter filter = new IntentFilter(); + filter.addAction("view"); + filters.add(filter); + + componentNames.add(new ComponentName("xxxx", "yy" + i)); + } + Intent intent = null; + if (includeIntent) { + intent = makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class); + } + return makeShortcut( + id, "Title-" + id, /* activity =*/ null, /* icon */ null, + intent, /* rank =*/ 0, filters, componentNames); } protected ShortcutInfo makePackageShortcut(String packageName, String id) { @@ -1378,7 +1404,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { setCaller(packageName); ShortcutInfo s = makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); setCaller(origCaller); // restore the caller return s; @@ -1402,39 +1429,52 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithActivity(String id, ComponentName activity) { return makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); } protected ShortcutInfo makeShortcutWithIntent(String id, Intent intent) { return makeShortcut( id, "Title-" + id, /* activity =*/ null, /* icon =*/ null, - intent, /* rank =*/ 0); + intent, /* rank =*/ 0, /* chooserFilters =*/ null, + /* chooserComponentNames =*/ null); + } protected ShortcutInfo makeShortcutWithActivityAndTitle(String id, ComponentName activity, String title) { return makeShortcut( id, title, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0, + /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); } protected ShortcutInfo makeShortcutWithActivityAndRank(String id, ComponentName activity, int rank) { return makeShortcut( id, "Title-" + id, activity, /* icon =*/ null, - makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank); + makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank, + /* chooserFilters =*/ null, /* chooserComponentNames =*/ null); } /** * Make a shortcut with details. */ protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity, - Icon icon, Intent intent, int rank) { + Icon icon, Intent intent, int rank, @Nullable List<IntentFilter> chooserFilters, + @Nullable List<ComponentName> chooserComponentNames) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) .setShortLabel(title) - .setRank(rank) - .setIntent(intent); + .setRank(rank); + if (intent != null) { + b.setIntent(intent); + } + if (chooserFilters != null) { + for (int i = 0; i < chooserFilters.size(); i++) { + b.addChooserIntentFilter(chooserFilters.get(i), chooserComponentNames.get(i)); + } + } if (icon != null) { b.setIcon(icon); } 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 28596f7b0ac2..d8db3315d423 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -413,9 +413,7 @@ public class PackageParserTest { assertTrue(Arrays.equals(a.sharedLibraryFiles, that.sharedLibraryFiles)); assertEquals(a.dataDir, that.dataDir); assertEquals(a.deviceProtectedDataDir, that.deviceProtectedDataDir); - assertEquals(a.deviceEncryptedDataDir, that.deviceEncryptedDataDir); assertEquals(a.credentialProtectedDataDir, that.credentialProtectedDataDir); - assertEquals(a.credentialEncryptedDataDir, that.credentialEncryptedDataDir); assertEquals(a.nativeLibraryDir, that.nativeLibraryDir); assertEquals(a.secondaryNativeLibraryDir, that.secondaryNativeLibraryDir); assertEquals(a.nativeLibraryRootDir, that.nativeLibraryRootDir); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index e4d92ba7a38c..94ff07fb9d71 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -15,6 +15,7 @@ */ package com.android.server.pm; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllChooser; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned; @@ -256,7 +257,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo si2 = makeShortcut( "shortcut2", @@ -264,14 +267,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12); + /* weight */ 12, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo si3 = makeShortcut( "shortcut3", "Title 3", /* activity */ null, icon3, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 13); + /* weight */ 13, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3))); assertShortcutIds(assertAllNotKeyFieldsOnly( @@ -982,8 +989,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"), - makeShortcut("s5") - ))); + makeShortcut("s5"), + makeChooserShortcut("s6", 2, true), + makeChooserShortcut("s7", 2, true), + makeChooserShortcut("s8", 1, true)))); }); runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -991,11 +1000,13 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"), - makeShortcut("s5") - ))); + makeShortcut("s5"), + makeChooserShortcut("s6", 2, true), + makeChooserShortcut("s7", 2, true), + makeChooserShortcut("s8", 1, true)))); }); runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> { - mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3", "s6"), getCallingUser()); mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"), getCallingUser()); @@ -1008,19 +1019,20 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.removeDynamicShortcuts(list("s1")); mManager.removeDynamicShortcuts(list("s3")); mManager.removeDynamicShortcuts(list("s5")); + mManager.removeDynamicShortcuts(list("s7")); }); runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s3", "s4", "s5"); + "s3", "s4", "s5", "s6", "s7", "s8"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), - "s2", "s3"); + "s2", "s3", "s6"); }); runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s2", "s4"); + "s2", "s4", "s6", "s8"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), "s4", "s5"); @@ -1057,10 +1069,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s3", "s4", "s5"); + "s3", "s4", "s5", "s6", "s7", "s8"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), - "s2", "s3"); + "s2", "s3", "s6"); ShortcutInfo s = getCallerShortcut("s2"); assertTrue(s.hasIconResource()); @@ -1076,7 +1088,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { assertShortcutIds(assertAllDynamic( mManager.getDynamicShortcuts()), - "s2", "s4"); + "s2", "s4", "s6", "s8"); assertShortcutIds(assertAllPinned( mManager.getPinnedShortcuts()), "s4", "s5"); @@ -1173,7 +1185,46 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - // === Test for launcher side APIs === + public void testUpdateShortcuts_chooser() { + runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { + assertTrue(mManager.setDynamicShortcuts(list( + makeShortcut("s1"), + makeChooserShortcut("s2", 2, false), + makeChooserShortcut("s3", 2, false) + ))); + + assertFalse(getCallerShortcut("s1").isChooser()); + assertTrue(getCallerShortcut("s2").isChooser()); + assertTrue(getCallerShortcut("s3").isChooser()); + + ShortcutInfo s = getCallerShortcut("s1"); + assertNull(s.getChooserIntentFilters()); + assertNull(s.getChooserComponentNames()); + + assertTrue(getCallerShortcut("s1").isDynamic()); + assertFalse(getCallerShortcut("s2").isDynamic()); + assertFalse(getCallerShortcut("s3").isDynamic()); + + + // Replace 2 with a chooser shortcut + mManager.updateShortcuts(list(makeChooserShortcut("s1", 2, true))); + + s = getCallerShortcut("s1"); + assertEquals(2, s.getChooserIntentFilters().length); + assertEquals(2, s.getChooserComponentNames().length); + + assertShortcutIds(assertAllChooser( + mManager.getDynamicShortcuts()), + "s1", "s2", "s3"); + + assertTrue(getCallerShortcut("s1").isDynamic()); + assertFalse(getCallerShortcut("s2").isDynamic()); + assertFalse(getCallerShortcut("s3").isDynamic()); + }); + } + + + // === Test for launcher side APIs === public void testGetShortcuts() { @@ -1484,15 +1535,17 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo s1_2 = makeShortcut( - "s2", - "Title 2", + "s2", "Title 2", /* activity */ null, /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12); + /* weight */ 12, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2))); dumpsysOnLogcat(); @@ -1505,7 +1558,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(s2_1))); dumpsysOnLogcat(); @@ -2674,10 +2729,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { final ShortcutInfo s1_2 = makeShortcut( "s2", "Title 2", - /* activity */ null, - /* icon =*/ null, + /* activity */ null, + /* icon =*/ null, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* rank */ 12); + /* rank */ 12, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo s1_3 = makeShortcut("s3"); @@ -2692,7 +2749,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* icon =*/ null, makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(s2_1))); }); @@ -3110,7 +3169,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3118,7 +3179,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12); + /* weight */ 12, + /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -3136,8 +3199,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { makeComponent(ShortcutActivity.class), icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, - "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + "key1", "val1", "nest", makeBundle("key", 123)), /* weight */ 10, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3145,7 +3208,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12); + /* weight */ 12, /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -3167,7 +3231,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { icon1, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class, "key1", "val1", "nest", makeBundle("key", 123)), - /* weight */ 10); + /* weight */ 10, /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); final ShortcutInfo si2 = makeShortcut( "s2", @@ -3175,7 +3240,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /* activity */ null, icon2, makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class), - /* weight */ 12); + /* weight */ 12, /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null); assertTrue(mManager.setDynamicShortcuts(list(si1, si2))); @@ -6800,10 +6866,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.setDynamicShortcuts(list( makeShortcut("ms1", "title1", new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - /* icon */ null, new Intent("action1"), /* rank */ 0), + /* icon */ null, new Intent("action1"), /* rank */ 0, + /* chooserFilter=*/ null, /* chooserComponentNames=*/ null), makeShortcut("ms2", "title2", new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), - /* icon */ null, new Intent("action1"), /* rank */ 0))); + /* icon */ null, new Intent("action1"), /* rank */ 0, /* chooserFilter=*/ null, + /* chooserComponentNames=*/ null))); }); runWithCaller(LAUNCHER_1, USER_0, () -> { diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 28ec4fd27ccc..c54fa02b68ef 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -34,6 +34,7 @@ import android.Manifest.permission; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.BitmapFactory; @@ -93,11 +94,6 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertExpectException( RuntimeException.class, - "intents cannot contain null", - () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null)); - - assertExpectException( - RuntimeException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); @@ -142,6 +138,19 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { "disabledMessage cannot be empty", () -> new ShortcutInfo.Builder(getTestContext(), "id").setDisabledMessage("")); + + assertExpectException( + RuntimeException.class, + "component name cannot be null", + () -> new ShortcutInfo.Builder(getTestContext(), "id") + .addChooserIntentFilter(new IntentFilter(Intent.ACTION_SEND), null)); + + assertExpectException( + RuntimeException.class, + "intent filter cannot be null", + () -> new ShortcutInfo.Builder(getTestContext(), "id") + .addChooserIntentFilter(null, new ComponentName("xxx", "s"))); + assertExpectException(NullPointerException.class, "action must be set", () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent())); @@ -240,6 +249,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); + IntentFilter chooserFilter = new IntentFilter(); + chooserFilter.addAction(Intent.ACTION_VIEW); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("l", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") @@ -252,6 +265,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) + .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) + .setChooserExtras(pb2) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -282,6 +297,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getTextResName()); assertEquals(0, si.getDisabledMessageResourceId()); assertEquals(null, si.getDisabledMessageResName()); + + assertEquals(1, si.getChooserIntentFilters().length); + assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); + assertEquals(1, si.getChooserComponentNames().length); + assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); + assertEquals(1, si.getChooserExtras().getInt("l")); } public void testShortcutInfoParcel_resId() { @@ -290,6 +311,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); + IntentFilter chooserFilter = new IntentFilter(); + chooserFilter.addAction(Intent.ACTION_VIEW); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("l", 1); si = new ShortcutInfo.Builder(getTestContext()) .setId("id") @@ -302,6 +327,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setRank(123) .setExtras(pb) + .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) + .setChooserExtras(pb2) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -338,6 +365,11 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); + IntentFilter chooserFilter = new IntentFilter(); + chooserFilter.addAction(Intent.ACTION_VIEW); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("l", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -349,6 +381,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) + .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) + .setChooserExtras(pb2) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); @@ -378,6 +412,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); + assertEquals(1, si.getChooserIntentFilters().length); + assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); + assertEquals(1, si.getChooserComponentNames().length); + assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); + assertEquals(1, si.getChooserExtras().getInt("l")); + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); @@ -445,6 +485,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); + IntentFilter chooserFilter = new IntentFilter(); + chooserFilter.addAction(Intent.ACTION_VIEW); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("l", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -456,6 +500,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) + .addChooserIntentFilter(chooserFilter, new ComponentName("a", "b")) + .setChooserExtras(pb2) .build(); sorig.addFlags(ShortcutInfo.FLAG_PINNED); sorig.setBitmapPath("abc"); @@ -488,6 +534,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(456, si.getIconResourceId()); assertEquals("string/r456", si.getIconResName()); + assertEquals(1, si.getChooserIntentFilters().length); + assertEquals(Intent.ACTION_VIEW, si.getChooserIntentFilters()[0].getAction(0)); + assertEquals(1, si.getChooserComponentNames().length); + assertEquals(new ComponentName("a", "b"), si.getChooserComponentNames()[0]); + assertEquals(1, si.getChooserExtras().getInt("l")); + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); assertEquals(mClientContext.getPackageName(), si.getPackage()); @@ -603,6 +655,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException { PersistableBundle pb = new PersistableBundle(); pb.putInt("k", 1); + IntentFilter chooserFilter = new IntentFilter(); + chooserFilter.addAction(Intent.ACTION_VIEW); + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("l", 1); ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext()) .setId("id") .setActivity(new ComponentName("a", "b")) @@ -714,12 +770,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(999, si.getRank()); - PersistableBundle pb2 = new PersistableBundle(); - pb2.putInt("x", 99); + PersistableBundle pb3 = new PersistableBundle(); + pb3.putInt("x", 99); si = sorig.clone(/* flags=*/ 0); si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id") - .setExtras(pb2).build()); + .setExtras(pb3).build()); assertEquals("text", si.getText()); assertEquals(99, si.getExtras().getInt("x")); } 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 0bd014c874b5..42ddedf0b340 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 @@ public class AppIdleHistoryTests extends AndroidTestCase { 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 class AppIdleHistoryTests extends AndroidTestCase { 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/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index ea45bd17b216..fd335c3041fb 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -508,6 +508,13 @@ public class ShortcutManagerTestUtils { return actualShortcuts; } + public static List<ShortcutInfo> assertAllChooser(List<ShortcutInfo> actualShortcuts) { + for (ShortcutInfo s : actualShortcuts) { + assertTrue("ID " + s.getId(), s.isChooser()); + } + return actualShortcuts; + } + public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) { for (ShortcutInfo s : actualShortcuts) { assertTrue("ID " + s.getId(), s.isPinned()); diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index f69dae44a316..f2985597573f 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 @@ package com.android.server.usage; 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 @@ public class AppIdleHistory { mElapsedSnapshot = elapsedRealtime; mScreenOnSnapshot = elapsedRealtime; mStorageDir = storageDir; - readScreenOnTimeLocked(); + readScreenOnTime(); } public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) { @@ -109,7 +108,7 @@ public class AppIdleHistory { 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 class AppIdleHistory { } } - public long getScreenOnTimeLocked(long elapsedRealtime) { + public long getScreenOnTime(long elapsedRealtime) { long screenOnTime = mScreenOnDuration; if (mScreenOn) { screenOnTime += elapsedRealtime - mScreenOnSnapshot; @@ -135,7 +134,7 @@ public class AppIdleHistory { return new File(mStorageDir, "screen_on_time"); } - private void readScreenOnTimeLocked() { + private void readScreenOnTime() { File screenOnTimeFile = getScreenOnTimeFile(); if (screenOnTimeFile.exists()) { try { @@ -146,11 +145,11 @@ public class AppIdleHistory { } 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 @@ public class AppIdleHistory { /** * 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 @@ public class AppIdleHistory { 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 @@ public class AppIdleHistory { 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 @@ public class AppIdleHistory { 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 class AppIdleHistory { } } - public void writeAppIdleTimesLocked(int userId) { + public void writeAppIdleTimes(int userId) { FileOutputStream fos = null; AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); try { @@ -346,7 +345,7 @@ public class AppIdleHistory { 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 @@ public class AppIdleHistory { 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 @@ public class AppIdleHistory { 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 @@ public class AppIdleHistory { 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 3c743b5cb355..469a8f144cb3 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { cleanUpRemovedUsersLocked(); + } + synchronized (mAppIdleLock) { mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime()); } @@ -234,8 +240,8 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements @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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } void setChargingState(boolean charging) { - synchronized (mLock) { + synchronized (mAppIdleLock) { if (mCharging != charging) { mCharging = charging; postParoleStateChanged(); @@ -439,15 +447,16 @@ public class UsageStatsService extends SystemService implements /** 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements /** 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements continue; } if (!packageName.equals(providerPkgName)) { - forceIdleState(packageName, userId, false); + setAppIdleAsync(packageName, false, userId); } } catch (NameNotFoundException e) { // Shouldn't happen @@ -728,25 +750,28 @@ public class UsageStatsService extends SystemService implements * 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements } void removeListener(AppIdleStateChangeListener listener) { - synchronized (mLock) { + synchronized (mAppIdleLock) { mPackageAccessListeners.remove(listener); } } @@ -988,7 +1015,7 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } private boolean isCarrierApp(String packageName) { - synchronized (mLock) { + synchronized (mAppIdleLock) { if (!mHaveCarrierPrivilegedApps) { - fetchCarrierPrivilegedAppsLocked(); + fetchCarrierPrivilegedAppsLA(); } if (mCarrierPrivilegedApps != null) { return mCarrierPrivilegedApps.contains(packageName); @@ -1027,13 +1054,14 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements @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 d9f352cbbf7b..8d335a51bb55 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 @@ class UserUsageStatsService { pw.decreaseIndent(); pw.println(); - pw.increaseIndent(); pw.println("ChooserCounts"); pw.increaseIndent(); for (UsageStats usageStats : pkgStats.values()) { @@ -553,6 +552,7 @@ class UserUsageStatsService { } pw.println(); } + pw.decreaseIndent(); pw.println("configurations"); pw.increaseIndent(); diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java index 216603cad5ee..e0e3a085315a 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -235,7 +235,7 @@ public class VideoProfile implements Parcelable { StringBuilder sb = new StringBuilder(); sb.append("Audio"); - if (isAudioOnly(videoState)) { + if (videoState == STATE_AUDIO_ONLY) { sb.append(" Only"); } else { if (isTransmissionEnabled(videoState)) { @@ -256,6 +256,9 @@ public class VideoProfile implements Parcelable { /** * Indicates whether the video state is audio only. + * <p> + * Note: Considers only whether either both the {@link #STATE_RX_ENABLED} or + * {@link #STATE_TX_ENABLED} bits are off, but not {@link #STATE_PAUSED}. * * @param videoState The video state. * @return {@code True} if the video state is audio only, {@code false} otherwise. diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 6f51c6e3e461..cc21b5ec116d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1140,6 +1140,8 @@ public class PhoneNumberUtils private static final String KOREA_ISO_COUNTRY_CODE = "KR"; + private static final String JAPAN_ISO_COUNTRY_CODE = "JP"; + /** * Breaks the given number down and formats it according to the rules * for the country the number is from. @@ -1460,15 +1462,25 @@ public class PhoneNumberUtils String result = null; try { PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso); - /** - * Need to reformat any local Korean phone numbers (when the user is in Korea) with - * country code to corresponding national format which would replace the leading - * +82 with 0. - */ - if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) && + + if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) && (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) && (pn.getCountryCodeSource() == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) { + /** + * Need to reformat any local Korean phone numbers (when the user is in Korea) with + * country code to corresponding national format which would replace the leading + * +82 with 0. + */ + result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL); + } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) && + pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) && + (pn.getCountryCodeSource() == + PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) { + /** + * Need to reformat Japanese phone numbers (when user is in Japan) with the national + * dialing format. + */ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL); } else { result = util.formatInOriginalFormat(pn, defaultCountryIso); diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index fe8dbfbd9f24..406f01e82c35 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -41,6 +41,9 @@ import com.android.ims.internal.IImsServiceFeatureListener; import com.android.ims.internal.IImsUt; import com.android.internal.annotations.VisibleForTesting; +import static android.Manifest.permission.MODIFY_PHONE_STATE; +import static android.Manifest.permission.READ_PHONE_STATE; + /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend * ImsService must register the service in their AndroidManifest to be detected by the framework. @@ -94,6 +97,7 @@ public abstract class ImsService extends ImsServiceBase { public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c) throws RemoteException { synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature"); onCreateImsFeatureInternal(slotId, feature, c); } } @@ -101,6 +105,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void removeImsFeature(int slotId, int feature) throws RemoteException { synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature"); onRemoveImsFeatureInternal(slotId, feature); } } @@ -108,6 +113,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent, IImsRegistrationListener listener) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -120,6 +126,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void endSession(int slotId, int featureType, int sessionId) throws RemoteException { synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession"); MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { feature.endSession(sessionId); @@ -130,6 +137,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public boolean isConnected(int slotId, int featureType, int callSessionType, int callType) throws RemoteException { + enforceCallingOrSelfPermission(READ_PHONE_STATE, "isConnected"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -141,6 +149,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public boolean isOpened(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(READ_PHONE_STATE, "isOpened"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -152,6 +161,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public int getFeatureStatus(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(READ_PHONE_STATE, "getFeatureStatus"); int status = ImsFeature.STATE_NOT_AVAILABLE; synchronized (mFeatures) { SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); @@ -168,6 +178,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void addRegistrationListener(int slotId, int featureType, IImsRegistrationListener listener) throws RemoteException { + enforceCallingOrSelfPermission(READ_PHONE_STATE, "addRegistrationListener"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -179,6 +190,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void removeRegistrationListener(int slotId, int featureType, IImsRegistrationListener listener) throws RemoteException { + enforceCallingOrSelfPermission(READ_PHONE_STATE, "removeRegistrationListener"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -190,6 +202,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, int callSessionType, int callType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -202,6 +215,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsCallSession createCallSession(int slotId, int featureType, int sessionId, ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -214,6 +228,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId, String callId) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -226,6 +241,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsUt getUtInterface(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -238,6 +254,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsConfig getConfigInterface(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -249,6 +266,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void turnOnIms(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -259,6 +277,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void turnOffIms(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -270,6 +289,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsEcbm getEcbmInterface(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -282,6 +302,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { @@ -293,6 +314,7 @@ public abstract class ImsService extends ImsServiceBase { @Override public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface"); synchronized (mFeatures) { MMTelFeature feature = resolveMMTelFeature(slotId, featureType); if (feature != null) { diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index dc8dfba806dc..bcaac6e77bdd 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -270,7 +270,7 @@ public class TelephonyIntents { * Used in OMA-DM applications. */ public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION - = "android.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION"; + = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION"; /** * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml index 0fd8b12fafd7..2c6c8d7cae20 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/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java index 174bbcf27806..cc7631ac939a 100644 --- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java @@ -571,6 +571,8 @@ public class VectorDrawable_Delegate { @NonNull public Consumer<Float> getFloatPropertySetter(int propertyIdx) { switch (propertyIdx) { + case STROKE_WIDTH_INDEX: + return this::setStrokeWidth; case STROKE_ALPHA_INDEX: return this::setStrokeAlpha; case FILL_ALPHA_INDEX: diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png Binary files differindex a53dcbae327b..f15d669750a7 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml index 05a3665deab1..456b5f645945 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml @@ -263,7 +263,8 @@ android:id="@id/radioButton2" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="New RadioButton" /> + android:text="New RadioButton" + android:checked="true" /> </RadioGroup> diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index adf189b64544..57b98e984f17 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -31,8 +31,7 @@ import java.lang.ref.WeakReference; * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This * class provides functionality common to both publish and subscribe discovery sessions: * <ul> - * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} or - * {@link #sendMessage(PeerHandle, int, byte[], int)} methods. + * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])}. * <li>Creating a network-specifier when requesting a Aware connection: * {@link #createNetworkSpecifier(PeerHandle, byte[])}. * </ul> @@ -62,6 +61,8 @@ public class DiscoverySession { * {@link #sendMessage(PeerHandle, int, byte[], int)}. * * @return Maximum retry count when sending messages. + * + * @hide */ public static int getMaxSendRetryCount() { return MAX_SEND_RETRY_COUNT; @@ -163,6 +164,8 @@ public class DiscoverySession { * or MAC level) retries should be attempted if there is no ACK from the receiver * (note: no retransmissions are attempted in other failure cases). A value of 0 * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. + * + * @hide */ public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, @Nullable byte[] message, int retryCount) { @@ -195,8 +198,6 @@ public class DiscoverySession { * The peer will get a callback indicating a message was received using * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. - * Equivalent to {@link #sendMessage(PeerHandle, int, byte[], int)} - * with a {@code retryCount} of 0. * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java index 33da1823c8b8..9645b1d32a34 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java @@ -124,10 +124,9 @@ public class DiscoverySessionCallback { } /** - * Called when message transmission fails - when no ACK is received from the peer. - * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using - * the {@link DiscoverySession#sendMessage(PeerHandle, int, - * byte[], int)} method) - this event is received after all retries are exhausted. + * Called when message transmission initiated with + * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} fails. E.g. when no ACK is + * received from the peer. * <p> * Note that either this callback or * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)} will be received @@ -141,9 +140,7 @@ public class DiscoverySessionCallback { /** * Called when a message is received from a discovery session peer - in response to the - * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, - * byte[])} or {@link DiscoverySession#sendMessage(PeerHandle, - * int, byte[], int)}. + * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}. * * @param peerHandle An opaque handle to the peer matching our discovery operation. * @param message A byte array containing the message. |