Merge "Fix issue where we weren't dismissing the pip menu activity." into oc-dev
diff --git a/Android.mk b/Android.mk
index b8b85ba..92bd03e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -569,11 +569,11 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
framework-protos \
- android.hardware.health@1.0-java-constants \
- android.hardware.thermal@1.0-java-constants \
- android.hardware.tv.input@1.0-java-constants \
- android.hardware.usb@1.0-java-constants \
- android.hardware.vibrator@1.0-java-constants \
+ android.hardware.health-V1.0-java-constants \
+ android.hardware.thermal-V1.0-java-constants \
+ android.hardware.tv.input-V1.0-java-constants \
+ android.hardware.usb-V1.0-java-constants \
+ android.hardware.vibrator-V1.0-java-constants \
# Loaded with System.loadLibrary by android.view.textclassifier
LOCAL_REQUIRED_MODULES += libtextclassifier
diff --git a/api/current.txt b/api/current.txt
index 45fb4d9..501ca61 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2999,6 +2999,7 @@
method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ field public static final java.lang.String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator";
@@ -9348,6 +9349,7 @@
field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
+ field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED";
field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
@@ -9464,6 +9466,7 @@
field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
+ field public static final java.lang.String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -10361,12 +10364,10 @@
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 deprecated 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 deprecated 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
@@ -10920,9 +10921,6 @@
method public int describeContents();
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public deprecated android.content.ComponentName[] getChooserComponentNames();
- method public deprecated android.os.PersistableBundle getChooserExtras();
- method public deprecated android.content.IntentFilter[] getChooserIntentFilters();
method public java.lang.CharSequence getDisabledMessage();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
@@ -10935,7 +10933,6 @@
method public java.lang.CharSequence getShortLabel();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
- method public deprecated boolean isChooser();
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
@@ -10948,11 +10945,9 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
- method public deprecated 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 deprecated 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);
@@ -13762,7 +13757,7 @@
}
public class Typeface {
- method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
+ method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
method public static android.graphics.Typeface create(java.lang.String, int);
method public static android.graphics.Typeface create(android.graphics.Typeface, int);
method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -13789,6 +13784,7 @@
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
+ method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);
@@ -13796,7 +13792,7 @@
method public android.graphics.Typeface.Builder setWeight(int);
}
- public static abstract interface Typeface.FontRequestCallback {
+ public static abstract deprecated interface Typeface.FontRequestCallback {
method public abstract void onTypefaceRequestFailed(int);
method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
@@ -14864,15 +14860,16 @@
field public final int type;
}
- public final class SensorDirectChannel implements java.lang.AutoCloseable {
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
method public void close();
- method public boolean isValid();
+ method public int configure(android.hardware.Sensor, int);
+ method public boolean isOpen();
field public static final int RATE_FAST = 2; // 0x2
field public static final int RATE_NORMAL = 1; // 0x1
field public static final int RATE_STOP = 0; // 0x0
field public static final int RATE_VERY_FAST = 3; // 0x3
- field public static final int TYPE_ASHMEM = 1; // 0x1
field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+ field public static final int TYPE_MEMORY_FILE = 1; // 0x1
}
public class SensorEvent {
@@ -14906,7 +14903,6 @@
public abstract class SensorManager {
method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
method public boolean flush(android.hardware.SensorEventListener);
@@ -15755,7 +15751,6 @@
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
- field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
@@ -20583,7 +20578,7 @@
method public double getAccumulatedDeltaRangeMeters();
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
- method public double getAutomaticGainControlLevelInDb();
+ method public double getAutomaticGainControlLevelDb();
method public long getCarrierCycles();
method public float getCarrierFrequencyHz();
method public double getCarrierPhase();
@@ -20599,7 +20594,7 @@
method public int getState();
method public int getSvid();
method public double getTimeOffsetNanos();
- method public boolean hasAutomaticGainControlLevelInDb();
+ method public boolean hasAutomaticGainControlLevelDb();
method public boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
method public boolean hasCarrierPhase();
@@ -20693,7 +20688,7 @@
method public int getSatelliteCount();
method public int getSvid(int);
method public boolean hasAlmanacData(int);
- method public boolean hasCarrierFrequency(int);
+ method public boolean hasCarrierFrequencyHz(int);
method public boolean hasEphemerisData(int);
method public boolean usedInFix(int);
field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -22354,7 +22349,7 @@
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
- field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
+ field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
@@ -24521,14 +24516,14 @@
method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
method public static boolean isChannelUriForTunerInput(android.net.Uri);
method public static boolean isProgramUri(android.net.Uri);
+ method public static void requestChannelBrowsable(android.content.Context, long);
field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
- field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+ field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
field public static final java.lang.String AUTHORITY = "android.media.tv";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
- field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
}
@@ -31927,7 +31922,7 @@
field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
- field public static final int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
+ field public static final deprecated int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
@@ -34505,6 +34500,7 @@
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String);
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]);
method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler);
}
public static final class FontsContract.Columns implements android.provider.BaseColumns {
@@ -34538,6 +34534,18 @@
method public boolean isItalic();
}
+ public static class FontsContract.FontRequestCallback {
+ ctor public FontsContract.FontRequestCallback();
+ method public void onTypefaceRequestFailed(int);
+ method public void onTypefaceRetrieved(android.graphics.Typeface);
+ field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+ field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+ field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+ field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+ field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+ field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+ }
+
public final deprecated class LiveFolders implements android.provider.BaseColumns {
field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
field public static final java.lang.String DESCRIPTION = "description";
@@ -43875,7 +43883,6 @@
method public boolean isValid();
method public boolean isWideColorGamut();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
- field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int FLAG_PRESENTATION = 8; // 0x8
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
diff --git a/api/removed.txt b/api/removed.txt
index d20c08c..82705fd 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -100,6 +100,18 @@
}
+package android.hardware {
+
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
+ method public deprecated boolean isValid();
+ }
+
+ public abstract class SensorManager {
+ method public deprecated int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ae3f51..99a50d8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3123,6 +3123,7 @@
method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ field public static final java.lang.String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator";
@@ -9874,6 +9875,7 @@
field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
+ field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED";
field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
@@ -10012,6 +10014,7 @@
field public static final deprecated java.lang.String EXTRA_EMERGENCY_ONLY = "emergencyOnly";
field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
+ field public static final java.lang.String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -11020,12 +11023,10 @@
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 deprecated 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 deprecated 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
@@ -11673,9 +11674,6 @@
method public int describeContents();
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public deprecated android.content.ComponentName[] getChooserComponentNames();
- method public deprecated android.os.PersistableBundle getChooserExtras();
- method public deprecated android.content.IntentFilter[] getChooserIntentFilters();
method public java.lang.CharSequence getDisabledMessage();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
@@ -11688,7 +11686,6 @@
method public java.lang.CharSequence getShortLabel();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
- method public deprecated boolean isChooser();
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
@@ -11701,11 +11698,9 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
- method public deprecated 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 deprecated 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);
@@ -14529,7 +14524,7 @@
}
public class Typeface {
- method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
+ method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
method public static android.graphics.Typeface create(java.lang.String, int);
method public static android.graphics.Typeface create(android.graphics.Typeface, int);
method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -14556,6 +14551,7 @@
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
+ method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);
@@ -14563,7 +14559,7 @@
method public android.graphics.Typeface.Builder setWeight(int);
}
- public static abstract interface Typeface.FontRequestCallback {
+ public static abstract deprecated interface Typeface.FontRequestCallback {
method public abstract void onTypefaceRequestFailed(int);
method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
@@ -15637,15 +15633,16 @@
field public final int type;
}
- public final class SensorDirectChannel implements java.lang.AutoCloseable {
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
method public void close();
- method public boolean isValid();
+ method public int configure(android.hardware.Sensor, int);
+ method public boolean isOpen();
field public static final int RATE_FAST = 2; // 0x2
field public static final int RATE_NORMAL = 1; // 0x1
field public static final int RATE_STOP = 0; // 0x0
field public static final int RATE_VERY_FAST = 3; // 0x3
- field public static final int TYPE_ASHMEM = 1; // 0x1
field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+ field public static final int TYPE_MEMORY_FILE = 1; // 0x1
}
public class SensorEvent {
@@ -15679,7 +15676,6 @@
public abstract class SensorManager {
method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
method public boolean flush(android.hardware.SensorEventListener);
@@ -16541,7 +16537,6 @@
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
- field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
@@ -22083,7 +22078,7 @@
method public double getAccumulatedDeltaRangeMeters();
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
- method public double getAutomaticGainControlLevelInDb();
+ method public double getAutomaticGainControlLevelDb();
method public long getCarrierCycles();
method public float getCarrierFrequencyHz();
method public double getCarrierPhase();
@@ -22099,7 +22094,7 @@
method public int getState();
method public int getSvid();
method public double getTimeOffsetNanos();
- method public boolean hasAutomaticGainControlLevelInDb();
+ method public boolean hasAutomaticGainControlLevelDb();
method public boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
method public boolean hasCarrierPhase();
@@ -22193,7 +22188,7 @@
method public int getSatelliteCount();
method public int getSvid(int);
method public boolean hasAlmanacData(int);
- method public boolean hasCarrierFrequency(int);
+ method public boolean hasCarrierFrequencyHz(int);
method public boolean hasEphemerisData(int);
method public boolean usedInFix(int);
field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -24183,7 +24178,7 @@
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
- field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
+ field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
@@ -26495,12 +26490,15 @@
method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
method public static boolean isChannelUriForTunerInput(android.net.Uri);
method public static boolean isProgramUri(android.net.Uri);
+ method public static void requestChannelBrowsable(android.content.Context, long);
+ field public static final java.lang.String ACTION_CHANNEL_BROWSABLE_REQUESTED = "android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED";
field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
- field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+ field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
field public static final java.lang.String AUTHORITY = "android.media.tv";
+ field public static final java.lang.String EXTRA_BLOCKED_PACKAGES = "android.media.tv.extra.BLOCKED_PACKAGES";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
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";
@@ -26508,9 +26506,16 @@
field public static final java.lang.String EXTRA_EXISTING_COLUMN_NAMES = "android.media.tv.extra.EXISTING_COLUMN_NAMES";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
+ field public static final java.lang.String EXTRA_RESULT_CODE = "android.media.tv.extra.RESULT_CODE";
field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
field public static final java.lang.String METHOD_ADD_COLUMN = "add_column";
+ field public static final java.lang.String METHOD_BLOCK_PACKAGE = "block_package";
+ field public static final java.lang.String METHOD_GET_BLOCKED_PACKAGES = "get_blocked_packages";
field public static final java.lang.String METHOD_GET_COLUMNS = "get_columns";
+ field public static final java.lang.String METHOD_UNBLOCK_PACKAGE = "unblock_package";
+ field public static final int RESULT_ERROR_INVALID_ARGUMENT = 2; // 0x2
+ field public static final int RESULT_ERROR_IO = 1; // 0x1
+ field public static final int RESULT_OK = 0; // 0x0
}
public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns {
@@ -26541,7 +26546,6 @@
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
- field public static final java.lang.String COLUMN_SYSTEM_APPROVED = "system_approved";
field public static final java.lang.String COLUMN_TRANSIENT = "transient";
field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
field public static final java.lang.String COLUMN_TYPE = "type";
@@ -34812,7 +34816,7 @@
field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
- field public static final int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
+ field public static final deprecated int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
@@ -37482,6 +37486,7 @@
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String);
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]);
method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler);
}
public static final class FontsContract.Columns implements android.provider.BaseColumns {
@@ -37515,6 +37520,18 @@
method public boolean isItalic();
}
+ public static class FontsContract.FontRequestCallback {
+ ctor public FontsContract.FontRequestCallback();
+ method public void onTypefaceRequestFailed(int);
+ method public void onTypefaceRetrieved(android.graphics.Typeface);
+ field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+ field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+ field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+ field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+ field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+ field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+ }
+
public final deprecated class LiveFolders implements android.provider.BaseColumns {
field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
field public static final java.lang.String DESCRIPTION = "description";
@@ -43666,7 +43683,7 @@
method public boolean isSmsCapable();
method public boolean isTtyModeSupported();
method public boolean isVideoCallingEnabled();
- method public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
+ method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -43683,7 +43700,7 @@
method public boolean setPreferredNetworkTypeToGlobal();
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
- method public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
@@ -47432,7 +47449,6 @@
method public boolean isValid();
method public boolean isWideColorGamut();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
- field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int FLAG_PRESENTATION = 8; // 0x8
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1effe9c..bdcafae 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -98,6 +98,18 @@
}
+package android.hardware {
+
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
+ method public deprecated boolean isValid();
+ }
+
+ public abstract class SensorManager {
+ method public deprecated int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index fd8c2d0..a113cc1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2999,6 +2999,7 @@
method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ field public static final java.lang.String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator";
@@ -9382,6 +9383,7 @@
field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
+ field public static final java.lang.String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED";
field public static final java.lang.String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH";
field public static final java.lang.String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED";
field public static final deprecated java.lang.String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
@@ -9498,6 +9500,7 @@
field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
+ field public static final java.lang.String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -10398,12 +10401,10 @@
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 deprecated 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 deprecated 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
@@ -10961,9 +10962,6 @@
method public int describeContents();
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public deprecated android.content.ComponentName[] getChooserComponentNames();
- method public deprecated android.os.PersistableBundle getChooserExtras();
- method public deprecated android.content.IntentFilter[] getChooserIntentFilters();
method public java.lang.CharSequence getDisabledMessage();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
@@ -10976,7 +10974,6 @@
method public java.lang.CharSequence getShortLabel();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
- method public deprecated boolean isChooser();
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
@@ -10989,11 +10986,9 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
- method public deprecated 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 deprecated 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);
@@ -11781,15 +11776,6 @@
field protected final java.util.ArrayList<T> mObservers;
}
- public final class PageViewCursor extends android.database.CursorWrapper implements android.database.CrossProcessCursor {
- ctor public PageViewCursor(android.database.Cursor, android.os.Bundle);
- method public void fillWindow(int, android.database.CursorWindow);
- method public android.database.CursorWindow getWindow();
- method public boolean onMove(int, int);
- method public static android.database.Cursor wrap(android.database.Cursor, android.os.Bundle);
- field public static final java.lang.String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
- }
-
public class SQLException extends java.lang.RuntimeException {
ctor public SQLException();
ctor public SQLException(java.lang.String);
@@ -13813,7 +13799,7 @@
}
public class Typeface {
- method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
+ method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
method public static android.graphics.Typeface create(java.lang.String, int);
method public static android.graphics.Typeface create(android.graphics.Typeface, int);
method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -13840,6 +13826,7 @@
ctor public Typeface.Builder(java.lang.String);
ctor public Typeface.Builder(android.content.res.AssetManager, java.lang.String);
method public android.graphics.Typeface build();
+ method public android.graphics.Typeface.Builder setFallback(java.lang.String);
method public android.graphics.Typeface.Builder setFontVariationSettings(java.lang.String) throws android.graphics.fonts.FontVariationAxis.InvalidFormatException;
method public android.graphics.Typeface.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
method public android.graphics.Typeface.Builder setItalic(boolean);
@@ -13847,7 +13834,7 @@
method public android.graphics.Typeface.Builder setWeight(int);
}
- public static abstract interface Typeface.FontRequestCallback {
+ public static abstract deprecated interface Typeface.FontRequestCallback {
method public abstract void onTypefaceRequestFailed(int);
method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
@@ -14918,15 +14905,16 @@
field public final int type;
}
- public final class SensorDirectChannel implements java.lang.AutoCloseable {
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
method public void close();
- method public boolean isValid();
+ method public int configure(android.hardware.Sensor, int);
+ method public boolean isOpen();
field public static final int RATE_FAST = 2; // 0x2
field public static final int RATE_NORMAL = 1; // 0x1
field public static final int RATE_STOP = 0; // 0x0
field public static final int RATE_VERY_FAST = 3; // 0x3
- field public static final int TYPE_ASHMEM = 1; // 0x1
field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+ field public static final int TYPE_MEMORY_FILE = 1; // 0x1
}
public class SensorEvent {
@@ -14960,7 +14948,6 @@
public abstract class SensorManager {
method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
method public boolean flush(android.hardware.SensorEventListener);
@@ -15813,7 +15800,6 @@
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
- field public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
@@ -20661,7 +20647,7 @@
method public double getAccumulatedDeltaRangeMeters();
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
- method public double getAutomaticGainControlLevelInDb();
+ method public double getAutomaticGainControlLevelDb();
method public long getCarrierCycles();
method public float getCarrierFrequencyHz();
method public double getCarrierPhase();
@@ -20677,7 +20663,7 @@
method public int getState();
method public int getSvid();
method public double getTimeOffsetNanos();
- method public boolean hasAutomaticGainControlLevelInDb();
+ method public boolean hasAutomaticGainControlLevelDb();
method public boolean hasCarrierCycles();
method public boolean hasCarrierFrequencyHz();
method public boolean hasCarrierPhase();
@@ -20808,7 +20794,7 @@
method public int getSatelliteCount();
method public int getSvid(int);
method public boolean hasAlmanacData(int);
- method public boolean hasCarrierFrequency(int);
+ method public boolean hasCarrierFrequencyHz(int);
method public boolean hasEphemerisData(int);
method public boolean usedInFix(int);
field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -22470,7 +22456,7 @@
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
- field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
+ field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
@@ -24637,14 +24623,14 @@
method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
method public static boolean isChannelUriForTunerInput(android.net.Uri);
method public static boolean isProgramUri(android.net.Uri);
+ method public static void requestChannelBrowsable(android.content.Context, long);
field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
- field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
field public static final java.lang.String ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED";
+ field public static final java.lang.String ACTION_REQUEST_CHANNEL_BROWSABLE = "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
field public static final java.lang.String ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED = "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED";
field public static final java.lang.String AUTHORITY = "android.media.tv";
field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
- field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PREVIEW_PROGRAM_ID = "android.media.tv.extra.PREVIEW_PROGRAM_ID";
field public static final java.lang.String EXTRA_WATCH_NEXT_PROGRAM_ID = "android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
}
@@ -32068,7 +32054,7 @@
field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
- field public static final int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
+ field public static final deprecated int MEASUREMENT_CPU_POWER_MAMS = 10064; // 0x2750
field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
@@ -34649,6 +34635,7 @@
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String);
method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]);
method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler);
}
public static final class FontsContract.Columns implements android.provider.BaseColumns {
@@ -34682,6 +34669,18 @@
method public boolean isItalic();
}
+ public static class FontsContract.FontRequestCallback {
+ ctor public FontsContract.FontRequestCallback();
+ method public void onTypefaceRequestFailed(int);
+ method public void onTypefaceRetrieved(android.graphics.Typeface);
+ field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+ field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+ field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+ field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+ field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+ field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+ }
+
public final deprecated class LiveFolders implements android.provider.BaseColumns {
field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
field public static final java.lang.String DESCRIPTION = "description";
@@ -44247,7 +44246,6 @@
method public boolean isValid();
method public boolean isWideColorGamut();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
- field public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 32; // 0x20
field public static final int FLAG_PRESENTATION = 8; // 0x8
field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_ROUND = 16; // 0x10
@@ -44318,6 +44316,7 @@
method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int);
method public static android.view.FocusFinder getInstance();
+ method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
}
public final class FrameMetrics {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index d20c08c..82705fd 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -100,6 +100,18 @@
}
+package android.hardware {
+
+ public final class SensorDirectChannel implements java.nio.channels.Channel {
+ method public deprecated boolean isValid();
+ }
+
+ public abstract class SensorManager {
+ method public deprecated int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 8052288..b320d5d 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -335,6 +335,7 @@
* are removed, or an account's credentials (saved password, etc) are changed.
*
* @see #addOnAccountsUpdatedListener
+ * @see #ACTION_ACCOUNT_REMOVED
*
* @deprecated use {@link #addOnAccountsUpdatedListener} to get account updates in runtime.
*/
@@ -344,6 +345,14 @@
"android.accounts.LOGIN_ACCOUNTS_CHANGED";
/**
+ * Action sent as a broadcast Intent by the AccountsService when any account is removed.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(includeBackground = true)
+ public static final String ACTION_ACCOUNT_REMOVED =
+ "android.accounts.action.ACCOUNT_REMOVED";
+
+ /**
* Action sent as a broadcast Intent to specific package by the AccountsService
* when account visibility or account's credentials (saved password, etc) are changed.
*
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 595ad35..fc827a9 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -190,6 +190,8 @@
int flags, in Bundle options, int userId);
void cancelIntentSender(in IIntentSender sender);
String getPackageForIntentSender(in IIntentSender sender);
+ void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
+ void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
void enterSafeMode();
boolean startNextMatchingActivity(in IBinder callingActivity,
in Intent intent, in Bundle options);
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 28421eb..8519dba 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -1011,6 +1011,18 @@
}
/**
+ * check if in-band ringing is supported for this platform.
+ *
+ * @return true if in-band ringing is supported
+ * false if in-band ringing is not supported
+ * @hide
+ */
+ public static boolean isInbandRingingSupported(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
+ }
+
+ /**
* Send Headset the BIND response from AG to report change in the status of the
* HF indicators to the headset
*
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 2f87633..d428a3a 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -24,7 +24,6 @@
import android.database.CursorToBulkCursorAdaptor;
import android.database.DatabaseUtils;
import android.database.IContentObserver;
-import android.database.PageViewCursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -104,7 +103,6 @@
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
- cursor = PageViewCursor.wrap(cursor, queryArgs);
try {
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 88bade1..6375775 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2745,7 +2745,8 @@
* {@link #BIND_WAIVE_PRIORITY}.
* @return If you have successfully bound to the service, {@code true} is returned;
* {@code false} is returned if the connection is not made so you will not
- * receive the service object.
+ * receive the service object. However, you should still call
+ * {@link #unbindService} to release the connection.
*
* @throws SecurityException If the caller does not have permission to access the service
* or the service can not be found.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a4b5ffd..2485bb8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -657,6 +657,16 @@
public static final String ACTION_VIEW = "android.intent.action.VIEW";
/**
+ * Extra that can be included on activity intents coming from the storage UI
+ * when it launches sub-activities to manage various types of storage. For example,
+ * it may use {@link #ACTION_VIEW} with a "image/*" MIME type to have an app show
+ * the images on the device, and in that case also include this extra to tell the
+ * app it is coming from the storage UI so should help the user manage storage of
+ * this type.
+ */
+ public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
+
+ /**
* A synonym for {@link #ACTION_VIEW}, the "standard" action that is
* performed on a piece of data.
*/
@@ -2062,13 +2072,13 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
/**
- * Broadcast Action: A new application package has been installed on the
+ * Broadcast Action: An application package has been installed or updated on the
* device. The data contains the name of the package. Note that the
* newly installed package does <em>not</em> receive this broadcast.
* <p>May include the following extras:
* <ul>
- * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
- * <li> {@link #EXTRA_REPLACING} is set to true if this is following
+ * <li> {@link #EXTRA_UID} containing the integer uid assigned to this package.
+ * <li> {@link #EXTRA_REPLACING} is set to {@code true} if this is following
* an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package.
* </ul>
*
@@ -2078,6 +2088,22 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
/**
+ * Broadcast Action: A new application package has been installed on the
+ * device. The data contains the name of the package. Note that the
+ * newly installed package does <em>not</em> receive this broadcast.
+ * <p class="note">Unlike {@link #ACTION_PACKAGE_ADDED}, this broadcast is delivered
+ * to manifest receivers as well as those registered at runtime.
+ * <p>May include the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_FIRST_ADDED = "android.intent.action.PACKAGE_FIRST_ADDED";
+ /**
* Broadcast Action: A new version of an application package has been
* installed, replacing an existing version that was previously installed.
* The data contains the name of the package.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4d76755..c3bdde5 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -275,10 +275,6 @@
@Deprecated
public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
- /** @deprecated punted, don't use. */
- @Deprecated
- public static final int FLAG_MATCH_CHOOSER = 1 << 4;
-
/**
* Does not retrieve CHOOSER only shortcuts.
* TODO: Add another flag for MATCH_ALL_PINNED
@@ -375,12 +371,6 @@
return this;
}
- /** @deprecated punted, don't use. */
- @Deprecated
- public ShortcutQuery setIntent(@Nullable Intent intent) {
- return this;
- }
-
/**
* Set query options. At least one of the {@code MATCH} flags should be set. Otherwise,
* no shortcuts will be returned.
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 3f4a090..2b76ae2 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1032,20 +1032,6 @@
return this;
}
- /** @deprecated punted, don't use. */
- @Deprecated
- @NonNull
- public Builder setChooserExtras(@NonNull PersistableBundle extras) {
- return this;
- }
-
- /** @deprecated punted, don't use. */
- @Deprecated
- public Builder addChooserIntentFilter(@NonNull IntentFilter filter,
- @NonNull ComponentName name) {
- return this;
- }
-
/**
* Creates a {@link ShortcutInfo} instance.
*/
@@ -1246,27 +1232,6 @@
return mIntentPersistableExtrases;
}
- /** @deprecated punted, don't use. */
- @Deprecated
- @NonNull
- public PersistableBundle getChooserExtras() {
- return new PersistableBundle();
- }
-
- /** @deprecated punted, don't use. */
- @Deprecated
- @NonNull
- public IntentFilter[] getChooserIntentFilters() {
- return new IntentFilter[0];
- }
-
- /** @deprecated punted, don't use. */
- @Deprecated
- @NonNull
- public ComponentName[] getChooserComponentNames() {
- return new ComponentName[0];
- }
-
/**
* "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).
@@ -1388,12 +1353,6 @@
return hasFlags(FLAG_PINNED);
}
- /** @deprecated punted, don't use. */
- @Deprecated
- public boolean isChooser() {
- return false;
- }
-
/**
* Return whether a shortcut is static; that is, whether a shortcut is
* published from AndroidManifest.xml. If {@code true}, the shortcut is
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index c8353c9..88c1627 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -127,7 +127,7 @@
*/
public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0x2;
- /** Constant for {@link #colorMode}: bits that encode whether the dynamic range of the screen. */
+ /** Constant for {@link #colorMode}: bits that encode the dynamic range of the screen. */
public static final int COLOR_MODE_HDR_MASK = 0xc;
/** Constant for {@link #colorMode}: bits shift to get the screen dynamic range. */
public static final int COLOR_MODE_HDR_SHIFT = 2;
@@ -155,7 +155,7 @@
COLOR_MODE_HDR_UNDEFINED;
/**
- * Bit mask of for color capabilities of the screen. Currently there are two fields:
+ * Bit mask of color capabilities of the screen. Currently there are two fields:
* <p>The {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} bits define the color gamut of
* the screen. They may be one of
* {@link #COLOR_MODE_WIDE_COLOR_GAMUT_NO} or {@link #COLOR_MODE_WIDE_COLOR_GAMUT_YES}.</p>
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 3693bce9..408bee8 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1450,7 +1450,8 @@
* <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
* with the array.
*
- * @param attrs The desired attributes.
+ * @param attrs The desired attributes. These attribute IDs must be sorted in ascending
+ * order.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
@@ -1474,7 +1475,8 @@
* with the array.
*
* @param resId The desired style resource.
- * @param attrs The desired attributes in the style.
+ * @param attrs The desired attributes in the style. These attribute IDs must be sorted in
+ * ascending order.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
@@ -1521,7 +1523,8 @@
* the styles.
*
* @param set The base set of attribute values. May be null.
- * @param attrs The desired attributes to be retrieved.
+ * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
+ * in ascending order.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies
* defaults values for the TypedArray. Can be
@@ -1552,7 +1555,8 @@
* @param values The base set of attribute values, must be equal in
* length to {@code attrs}. All values must be of type
* {@link TypedValue#TYPE_ATTRIBUTE}.
- * @param attrs The desired attributes to be retrieved.
+ * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
+ * in ascending order.
* @return Returns a TypedArray holding an array of the attribute
* values. Be sure to call {@link TypedArray#recycle()}
* when done with it.
@@ -1791,14 +1795,15 @@
* performing styling of them using a theme and/or style resources.
*
* @param set The current attribute values to retrieve.
- * @param attrs The specific attributes to be retrieved.
+ * @param attrs The specific attributes to be retrieved. These attribute IDs must be sorted in
+ * ascending order.
* @return Returns a TypedArray holding an array of the attribute values.
* Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
* when done with it.
*
* @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
- public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+ public TypedArray obtainAttributes(AttributeSet set, @StyleableRes int[] attrs) {
int len = attrs.length;
TypedArray array = TypedArray.obtain(this, len);
diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java
deleted file mode 100644
index 4569a27..0000000
--- a/core/java/android/database/PageViewCursor.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.database;
-
-import static com.android.internal.util.ArrayUtils.contains;
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.content.ContentResolver;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.MathUtils;
-
-import java.util.Arrays;
-
-/**
- * Cursor wrapper that provides visibility into a subset of a wrapped cursor.
- *
- * The window is specified by offset and limit.
- *
- * @hide
- */
-@TestApi
-public final class PageViewCursor extends CursorWrapper implements CrossProcessCursor {
-
- /** An extra added to results that are auto-paged using the wrapper. */
- public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
-
- private static final String[] EMPTY_ARGS = new String[0];
- private static final String TAG = "PageViewCursor";
- private static final boolean DEBUG = Build.IS_DEBUGGABLE;
- private static final boolean VERBOSE = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
-
- private final int mOffset; // aka first index
- private final int mCount;
- private final Bundle mExtras;
-
- private @Nullable CursorWindow mWindow;
- private int mPos = -1;
- private int mWindowFillCount = 0;
-
- /**
- * @see PageViewCursor#wrap(Cursor, Bundle)
- */
- public PageViewCursor(Cursor cursor, Bundle queryArgs) {
- super(cursor);
-
- int offset = queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0);
- int limit = queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE);
-
- checkArgument(offset > -1);
- checkArgument(limit > -1);
-
- int count = mCursor.getCount();
-
- mOffset = offset;
-
- mExtras = new Bundle();
- Bundle extras = cursor.getExtras();
- if (extras != null) {
- mExtras.putAll(extras);
- }
-
- // When we're wrapping another cursor, it should not already be "paged".
- checkArgument(!hasPagedResponseDetails(mExtras));
-
- mExtras.putBoolean(EXTRA_AUTO_PAGED, true);
- mExtras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, count);
-
- // Ensure we retain any extra args supplied in cursor extras, and add
- // offset and/or limit.
- String[] existingArgs = mExtras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
- existingArgs = existingArgs != null ? existingArgs : EMPTY_ARGS;
-
- int size = existingArgs.length;
-
- // copy the array with space for the extra query args we'll be adding.
- String[] newArgs = Arrays.copyOf(existingArgs, size + 2);
-
- if (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET)) {
- newArgs[size++] = ContentResolver.QUERY_ARG_OFFSET;
- }
- if (queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT)) {
- newArgs[size++] = ContentResolver.QUERY_ARG_LIMIT;
- }
-
- assert(size > existingArgs.length); // must add at least one arg.
-
- // At this point there may be a null element at the end of
- // the array because our pre-sizing didn't match the actualy
- // number of args we added. So we trim.
- if (size == newArgs.length - 1) {
- newArgs = Arrays.copyOf(newArgs, size);
- }
- mExtras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, newArgs);
-
- mCount = MathUtils.constrain(count - offset, 0, limit);
-
- if (DEBUG) Log.d(TAG, "Wrapped cursor"
- + " offset: " + mOffset
- + ", limit: " + limit
- + ", delegate_size: " + count
- + ", paged_count: " + mCount);
- }
-
- @Override
- public Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public int getPosition() {
- return mPos;
- }
-
- @Override
- public boolean isBeforeFirst() {
- if (mCount == 0) {
- return true;
- }
- return mPos == -1;
- }
-
- @Override
- public boolean isAfterLast() {
- if (mCount == 0) {
- return true;
- }
- return mPos == mCount;
- }
-
- @Override
- public boolean isFirst() {
- return mPos == 0;
- }
-
- @Override
- public boolean isLast() {
- return mPos == mCount - 1;
- }
-
- @Override
- public boolean moveToFirst() {
- return moveToPosition(0);
- }
-
- @Override
- public boolean moveToLast() {
- return moveToPosition(mCount - 1);
- }
-
- @Override
- public boolean moveToNext() {
- return move(1);
- }
-
- @Override
- public boolean moveToPrevious() {
- return move(-1);
- }
-
- @Override
- public boolean move(int offset) {
- return moveToPosition(mPos + offset);
- }
-
- @Override
- public boolean moveToPosition(int position) {
- if (position >= mCount) {
- if (VERBOSE) Log.v(TAG, "Invalid Positon: " + position + " >= count: " + mCount
- + ". Moving to last record.");
- mPos = mCount;
- super.moveToPosition(mOffset + mPos); // move into "after last" state.
- return false;
- }
-
- // Make sure position isn't before the beginning of the cursor
- if (position < 0) {
- if (VERBOSE) Log.v(TAG, "Ignoring invalid move to position: " + position);
- mPos = -1;
- super.moveToPosition(mPos);
- return false;
- }
-
- if (position == mPos) {
- if (VERBOSE) Log.v(TAG, "Ignoring no-op move to position: " + position);
- return true;
- }
-
- int delegatePosition = position + mOffset;
- if (VERBOSE) Log.v(TAG, "Moving delegate cursor to position: " + delegatePosition);
- if (super.moveToPosition(delegatePosition)) {
- mPos = position;
- return true;
- } else {
- mPos = -1;
- super.moveToPosition(-1);
- return false;
- }
- }
-
- @Override
- public boolean onMove(int oldPosition, int newPosition) {
- throw new UnsupportedOperationException("Not supported.");
- }
-
- @Override
- public int getCount() {
- return mCount;
- }
-
- @Override
- public boolean getWantsAllOnMoveCalls() {
- return false; // we want bulk cursor adapter to lift data into a CursorWindow.
- }
-
- @Override
- public CursorWindow getWindow() {
- assert(mPos == -1 || mPos == 0);
- if (mWindow == null) {
- mWindow = new CursorWindow("PageViewCursorWindow");
- fillWindow(0, mWindow);
- }
-
- return mWindow;
- }
-
- @Override
- public void fillWindow(int position, CursorWindow window) {
- assert(window == mWindow);
-
- if (mWindowFillCount++ > 0) {
- Log.w(TAG, "Re-filling window on paged cursor! Reduce ContentResolver.QUERY_ARG_LIMIT");
- }
-
- DatabaseUtils.cursorFillWindow(this, position, window);
- }
-
- /**
- * Wraps the cursor such that it will honor paging args (if present), AND if the cursor does
- * not report paging size.
- * <p>
- * No-op if cursor already contains paging or is less than specified page size.
- */
- public static Cursor wrap(Cursor cursor, @Nullable Bundle queryArgs) {
-
- boolean hasPagingArgs = queryArgs != null
- && (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET)
- || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT));
-
- if (!hasPagingArgs) {
- if (VERBOSE) Log.v(TAG, "No-wrap: No paging args in request.");
- return cursor;
- }
-
- if (hasPagedResponseDetails(cursor.getExtras())) {
- if (VERBOSE) Log.v(TAG, "No-wrap. Cursor has paging details.");
- return cursor;
- }
-
- // Cursors that want all calls aren't compatible with our way
- // of doing business. TODO: Cover this case in CTS.
- if (cursor.getWantsAllOnMoveCalls()) {
- Log.w(TAG, "Unable to wrap cursor that wants to hear about move calls.");
- return cursor;
- }
-
- return new PageViewCursor(cursor, queryArgs);
- }
-
- /**
- * @return true if the extras contains information indicating the associated cursor is
- * paged.
- */
- private static boolean hasPagedResponseDetails(@Nullable Bundle extras) {
- if (extras == null) {
- return false;
- }
-
- if (extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) {
- return true;
- }
-
- String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
- if (honoredArgs != null
- && (contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)
- || contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) {
- return true;
- }
-
- return false;
- }
-}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 0218cb5..a895f82 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -836,16 +836,16 @@
}
/**
- * Test if sensor support direct channel backed by a specific type of shared memory.
+ * Test if a sensor supports a specified direct channel type.
*
* @param sharedMemType type of shared memory used by direct channel.
- * @return <code>true</code> if the shared memory type is supported.
- * @see SensorDirectChannel#TYPE_ASHMEM
+ * @return <code>true</code> if the specified shared memory type is supported.
+ * @see SensorDirectChannel#TYPE_MEMORY_FILE
* @see SensorDirectChannel#TYPE_HARDWARE_BUFFER
*/
public boolean isDirectChannelTypeSupported(@SensorDirectChannel.MemoryType int sharedMemType) {
switch (sharedMemType) {
- case SensorDirectChannel.TYPE_ASHMEM:
+ case SensorDirectChannel.TYPE_MEMORY_FILE:
return (mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0;
case SensorDirectChannel.TYPE_HARDWARE_BUFFER:
return (mFlags & (1 << DIRECT_CHANNEL_SHIFT + 1)) > 0;
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
index a65d57d..bd7f9cf 100644
--- a/core/java/android/hardware/SensorDirectChannel.java
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -23,26 +23,31 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.channels.Channel;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Class representing a sensor direct channel. Use {@link
- * SensorManager#createDirectChannel(android.os.MemoryFile)} to obtain object.
+ * Class representing a sensor direct channel. Use
+ * {@link SensorManager#createDirectChannel(android.os.MemoryFile)} or
+ * {@link SensorManager#createDirectChannel(android.hardware.HardwareBuffer)}
+ * to obtain an object. The channel object can be then configured
+ * (see {@link #configure(Sensor, int)})
+ * to start delivery of sensor events into shared memory buffer.
*/
-public final class SensorDirectChannel implements AutoCloseable {
+public final class SensorDirectChannel implements Channel {
// shared memory types
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {TYPE_ASHMEM, TYPE_HARDWARE_BUFFER})
+ @IntDef(flag = true, value = {TYPE_MEMORY_FILE, TYPE_HARDWARE_BUFFER})
public @interface MemoryType {};
/**
* Shared memory type ashmem, wrapped in MemoryFile object.
*
* @see SensorManager#createDirectChannel(MemoryFile)
*/
- public static final int TYPE_ASHMEM = 1;
+ public static final int TYPE_MEMORY_FILE = 1;
/**
* Shared memory type wrapped by HardwareBuffer object.
@@ -61,7 +66,7 @@
/**
* Sensor stopped (no event output).
*
- * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+ * @see #configure(Sensor, int)
*/
public static final int RATE_STOP = 0;
/**
@@ -70,7 +75,7 @@
* The actual rate is expected to be between 55% to 220% of nominal rate, thus between 27.5Hz to
* 110Hz.
*
- * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+ * @see #configure(Sensor, int)
*/
public static final int RATE_NORMAL = 1; //50Hz
/**
@@ -79,7 +84,7 @@
* The actual rate is expected to be between 55% to 220% of nominal rate, thus between 110Hz to
* 440Hz.
*
- * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+ * @see #configure(Sensor, int)
*/
public static final int RATE_FAST = 2; // ~200Hz
/**
@@ -88,7 +93,7 @@
* The actual rate is expected to be between 55% to 220% of nominal rate, thus between 440Hz to
* 1760Hz.
*
- * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+ * @see #configure(Sensor, int)
*/
public static final int RATE_VERY_FAST = 3; // ~800Hz
@@ -98,10 +103,17 @@
*
* @return <code>true</code> if channel is valid.
*/
- public boolean isValid() {
+ @Override
+ public boolean isOpen() {
return !mClosed.get();
}
+ /** @removed */
+ @Deprecated
+ public boolean isValid() {
+ return isOpen();
+ }
+
/**
* Close sensor direct channel.
*
@@ -113,13 +125,61 @@
*/
@Override
public void close() {
- mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
+ mCloseGuard.close();
// actual close action
mManager.destroyDirectChannel(this);
}
}
+ /**
+ * Configure sensor rate or stop sensor report.
+ *
+ * To start event report of a sensor, or change rate of existing report, call this function with
+ * rateLevel other than {@link android.hardware.SensorDirectChannel#RATE_STOP}. Sensor events
+ * will be added into a queue formed by the shared memory used in creation of direction channel.
+ * Each element of the queue has size of 104 bytes and represents a sensor event. Data
+ * structure of an element (all fields in little-endian):
+ *
+ * <pre>
+ * offset type name
+ * ------------------------------------------------------------------------
+ * 0x0000 int32_t size (always 104)
+ * 0x0004 int32_t sensor report token
+ * 0x0008 int32_t type (see SensorType)
+ * 0x000C uint32_t atomic counter
+ * 0x0010 int64_t timestamp (see Event)
+ * 0x0018 float[16]/int64_t[8] data (data type depends on sensor type)
+ * 0x0058 int32_t[4] reserved (set to zero)
+ * </pre>
+ *
+ * There are no head or tail pointers. The sequence and frontier of new sensor events is
+ * determined by the atomic counter, which counts from 1 after creation of direct channel and
+ * increments 1 for each new event. Atomic counter will wrap back to 1 after it reaches
+ * UINT32_MAX, skipping value 0 to avoid confusion with uninitialized memory. The writer in
+ * sensor system will wrap around from the start of shared memory region when it reaches the
+ * end. If size of memory region is not a multiple of size of element (104 bytes), the residual
+ * is not used at the end. Function returns a positive sensor report token on success. This
+ * token can be used to differentiate sensor events from multiple sensor of the same type. For
+ * example, if there are two accelerometers in the system A and B, it is guaranteed different
+ * report tokens will be returned when starting sensor A and B.
+ *
+ * To stop a sensor, call this function with rateLevel equal {@link
+ * android.hardware.SensorDirectChannel#RATE_STOP}. If the sensor parameter is left to be null,
+ * this will stop all active sensor report associated with the direct channel specified.
+ * Function return 1 on success or 0 on failure.
+ *
+ * @param sensor A {@link android.hardware.Sensor} object to denote sensor to be operated.
+ * @param rateLevel rate level defined in {@link android.hardware.SensorDirectChannel}.
+ * @return * starting report or changing rate: positive sensor report token on success,
+ * 0 on failure;
+ * * stopping report: 1 on success, 0 on failure.
+ * @throws NullPointerException when channel is null.
+ */
+ public int configure(Sensor sensor, @RateLevel int rateLevel) {
+ return mManager.configureDirectChannelImpl(this, sensor, rateLevel);
+ }
+
/** @hide */
SensorDirectChannel(SensorManager manager, int id, int type, long size) {
mManager = manager;
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 1dc6478..ed56391 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -882,7 +882,12 @@
/**
- * Create a sensor direct channel backed by shared memory wrapped by MemoryFile object.
+ * Create a sensor direct channel backed by shared memory wrapped in MemoryFile object.
+ *
+ * The resulting channel can be used for delivering sensor events to native code, other
+ * processes, GPU/DSP or other co-processors without CPU intervention. This is the recommanded
+ * for high performance sensor applications that use high sensor rates (e.g. greater than 200Hz)
+ * and cares about sensor event latency.
*
* Use the returned {@link android.hardware.SensorDirectChannel} object to configure direct
* report of sensor events. After use, call {@link android.hardware.SensorDirectChannel#close()}
@@ -890,7 +895,7 @@
*
* @param mem A {@link android.os.MemoryFile} shared memory object.
* @return A {@link android.hardware.SensorDirectChannel} object if successful, null otherwise.
- * @throws IllegalArgumentException when mem is null.
+ * @throws NullPointerException when mem is null.
* @see SensorDirectChannel#close()
* @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
*/
@@ -899,7 +904,12 @@
}
/**
- * Create a sensor direct channel backed by shared memory wrapped by HardwareBuffer object.
+ * Create a sensor direct channel backed by shared memory wrapped in HardwareBuffer object.
+ *
+ * The resulting channel can be used for delivering sensor events to native code, other
+ * processes, GPU/DSP or other co-processors without CPU intervention. This is the recommanded
+ * for high performance sensor applications that use high sensor rates (e.g. greater than 200Hz)
+ * and cares about sensor event latency.
*
* Use the returned {@link android.hardware.SensorDirectChannel} object to configure direct
* report of sensor events. After use, call {@link android.hardware.SensorDirectChannel#close()}
@@ -908,7 +918,7 @@
* @param mem A {@link android.hardware.HardwareBuffer} shared memory object.
* @return A {@link android.hardware.SensorDirectChannel} object if successful,
* null otherwise.
- * @throws IllegalArgumentException when mem is null.
+ * @throws NullPointerException when mem is null.
* @see SensorDirectChannel#close()
* @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
*/
@@ -928,50 +938,9 @@
/** @hide */
protected abstract void destroyDirectChannelImpl(SensorDirectChannel channel);
- /**
- * Configure sensor rate or stop sensor report on a direct report channel specified.
- *
- * To start event report of a sensor, or change rate of existing report, call this function with
- * rateLevel other than {@link android.hardware.SensorDirectChannel#RATE_STOP}. Sensor events
- * will be added into a queue formed by the shared memory used in creation of direction channel.
- * Each element of the queue has size of 104 bytes and represents a sensor event. Data
- * structure of an element (all fields in little-endian):
- *
- * offset type name
- *- ---------------------------------------------
- * 0x0000 int32_t size (always 104)
- * 0x0004 int32_t sensor report token
- * 0x0008 int32_t type (see SensorType)
- * 0x000C uint32_t atomic counter
- * 0x0010 int64_t timestamp (see Event)
- * 0x0018 float[16]/int64_t[8] data (data type depends on sensor type)
- * 0x0058 int32_t[4] reserved (set to zero)
- *
- * There is no head or tail pointers. The sequence and frontier of new sensor events is
- * determined by the atomic counter, which counts from 1 after creation of direct channel and
- * increments 1 for each new event. The writer in sensor system will wrap around from to
- * start of shared memory region when it reaches the end. If size of memory region is not
- * a multiple of size of element (104 bytes), the residual is not used at the end.
- * Function returns a positive sensor report token on success. This token can be used for
- * differentiate sensor events from multiple sensor of the same type. For example, if there are
- * two accelerometer in the system A and B, it is guaranteed different report tokens will be
- * returned when starting sensor A and B.
- *
- * To stop a sensor, call this function with rateLevel equal {@link
- * android.hardware.SensorDirectChannel#RATE_STOP}. If the sensor parameter is left to be null,
- * this will stop all active sensor report associated with the direct channel specified.
- * Function return 1 on success or 0 on failure.
- *
- * @param channel A {@link android.hardware.SensorDirectChannel} object representing direct
- * channel to be configured.
- * @param sensor A {@link android.hardware.Sensor} object to denote sensor to be operated.
- * @param rateLevel rate level defined in {@link android.hardware.SensorDirectChannel}.
- * @return starting report or changing rate: positive sensor report token on success, 0 on failure;
- * stopping report: 1 on success, 0 on failure.
- * @throws IllegalArgumentException when SensorDirectChannel is null.
- */
- public int configureDirectChannel(SensorDirectChannel channel, Sensor sensor,
- @SensorDirectChannel.RateLevel int rateLevel) {
+ /** @removed */
+ @Deprecated
+ public int configureDirectChannel(SensorDirectChannel channel, Sensor sensor, int rateLevel) {
return configureDirectChannelImpl(channel, sensor, rateLevel);
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 0677179..10c4cb3 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -35,6 +35,7 @@
import com.android.internal.annotations.GuardedBy;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -500,10 +501,8 @@
/** @hide */
protected int configureDirectChannelImpl(
SensorDirectChannel channel, Sensor sensor, int rate) {
- if (channel == null) throw new IllegalArgumentException("channel cannot be null");
-
- if (!channel.isValid()) {
- throw new IllegalStateException("channel is invalid");
+ if (!channel.isOpen()) {
+ throw new IllegalStateException("channel is closed");
}
if (rate < SensorDirectChannel.RATE_STOP
@@ -532,7 +531,8 @@
/** @hide */
protected SensorDirectChannel createDirectChannelImpl(
MemoryFile memoryFile, HardwareBuffer hardwareBuffer) {
- SensorDirectChannel ch = null;
+ int id;
+ int type;
long size;
if (memoryFile != null) {
int fd;
@@ -549,11 +549,13 @@
}
size = memoryFile.length();
- int id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_ASHMEM, fd, null);
- if (id > 0) {
- ch = new SensorDirectChannel(this, id, SensorDirectChannel.TYPE_ASHMEM, size);
+ id = nativeCreateDirectChannel(
+ mNativeInstance, size, SensorDirectChannel.TYPE_MEMORY_FILE, fd, null);
+ if (id <= 0) {
+ throw new UncheckedIOException(
+ new IOException("create MemoryFile direct channel failed " + id));
}
+ type = SensorDirectChannel.TYPE_MEMORY_FILE;
} else if (hardwareBuffer != null) {
if (hardwareBuffer.getFormat() != HardwareBuffer.BLOB) {
throw new IllegalArgumentException("Format of HardwareBuffer must be BLOB");
@@ -571,18 +573,18 @@
"HardwareBuffer must set usage flag USAGE0_SENSOR_DIRECT_DATA");
}
size = hardwareBuffer.getWidth();
- int id = nativeCreateDirectChannel(
+ id = nativeCreateDirectChannel(
mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
-1, hardwareBuffer);
- if (id > 0) {
- ch = new SensorDirectChannel(
- this, id, SensorDirectChannel.TYPE_HARDWARE_BUFFER, size);
+ if (id <= 0) {
+ throw new UncheckedIOException(
+ new IOException("create HardwareBuffer direct channel failed " + id));
}
+ type = SensorDirectChannel.TYPE_HARDWARE_BUFFER;
} else {
- throw new IllegalArgumentException("Invalid parameter");
+ throw new NullPointerException("shared memory object cannot be null");
}
-
- return ch;
+ return new SensorDirectChannel(this, id, type, size);
}
/** @hide */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a529c2f..3267172 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -247,6 +247,7 @@
* </p>
*
* @see #createVirtualDisplay
+ * @hide
*/
public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 425a89d..1e98301 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -37,7 +37,7 @@
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about communicating with USB hardware, read the
- * <a href="{@docRoot}guide/topics/usb/index.html">USB</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB</a> developer guide.</p>
* </div>
*/
public class UsbDevice implements Parcelable {
diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java
index d8768e7..5be977a 100644
--- a/core/java/android/metrics/MetricsReader.java
+++ b/core/java/android/metrics/MetricsReader.java
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.LinkedList;
import java.util.Queue;
+import java.util.concurrent.TimeUnit;
/**
* Read platform logs.
@@ -80,7 +81,7 @@
mPendingQueue.clear();
mSeenQueue.clear();
for (Event event : nativeEvents) {
- final long eventTimestampMs = event.getTimeNanos() / 1000000;
+ final long eventTimestampMs = event.getTimeMillis();
Object data = event.getData();
Object[] objects;
if (data instanceof Object[]) {
@@ -152,24 +153,25 @@
*/
@VisibleForTesting
public static class Event {
- long mTimeNanos;
+ long mTimeMillis;
int mPid;
Object mData;
- public Event(long timeNanos, int pid, Object data) {
- mTimeNanos = timeNanos;
+ public Event(long timeMillis, int pid, Object data) {
+ mTimeMillis = timeMillis;
mPid = pid;
mData = data;
}
Event(EventLog.Event nativeEvent) {
- mTimeNanos = nativeEvent.getTimeNanos();
+ mTimeMillis = TimeUnit.MILLISECONDS.convert(
+ nativeEvent.getTimeNanos(), TimeUnit.NANOSECONDS);
mPid = nativeEvent.getProcessId();
mData = nativeEvent.getData();
}
- public long getTimeNanos() {
- return mTimeNanos;
+ public long getTimeMillis() {
+ return mTimeMillis;
}
public int getProcessId() {
@@ -196,7 +198,8 @@
throws IOException {
// Testing in Android: the Static Final Class Strikes Back!
ArrayList<EventLog.Event> nativeEvents = new ArrayList<>();
- EventLog.readEventsOnWrapping(tags, horizonMs, nativeEvents);
+ long horizonNs = TimeUnit.NANOSECONDS.convert(horizonMs, TimeUnit.MILLISECONDS);
+ EventLog.readEventsOnWrapping(tags, horizonNs, nativeEvents);
for (EventLog.Event nativeEvent : nativeEvents) {
Event event = new Event(nativeEvent);
events.add(event);
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 63ccaae..46bb346 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.BitUtils;
/**
* Represents a core networking event defined in package android.net.metrics.
@@ -78,13 +79,15 @@
public String toString() {
StringBuilder buffer = new StringBuilder("ConnectivityMetricsEvent(");
buffer.append(String.format("%tT.%tL", timestamp, timestamp));
- // TODO: add transports
if (netId != 0) {
buffer.append(", ").append(netId);
}
if (ifname != null) {
buffer.append(", ").append(ifname);
}
+ for (int t : BitUtils.unpackBits(transports)) {
+ buffer.append(", ").append(NetworkCapabilities.transportNameOf(t));
+ }
buffer.append("): ").append(data.toString());
return buffer.toString();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8665b9c..a594bef 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -19,7 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import java.lang.IllegalArgumentException;
+import com.android.internal.util.BitUtils;
/**
* This class represents the capabilities of a network. This is used both to specify
@@ -289,7 +289,7 @@
* @hide
*/
public int[] getCapabilities() {
- return enumerateBits(mNetworkCapabilities);
+ return BitUtils.unpackBits(mNetworkCapabilities);
}
/**
@@ -305,19 +305,6 @@
return ((mNetworkCapabilities & (1 << capability)) != 0);
}
- private int[] enumerateBits(long val) {
- int size = Long.bitCount(val);
- int[] result = new int[size];
- int index = 0;
- int resource = 0;
- while (val > 0) {
- if ((val & 1) == 1) result[index++] = resource;
- val = val >> 1;
- resource++;
- }
- return result;
- }
-
private void combineNetCapabilities(NetworkCapabilities nc) {
this.mNetworkCapabilities |= nc.mNetworkCapabilities;
}
@@ -428,6 +415,15 @@
/** @hide */
public static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
+ private static final String[] TRANSPORT_NAMES = {
+ "CELLULAR",
+ "WIFI",
+ "BLUETOOTH",
+ "ETHERNET",
+ "VPN",
+ "WIFI_AWARE"
+ };
+
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
* Multiple transports may be applied sequentially. Note that when searching
@@ -474,18 +470,7 @@
* @hide
*/
public int[] getTransportTypes() {
- return enumerateBits(mTransportTypes);
- }
-
- /**
- * Gets all the transports set on this {@code NetworkCapability} instance.
- *
- * @return a bit field composed of up bits at indexes defined by
- * {@code NetworkCapabilities.TRANSPORT_*} values for this instance.
- * @hide
- */
- public long getTransports() {
- return mTransportTypes;
+ return BitUtils.unpackBits(mTransportTypes);
}
/**
@@ -899,18 +884,23 @@
* @hide
*/
public static String transportNamesOf(int[] types) {
- String transports = "";
- for (int i = 0; i < types.length;) {
- switch (types[i]) {
- case TRANSPORT_CELLULAR: transports += "CELLULAR"; break;
- case TRANSPORT_WIFI: transports += "WIFI"; break;
- case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
- case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
- case TRANSPORT_VPN: transports += "VPN"; break;
- case TRANSPORT_WIFI_AWARE: transports += "WIFI_AWARE"; break;
- }
- if (++i < types.length) transports += "|";
+ if (types == null || types.length == 0) {
+ return "";
}
- return transports;
+ StringBuilder transports = new StringBuilder();
+ for (int t : types) {
+ transports.append("|").append(transportNameOf(t));
+ }
+ return transports.substring(1);
+ }
+
+ /**
+ * @hide
+ */
+ public static String transportNameOf(int transport) {
+ if (transport < 0 || TRANSPORT_NAMES.length <= transport) {
+ return "UNKNOWN";
+ }
+ return TRANSPORT_NAMES[transport];
}
}
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index 271b0a7..af0459d 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -20,6 +20,19 @@
/**
* The base class for implementing a network recommendation provider.
+ * <p>
+ * A network recommendation provider is any application which:
+ * <ul>
+ * <li>Is granted the {@link permission#SCORE_NETWORKS} permission.
+ * <li>Includes a Service for the {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} intent
+ * which is protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE} permission.
+ * </ul>
+ * <p>
+ * Implementations are required to implement the abstract methods in this class and return the
+ * result of {@link #getBinder()} from the <code>onBind()</code> method in their Service.
+ * <p>
+ * The default network recommendation provider is controlled via the
+ * <code>config_defaultNetworkRecommendationProviderPackage</code> config key.
* @hide
*/
@SystemApi
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java
index 214edee..30b2656 100644
--- a/core/java/android/net/metrics/ConnectStats.java
+++ b/core/java/android/net/metrics/ConnectStats.java
@@ -16,53 +16,47 @@
package android.net.metrics;
+import android.net.NetworkCapabilities;
import android.system.OsConstants;
import android.util.IntArray;
import android.util.SparseIntArray;
+import com.android.internal.util.BitUtils;
import com.android.internal.util.TokenBucket;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
/**
- * A class that aggregates connect() statistics and helps build
- * IpConnectivityLogClass.ConnectStatistics instances.
- *
+ * A class that aggregates connect() statistics.
* {@hide}
*/
public class ConnectStats {
private final static int EALREADY = OsConstants.EALREADY;
private final static int EINPROGRESS = OsConstants.EINPROGRESS;
+ /** Network id of the network associated with the event, or 0 if unspecified. */
+ public final int netId;
+ /** Transports of the network associated with the event, as defined in NetworkCapabilities. */
+ public final long transports;
/** How many events resulted in a given errno. */
- private final SparseIntArray mErrnos = new SparseIntArray();
- /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */
- private final IntArray mLatencies = new IntArray();
+ public final SparseIntArray errnos = new SparseIntArray();
+ /** Latencies of successful blocking connects. TODO: add non-blocking connects latencies. */
+ public final IntArray latencies = new IntArray();
/** TokenBucket for rate limiting latency recording. */
- private final TokenBucket mLatencyTb;
+ public final TokenBucket mLatencyTb;
/** Maximum number of latency values recorded. */
- private final int mMaxLatencyRecords;
+ public final int mMaxLatencyRecords;
/** Total count of successful connects. */
- private int mConnectCount = 0;
+ public int connectCount = 0;
/** Total count of successful connects done in blocking mode. */
- private int mConnectBlockingCount = 0;
+ public int connectBlockingCount = 0;
/** Total count of successful connects with IPv6 socket address. */
- private int mIpv6ConnectCount = 0;
+ public int ipv6ConnectCount = 0;
- public ConnectStats(TokenBucket tb, int maxLatencyRecords) {
+ public ConnectStats(int netId, long transports, TokenBucket tb, int maxLatencyRecords) {
+ this.netId = netId;
+ this.transports = transports;
mLatencyTb = tb;
mMaxLatencyRecords = maxLatencyRecords;
}
- public ConnectStatistics toProto() {
- ConnectStatistics stats = new ConnectStatistics();
- stats.connectCount = mConnectCount;
- stats.connectBlockingCount = mConnectBlockingCount;
- stats.ipv6AddrCount = mIpv6ConnectCount;
- stats.latenciesMs = mLatencies.toArray();
- stats.errnosCounters = toPairArrays(mErrnos);
- return stats;
- }
-
public void addEvent(int errno, int latencyMs, String ipAddr) {
if (isSuccess(errno)) {
countConnect(errno, ipAddr);
@@ -73,12 +67,12 @@
}
private void countConnect(int errno, String ipAddr) {
- mConnectCount++;
+ connectCount++;
if (!isNonBlocking(errno)) {
- mConnectBlockingCount++;
+ connectBlockingCount++;
}
if (isIPv6(ipAddr)) {
- mIpv6ConnectCount++;
+ ipv6ConnectCount++;
}
}
@@ -91,16 +85,16 @@
// Rate limited
return;
}
- if (mLatencies.size() >= mMaxLatencyRecords) {
+ if (latencies.size() >= mMaxLatencyRecords) {
// Hard limit the total number of latency measurements.
return;
}
- mLatencies.add(ms);
+ latencies.add(ms);
}
private void countError(int errno) {
- final int newcount = mErrnos.get(errno, 0) + 1;
- mErrnos.put(errno, newcount);
+ final int newcount = errnos.get(errno, 0) + 1;
+ errnos.put(errno, newcount);
}
private static boolean isSuccess(int errno) {
@@ -117,27 +111,18 @@
return ipAddr.contains(":");
}
- private static Pair[] toPairArrays(SparseIntArray counts) {
- final int s = counts.size();
- Pair[] pairs = new Pair[s];
- for (int i = 0; i < s; i++) {
- Pair p = new Pair();
- p.key = counts.keyAt(i);
- p.value = counts.valueAt(i);
- pairs[i] = p;
- }
- return pairs;
- }
-
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("ConnectStats(")
- .append(String.format("%d success, ", mConnectCount))
- .append(String.format("%d blocking, ", mConnectBlockingCount))
- .append(String.format("%d IPv6 dst", mIpv6ConnectCount));
- for (int i = 0; i < mErrnos.size(); i++) {
- String errno = OsConstants.errnoName(mErrnos.keyAt(i));
- int count = mErrnos.valueAt(i);
+ StringBuilder builder = new StringBuilder("ConnectStats(").append(netId).append(", ");
+ for (int t : BitUtils.unpackBits(transports)) {
+ builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
+ }
+ builder.append(String.format("%d success, ", connectCount));
+ builder.append(String.format("%d blocking, ", connectBlockingCount));
+ builder.append(String.format("%d IPv6 dst", ipv6ConnectCount));
+ for (int i = 0; i < errnos.size(); i++) {
+ String errno = OsConstants.errnoName(errnos.keyAt(i));
+ int count = errnos.valueAt(i);
builder.append(String.format(", %s: %d", errno, count));
}
return builder.append(")").toString();
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index 89ae1c2..a4970e4 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -16,67 +16,70 @@
package android.net.metrics;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.net.NetworkCapabilities;
+import java.util.Arrays;
+import com.android.internal.util.BitUtils;
/**
* A DNS event recorded by NetdEventListenerService.
* {@hide}
*/
-final public class DnsEvent implements Parcelable {
- public final int netId;
+final public class DnsEvent {
- // The event type is currently only 1 or 2, so we store it as a byte.
- public final byte[] eventTypes;
+ private static final int SIZE_LIMIT = 20000;
+
+ // Network id of the network associated with the event, or 0 if unspecified.
+ public final int netId;
+ // Transports of the network associated with the event, as defined in NetworkCapabilities.
+ // It is the caller responsability to ensure the value of transports does not change between
+ // calls to addResult.
+ public final long transports;
+ // The number of DNS queries recorded. Queries are stored in the structure-of-array style where
+ // the eventTypes, returnCodes, and latenciesMs arrays have the same length and the i-th event
+ // is spread across the three array at position i.
+ public int eventCount;
+ // The types of DNS queries as defined in INetdEventListener.
+ public byte[] eventTypes;
// Current getaddrinfo codes go from 1 to EAI_MAX = 15. gethostbyname returns errno, but there
// are fewer than 255 errno values. So we store the result code in a byte as well.
- public final byte[] returnCodes;
- // The latency is an integer because a) short arrays aren't parcelable and b) a short can only
- // store a maximum latency of 32757 or 65535 ms, which is too short for pathologically slow
- // queries.
- public final int[] latenciesMs;
+ public byte[] returnCodes;
+ // Latencies in milliseconds of queries, stored as ints.
+ public int[] latenciesMs;
- public DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) {
+ public DnsEvent(int netId, long transports, int initialCapacity) {
this.netId = netId;
- this.eventTypes = eventTypes;
- this.returnCodes = returnCodes;
- this.latenciesMs = latenciesMs;
+ this.transports = transports;
+ eventTypes = new byte[initialCapacity];
+ returnCodes = new byte[initialCapacity];
+ latenciesMs = new int[initialCapacity];
}
- private DnsEvent(Parcel in) {
- this.netId = in.readInt();
- this.eventTypes = in.createByteArray();
- this.returnCodes = in.createByteArray();
- this.latenciesMs = in.createIntArray();
+ public void addResult(byte eventType, byte returnCode, int latencyMs) {
+ if (eventCount >= SIZE_LIMIT) {
+ // TODO: implement better rate limiting that does not biases metrics.
+ return;
+ }
+ if (eventCount == eventTypes.length) {
+ resize((int) (1.4 * eventCount));
+ }
+ eventTypes[eventCount] = eventType;
+ returnCodes[eventCount] = returnCode;
+ latenciesMs[eventCount] = latencyMs;
+ eventCount++;
}
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(netId);
- out.writeByteArray(eventTypes);
- out.writeByteArray(returnCodes);
- out.writeIntArray(latenciesMs);
- }
-
- @Override
- public int describeContents() {
- return 0;
+ public void resize(int newLength) {
+ eventTypes = Arrays.copyOf(eventTypes, newLength);
+ returnCodes = Arrays.copyOf(returnCodes, newLength);
+ latenciesMs = Arrays.copyOf(latenciesMs, newLength);
}
@Override
public String toString() {
- return String.format("DnsEvent(%d, %d events)", netId, eventTypes.length);
+ StringBuilder builder = new StringBuilder("DnsEvent(").append(netId).append(", ");
+ for (int t : BitUtils.unpackBits(transports)) {
+ builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
+ }
+ return builder.append(eventCount).append(" events)").toString();
}
-
- public static final Parcelable.Creator<DnsEvent> CREATOR = new Parcelable.Creator<DnsEvent>() {
- @Override
- public DnsEvent createFromParcel(Parcel in) {
- return new DnsEvent(in);
- }
-
- @Override
- public DnsEvent[] newArray(int size) {
- return new DnsEvent[size];
- }
- };
}
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index ac727ca..4e57efa 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -23,6 +23,7 @@
import android.os.ServiceManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.BitUtils;
/**
* Class for logging IpConnectvity events with IpConnectivityMetrics
@@ -117,10 +118,10 @@
* @param data is a Parcelable instance representing the event.
* @return true if the event was successfully logged.
*/
- public boolean log(int netid, long transports, Parcelable data) {
+ public boolean log(int netid, int[] transports, Parcelable data) {
ConnectivityMetricsEvent ev = makeEv(data);
ev.netId = netid;
- ev.transports = transports;
+ ev.transports = BitUtils.packBits(transports);
return log(ev);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4c6d22a..832031e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -182,7 +182,7 @@
* New in version 19:
* - Wakelock data (wl) gets current and max times.
* New in version 20:
- * - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs.
+ * - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs, Syncs.
*/
static final String CHECKIN_VERSION = "20";
@@ -594,11 +594,6 @@
public abstract long getSystemCpuTimeUs(int which);
/**
- * Get the total cpu power consumed (in milli-ampere-microseconds).
- */
- public abstract long getCpuPowerMaUs(int which);
-
- /**
* Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a
* given CPU cluster.
* @param cluster the index of the CPU cluster.
@@ -3392,9 +3387,13 @@
// Convert from microseconds to milliseconds with rounding
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
+ final Timer bgTimer = timer.getSubTimer();
+ final long bgTime = bgTimer != null ?
+ (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1;
+ final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
if (totalTime != 0) {
dumpLine(pw, uid, category, SYNC_DATA, "\"" + syncs.keyAt(isy) + "\"",
- totalTime, count);
+ totalTime, count, bgTime, bgCount);
}
}
@@ -3467,10 +3466,9 @@
final long userCpuTimeUs = u.getUserCpuTimeUs(which);
final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
- final long powerCpuMaUs = u.getCpuPowerMaUs(which);
- if (userCpuTimeUs > 0 || systemCpuTimeUs > 0 || powerCpuMaUs > 0) {
+ if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) {
dumpLine(pw, uid, category, CPU_DATA, userCpuTimeUs / 1000, systemCpuTimeUs / 1000,
- powerCpuMaUs / 1000);
+ 0 /* old cpu power, keep for compatibility */);
}
final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
@@ -4608,6 +4606,10 @@
// Convert from microseconds to milliseconds with rounding
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
+ final Timer bgTimer = timer.getSubTimer();
+ final long bgTime = bgTimer != null ?
+ (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1;
+ final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
sb.setLength(0);
sb.append(prefix);
sb.append(" Sync ");
@@ -4618,6 +4620,13 @@
sb.append("realtime (");
sb.append(count);
sb.append(" times)");
+ if (bgTime > 0) {
+ sb.append(", ");
+ formatTimeMs(sb, bgTime);
+ sb.append("background (");
+ sb.append(bgCount);
+ sb.append(" times)");
+ }
} else {
sb.append("(not used)");
}
@@ -4758,17 +4767,13 @@
final long userCpuTimeUs = u.getUserCpuTimeUs(which);
final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
- final long powerCpuMaUs = u.getCpuPowerMaUs(which);
- if (userCpuTimeUs > 0 || systemCpuTimeUs > 0 || powerCpuMaUs > 0) {
+ if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) {
sb.setLength(0);
sb.append(prefix);
sb.append(" Total cpu time: u=");
formatTimeMs(sb, userCpuTimeUs / 1000);
sb.append("s=");
formatTimeMs(sb, systemCpuTimeUs / 1000);
- sb.append("p=");
- printmAh(sb, powerCpuMaUs / (1000.0 * 1000.0 * 60.0 * 60.0));
- sb.append("mAh");
pw.println(sb.toString());
}
diff --git a/core/java/android/os/ProxyFileDescriptorCallback.java b/core/java/android/os/ProxyFileDescriptorCallback.java
index 2e9f8d9..e69fb55 100644
--- a/core/java/android/os/ProxyFileDescriptorCallback.java
+++ b/core/java/android/os/ProxyFileDescriptorCallback.java
@@ -21,12 +21,14 @@
/**
* Callback that handles file system requests from ProxyFileDescriptor.
+ *
+ * @see android.os.storage.StorageManager#openProxyFileDescriptor(int, ProxyFileDescriptorCallback)
*/
public abstract class ProxyFileDescriptorCallback {
/**
* Returns size of bytes provided by the file descriptor.
- * @return Size of bytes
- * @throws ErrnoException
+ * @return Size of bytes.
+ * @throws ErrnoException ErrnoException containing E constants in OsConstants.
*/
public long onGetSize() throws ErrnoException {
throw new ErrnoException("onGetSize", OsConstants.EBADF);
@@ -35,11 +37,13 @@
/**
* Provides bytes read from file descriptor.
* It needs to return exact requested size of bytes unless it reaches file end.
- * @param offset Where to read bytes from.
+ * @param offset Offset in bytes from the file head specifying where to read bytes. If a seek
+ * operation is conducted on the file descriptor, then a read operation is requested, the
+ * offset refrects the proper position of requested bytes.
* @param size Size for read bytes.
* @param data Byte array to store read bytes.
* @return Size of bytes returned by the function.
- * @throws ErrnoException
+ * @throws ErrnoException ErrnoException containing E constants in OsConstants.
*/
public int onRead(long offset, int size, byte[] data) throws ErrnoException {
throw new ErrnoException("onRead", OsConstants.EBADF);
@@ -47,19 +51,23 @@
/**
* Handles bytes written to file descriptor.
- * @param offset Where to write bytes to.
+ * @param offset Offset in bytes from the file head specifying where to write bytes. If a seek
+ * operation is conducted on the file descriptor, then a write operation is requested, the
+ * offset refrects the proper position of requested bytes.
* @param size Size for write bytes.
* @param data Byte array to be written to somewhere.
* @return Size of bytes processed by the function.
- * @throws ErrnoException
+ * @throws ErrnoException ErrnoException containing E constants in OsConstants.
*/
public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
throw new ErrnoException("onWrite", OsConstants.EBADF);
}
/**
- * Processes fsync request.
- * @throws ErrnoException
+ * Ensures all the written data are stored in permanent storage device.
+ * For example, if it has data stored in on memory cache, it needs to flush data to storage
+ * device.
+ * @throws ErrnoException ErrnoException containing E constants in OsConstants.
*/
public void onFsync() throws ErrnoException {
throw new ErrnoException("onFsync", OsConstants.EINVAL);
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
new file mode 100644
index 0000000..1ef3916
--- /dev/null
+++ b/core/java/android/os/VintfObject.java
@@ -0,0 +1,64 @@
+/*
+ * 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.os;
+
+import java.util.ArrayList;
+
+import android.util.Log;
+
+/** @hide */
+public class VintfObject {
+
+ private static final String LOG_TAG = "VintfObject";
+
+ /**
+ * Slurps all device information (both manifests)
+ * and report it.
+ * If any error in getting one of the manifests, it is not included in
+ * the list.
+ */
+ public static String[] report() {
+ ArrayList<String> ret = new ArrayList<>();
+ put(ret, getDeviceManifest(), "device manifest");
+ put(ret, getFrameworkManifest(), "framework manifest");
+ return ret.toArray(new String[0]);
+ }
+
+ /**
+ * Verify that the given metadata for an OTA package is compatible with
+ * this device.
+ *
+ * @param packageInfo a list of serialized form of HalMaanifest's /
+ * CompatibilityMatri'ces (XML).
+ * @return = 0 if success (compatible)
+ * > 0 if incompatible
+ * < 0 if any error (mount partition fails, illformed XML, etc.)
+ */
+ public static native int verify(String[] packageInfo);
+
+ // return null if any error, otherwise XML string.
+ private static native String getDeviceManifest();
+ private static native String getFrameworkManifest();
+
+ private static void put(ArrayList<String> list, String content, String message) {
+ if (content == null || content.length() == 0) {
+ Log.e(LOG_TAG, "Cannot get;" + message + "; check native logs for details.");
+ return;
+ }
+ list.add(content);
+ }
+}
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
index a702cdb..afc9d78 100644
--- a/core/java/android/os/health/UidHealthStats.java
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -445,7 +445,10 @@
/**
* An estimate of the number of milliamp-microsends used by this uid.
+ *
+ * @deprecated this measurement is vendor-dependent and not reliable.
*/
+ @Deprecated
@HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
public static final int MEASUREMENT_CPU_POWER_MAMS = HealthKeys.BASE_UID + 64;
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index f9508902..e9ef770 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -394,6 +394,150 @@
}
/**
+ * Interface used to receive asynchronously fetched typefaces.
+ */
+ public static class FontRequestCallback {
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+ * provider was not found on the device.
+ */
+ public static final int FAIL_REASON_PROVIDER_NOT_FOUND = RESULT_CODE_PROVIDER_NOT_FOUND;
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+ * provider must be authenticated and the given certificates do not match its signature.
+ */
+ public static final int FAIL_REASON_WRONG_CERTIFICATES = RESULT_CODE_WRONG_CERTIFICATES;
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+ * returned by the provider was not loaded properly.
+ */
+ public static final int FAIL_REASON_FONT_LOAD_ERROR = -3;
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+ * provider did not return any results for the given query.
+ */
+ public static final int FAIL_REASON_FONT_NOT_FOUND = Columns.RESULT_CODE_FONT_NOT_FOUND;
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+ * provider found the queried font, but it is currently unavailable.
+ */
+ public static final int FAIL_REASON_FONT_UNAVAILABLE = Columns.RESULT_CODE_FONT_UNAVAILABLE;
+ /**
+ * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+ * query was not supported by the provider.
+ */
+ public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY;
+
+ /** @hide */
+ @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
+ FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
+ FAIL_REASON_MALFORMED_QUERY })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface FontRequestFailReason {}
+
+ public FontRequestCallback() {}
+
+ /**
+ * Called then a Typeface request done via {@link Typeface#create(FontRequest,
+ * FontRequestCallback)} is complete. Note that this method will not be called if
+ * {@link #onTypefaceRequestFailed(int)} is called instead.
+ * @param typeface The Typeface object retrieved.
+ */
+ public void onTypefaceRetrieved(Typeface typeface) {}
+
+ /**
+ * Called when a Typeface request done via {@link Typeface#create(FontRequest,
+ * FontRequestCallback)} fails.
+ * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
+ * {@link #FAIL_REASON_FONT_NOT_FOUND},
+ * {@link #FAIL_REASON_FONT_LOAD_ERROR},
+ * {@link #FAIL_REASON_FONT_UNAVAILABLE} or
+ * {@link #FAIL_REASON_MALFORMED_QUERY}.
+ */
+ public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {}
+ }
+
+ /**
+ * Create a typeface object given a font request. The font will be asynchronously fetched,
+ * therefore the result is delivered to the given callback. See {@link FontRequest}.
+ * Only one of the methods in callback will be invoked, depending on whether the request
+ * succeeds or fails. These calls will happen on the caller thread.
+ * @param context A context to be used for fetching from font provider.
+ * @param request A {@link FontRequest} object that identifies the provider and query for the
+ * request. May not be null.
+ * @param callback A callback that will be triggered when results are obtained. May not be null.
+ * @param handler A handler to be processed the font fetching.
+ */
+ public static void requestFont(@NonNull Context context, @NonNull FontRequest request,
+ @NonNull FontRequestCallback callback, @NonNull Handler handler) {
+
+ final Handler callerThreadHandler = new Handler();
+ handler.post(() -> {
+ // TODO: Cache the result.
+ FontFamilyResult result;
+ try {
+ result = fetchFonts(context, null /* cancellation signal */, request);
+ } catch (NameNotFoundException e) {
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND));
+ return;
+ }
+
+ if (result.getStatusCode() != FontFamilyResult.STATUS_OK) {
+ switch (result.getStatusCode()) {
+ case FontFamilyResult.STATUS_WRONG_CERTIFICATES:
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES));
+ return;
+ case FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED:
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
+ return;
+ default:
+ // fetchFont returns unexpected status type. Fallback to load error.
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
+ return;
+ }
+ }
+
+ final FontInfo[] fonts = result.getFonts();
+ if (fonts == null || fonts.length == 0) {
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND));
+ return;
+ }
+ for (final FontInfo font : fonts) {
+ if (font.getResultCode() != Columns.RESULT_CODE_OK) {
+ // We proceed if all font entry is ready to use. Otherwise report the first
+ // error.
+ final int resultCode = font.getResultCode();
+ if (resultCode < 0) {
+ // Negative values are reserved for internal errors. Fallback to load error.
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
+ } else {
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ resultCode));
+ }
+ return;
+ }
+ }
+
+ final Typeface typeface = buildTypeface(context, null /* cancellation signal */, fonts);
+ if (typeface == null) {
+ // Something went wrong during reading font files. This happens if the given font
+ // file is an unsupported font type.
+ callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
+ return;
+ }
+
+ callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface));
+ });
+ }
+
+ /**
* Fetch fonts given a font request.
*
* @param context A {@link Context} to be used for fetching fonts.
@@ -448,15 +592,11 @@
int weight, boolean italic, @Nullable String fallbackFontName) {
final Map<Uri, ByteBuffer> uriBuffer =
prepareFontData(context, fonts, cancellationSignal);
- Typeface typeface = new Typeface.Builder(fonts, uriBuffer)
+ return new Typeface.Builder(fonts, uriBuffer)
+ .setFallback(fallbackFontName)
.setWeight(weight)
.setItalic(italic)
.build();
- // TODO: Use Typeface fallback instead.
- if (typeface == null) {
- typeface = Typeface.create(fallbackFontName, Typeface.NORMAL);
- }
- return typeface;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6c46f2e..05bacae 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3150,6 +3150,13 @@
public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
/**
+ * @hide
+ * Acessibility volume. This is used internally, changing this
+ * value will not change the volume.
+ */
+ public static final String VOLUME_ACCESSIBILITY = "volume_a11y";
+
+ /**
* Master volume (float in the range 0.0f to 1.0f).
*
* @hide
@@ -3212,6 +3219,22 @@
};
/**
+ * @hide
+ * The mapping of stream type (integer) to its setting.
+ * Unlike the VOLUME_SETTINGS array, this one contains as many entries as
+ * AudioSystem.NUM_STREAM_TYPES, and has empty strings for stream types whose volumes
+ * are never persisted.
+ */
+ public static final String[] VOLUME_SETTINGS_INT = {
+ VOLUME_VOICE, VOLUME_SYSTEM, VOLUME_RING, VOLUME_MUSIC,
+ VOLUME_ALARM, VOLUME_NOTIFICATION, VOLUME_BLUETOOTH_SCO,
+ "" /*STREAM_SYSTEM_ENFORCED, no setting for this stream*/,
+ "" /*STREAM_DTMF, no setting for this stream*/,
+ "" /*STREAM_TTS, no setting for this stream*/,
+ VOLUME_ACCESSIBILITY
+ };
+
+ /**
* Appended to various volume related settings to record the previous
* values before they the settings were affected by a silent/vibrate
* ringer mode change.
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 4ae5a29..2c9bb66 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -187,6 +187,27 @@
/**
* If the value came from a resource, these are the configurations for
* which its contents can change.
+ *
+ * <p>For example, if a resource has a value defined for the -land resource qualifier,
+ * this field will have the {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION} bit set.
+ * </p>
+ *
+ * @see android.content.pm.ActivityInfo#CONFIG_MCC
+ * @see android.content.pm.ActivityInfo#CONFIG_MNC
+ * @see android.content.pm.ActivityInfo#CONFIG_LOCALE
+ * @see android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
+ * @see android.content.pm.ActivityInfo#CONFIG_KEYBOARD
+ * @see android.content.pm.ActivityInfo#CONFIG_KEYBOARD_HIDDEN
+ * @see android.content.pm.ActivityInfo#CONFIG_NAVIGATION
+ * @see android.content.pm.ActivityInfo#CONFIG_ORIENTATION
+ * @see android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
+ * @see android.content.pm.ActivityInfo#CONFIG_UI_MODE
+ * @see android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE
+ * @see android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
+ * @see android.content.pm.ActivityInfo#CONFIG_DENSITY
+ * @see android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
+ * @see android.content.pm.ActivityInfo#CONFIG_COLOR_MODE
+ *
*/
public @Config int changingConfigurations = -1;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 6dedbde..3e9fab1 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -205,6 +205,7 @@
* </p>
*
* @see #getFlags
+ * @hide
*/
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 7792939..f47c355 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -18,14 +18,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.ArraySet;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
/**
@@ -58,7 +61,7 @@
private final UserSpecifiedFocusComparator mUserSpecifiedClusterComparator =
new UserSpecifiedFocusComparator((r, v) -> isValidId(v.getNextClusterForwardId())
? v.findUserSetNextKeyboardNavigationCluster(r, View.FOCUS_FORWARD) : null);
- private final FocusComparator mFocusComparator = new FocusComparator();
+ private final FocusSorter mFocusSorter = new FocusSorter();
private final ArrayList<View> mTempList = new ArrayList<View>();
@@ -760,66 +763,102 @@
return id != 0 && id != View.NO_ID;
}
- static FocusComparator getFocusComparator(ViewGroup root, boolean isRtl) {
- FocusComparator comparator = getInstance().mFocusComparator;
- comparator.setRoot(root);
- comparator.setIsLayoutRtl(isRtl);
- return comparator;
- }
+ static final class FocusSorter {
+ private ArrayList<Rect> mRectPool = new ArrayList<>();
+ private int mLastPoolRect;
+ private int mRtlMult;
+ private HashMap<View, Rect> mRectByView = null;
- static final class FocusComparator implements Comparator<View> {
- private final Rect mFirstRect = new Rect();
- private final Rect mSecondRect = new Rect();
- private ViewGroup mRoot = null;
- private boolean mIsLayoutRtl;
-
- public void setIsLayoutRtl(boolean b) {
- mIsLayoutRtl = b;
- }
-
- public void setRoot(ViewGroup root) {
- mRoot = root;
- }
-
- public int compare(View first, View second) {
+ private Comparator<View> mTopsComparator = (first, second) -> {
if (first == second) {
return 0;
}
- getRect(first, mFirstRect);
- getRect(second, mSecondRect);
+ Rect firstRect = mRectByView.get(first);
+ Rect secondRect = mRectByView.get(second);
- if (mFirstRect.top < mSecondRect.top) {
- return -1;
- } else if (mFirstRect.top > mSecondRect.top) {
- return 1;
- } else if (mFirstRect.left < mSecondRect.left) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.left > mSecondRect.left) {
- return mIsLayoutRtl ? -1 : 1;
- } else if (mFirstRect.bottom < mSecondRect.bottom) {
- return -1;
- } else if (mFirstRect.bottom > mSecondRect.bottom) {
- return 1;
- } else if (mFirstRect.right < mSecondRect.right) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.right > mSecondRect.right) {
- return mIsLayoutRtl ? -1 : 1;
- } else {
- // The view are distinct but completely coincident so we consider
- // them equal for our purposes. Since the sort is stable, this
- // means that the views will retain their layout order relative to one another.
+ int result = firstRect.top - secondRect.top;
+ if (result == 0) {
+ return firstRect.bottom - secondRect.bottom;
+ }
+ return result;
+ };
+
+ private Comparator<View> mSidesComparator = (first, second) -> {
+ if (first == second) {
return 0;
}
- }
- private void getRect(View view, Rect rect) {
- view.getDrawingRect(rect);
- mRoot.offsetDescendantRectToMyCoords(view, rect);
+ Rect firstRect = mRectByView.get(first);
+ Rect secondRect = mRectByView.get(second);
+
+ int result = firstRect.left - secondRect.left;
+ if (result == 0) {
+ return firstRect.right - secondRect.right;
+ }
+ return mRtlMult * result;
+ };
+
+ public void sort(View[] views, int start, int end, ViewGroup root, boolean isRtl) {
+ int count = end - start;
+ if (count < 2) {
+ return;
+ }
+ if (mRectByView == null) {
+ mRectByView = new HashMap<>();
+ }
+ mRtlMult = isRtl ? -1 : 1;
+ for (int i = mRectPool.size(); i < count; ++i) {
+ mRectPool.add(new Rect());
+ }
+ for (int i = start; i < end; ++i) {
+ Rect next = mRectPool.get(mLastPoolRect++);
+ views[i].getDrawingRect(next);
+ root.offsetDescendantRectToMyCoords(views[i], next);
+ mRectByView.put(views[i], next);
+ }
+
+ // Sort top-to-bottom
+ Arrays.sort(views, start, count, mTopsComparator);
+ // Sweep top-to-bottom to identify rows
+ int sweepBottom = mRectByView.get(views[start]).bottom;
+ int rowStart = start;
+ int sweepIdx = start + 1;
+ for (; sweepIdx < end; ++sweepIdx) {
+ Rect currRect = mRectByView.get(views[sweepIdx]);
+ if (currRect.top >= sweepBottom) {
+ // Next view is on a new row, sort the row we've just finished left-to-right.
+ if ((sweepIdx - rowStart) > 1) {
+ Arrays.sort(views, rowStart, sweepIdx, mSidesComparator);
+ }
+ sweepBottom = currRect.bottom;
+ rowStart = sweepIdx;
+ } else {
+ // Next view vertically overlaps, we need to extend our "row height"
+ sweepBottom = Math.max(sweepBottom, currRect.bottom);
+ }
+ }
+ // Sort whatever's left (final row) left-to-right
+ if ((sweepIdx - rowStart) > 1) {
+ Arrays.sort(views, rowStart, sweepIdx, mSidesComparator);
+ }
+
+ mLastPoolRect = 0;
+ mRectByView.clear();
}
}
/**
+ * Public for testing.
+ *
+ * @hide
+ */
+ @TestApi
+ public static void sort(View[] views, int start, int end, ViewGroup root, boolean isRtl) {
+ getInstance().mFocusSorter.sort(views, start, end, root, isRtl);
+ }
+
+ /**
* Sorts views according to any explicitly-specified focus-chains. If there are no explicitly
* specified focus chains (eg. no nextFocusForward attributes defined), this should be a no-op.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9072bf9..8b3e3fe 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6970,7 +6970,10 @@
dispatchPopulateAccessibilityEvent(event);
}
// In the beginning we called #isShown(), so we know that getParent() is not null.
- getParent().requestSendAccessibilityEvent(this, event);
+ ViewParent parent = getParent();
+ if (parent != null) {
+ getParent().requestSendAccessibilityEvent(this, event);
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9e1ceee..f9eb25d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -61,7 +61,6 @@
import com.android.internal.R;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -1216,7 +1215,7 @@
children[count++] = child;
}
}
- Arrays.sort(children, 0, count, FocusFinder.getFocusComparator(this, false));
+ FocusFinder.sort(children, 0, count, this, isLayoutRtl());
for (int i = 0; i < count; ++i) {
children[i].addFocusables(views, direction, focusableMode);
}
@@ -1266,7 +1265,7 @@
visibleChildren[count++] = child;
}
}
- Arrays.sort(visibleChildren, 0, count, FocusFinder.getFocusComparator(this, false));
+ FocusFinder.sort(visibleChildren, 0, count, this, isLayoutRtl());
for (int i = 0; i < count; ++i) {
visibleChildren[i].addKeyboardNavigationClusters(views, direction);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e85a658..41c209c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1068,5 +1068,19 @@
});
}
}
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.mContext.getMainThreadHandler().post(() -> {
+ try {
+ afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
+ }
+ });
+ }
+ }
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 7bea174..176eaac 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -59,7 +59,12 @@
void requestHideFillUi(in IBinder windowToken, in AutofillId id);
/**
- * Nitifies no fill UI will be shown.
+ * Notifies no fill UI will be shown.
*/
void notifyNoFillUi(in IBinder windowToken, in AutofillId id);
+
+ /**
+ * Starts the provided intent sender
+ */
+ void startIntentSender(in IntentSender intentSender);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9a8131e..f9f10af 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,7 +16,6 @@
package android.widget;
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -59,6 +58,7 @@
import android.graphics.fonts.FontVariationAxis;
import android.icu.text.DecimalFormatSymbols;
import android.os.AsyncTask;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
@@ -408,7 +408,7 @@
public Drawables(Context context) {
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- mIsRtlCompatibilityMode = targetSdkVersion < JELLY_BEAN_MR1
+ mIsRtlCompatibilityMode = targetSdkVersion < VERSION_CODES.JELLY_BEAN_MR1
|| !context.getApplicationInfo().hasRtlSupport();
mOverride = false;
}
@@ -1363,7 +1363,7 @@
== (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
mUseInternationalizedInput =
- context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O;
+ context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.O;
if (inputMethod != null) {
Class<?> c;
@@ -1408,7 +1408,7 @@
} else if (numeric != 0) {
createEditorIfNeeded();
mEditor.mKeyListener = DigitsKeyListener.getInstance(
- mUseInternationalizedInput ? getTextLocale() : null,
+ null, // locale
(numeric & SIGNED) != 0,
(numeric & DECIMAL) != 0);
inputType = mEditor.mKeyListener.getInputType();
@@ -3400,11 +3400,14 @@
return mTextPaint.getTextLocales();
}
- private void changeListenerLocaleTo(@NonNull Locale locale) {
+ private void changeListenerLocaleTo(@Nullable Locale locale) {
if (mListenerChanged) {
// If a listener has been explicitly set, don't change it. We may break something.
return;
}
+ // The following null check is not absolutely necessary since all calling points of
+ // changeListenerLocaleTo() guarantee a non-null mEditor at the moment. But this is left
+ // here in case others would want to call this method in the future.
if (mEditor != null) {
KeyListener listener = mEditor.mKeyListener;
if (listener instanceof DigitsKeyListener) {
@@ -3418,8 +3421,17 @@
} else {
return;
}
+ final boolean wasPasswordType = isPasswordInputType(mEditor.mInputType);
setKeyListenerOnly(listener);
setInputTypeFromEditor();
+ if (wasPasswordType) {
+ final int newInputClass = mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS;
+ if (newInputClass == EditorInfo.TYPE_CLASS_TEXT) {
+ mEditor.mInputType |= EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
+ } else if (newInputClass == EditorInfo.TYPE_CLASS_NUMBER) {
+ mEditor.mInputType |= EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD;
+ }
+ }
}
}
@@ -3434,7 +3446,6 @@
public void setTextLocale(@NonNull Locale locale) {
mLocalesChanged = true;
mTextPaint.setTextLocale(locale);
- changeListenerLocaleTo(locale);
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -3456,7 +3467,6 @@
public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) {
mLocalesChanged = true;
mTextPaint.setTextLocales(locales);
- changeListenerLocaleTo(locales.get(0));
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -3468,9 +3478,7 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (!mLocalesChanged) {
- final LocaleList locales = LocaleList.getDefault();
- mTextPaint.setTextLocales(locales);
- changeListenerLocaleTo(locales.get(0));
+ mTextPaint.setTextLocales(LocaleList.getDefault());
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -5586,6 +5594,29 @@
mEditor.mInputType = type;
}
+ /**
+ * @return {@code null} if the key listener should use pre-O (locale-independent). Otherwise
+ * a {@code Locale} object that can be used to customize key various listeners.
+ * @see DateKeyListener#getInstance(Locale)
+ * @see DateTimeKeyListener#getInstance(Locale)
+ * @see DigitsKeyListener#getInstance(Locale)
+ * @see TimeKeyListener#getInstance(Locale)
+ */
+ @Nullable
+ private Locale getCustomLocaleForKeyListenerOrNull() {
+ if (!mUseInternationalizedInput) {
+ // If the application does not target O, stick to the previous behavior.
+ return null;
+ }
+ final LocaleList locales = getImeHintLocales();
+ if (locales == null) {
+ // If the application does not explicitly specify IME hint locale, also stick to the
+ // previous behavior.
+ return null;
+ }
+ return locales.get(0);
+ }
+
private void setInputType(int type, boolean direct) {
final int cls = type & EditorInfo.TYPE_MASK_CLASS;
KeyListener input;
@@ -5603,15 +5634,26 @@
}
input = TextKeyListener.getInstance(autotext, cap);
} else if (cls == EditorInfo.TYPE_CLASS_NUMBER) {
+ final Locale locale = getCustomLocaleForKeyListenerOrNull();
input = DigitsKeyListener.getInstance(
- mUseInternationalizedInput ? getTextLocale() : null,
+ locale,
(type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0,
(type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0);
- if (mUseInternationalizedInput) {
- type = input.getInputType(); // Override type, if necessary for i18n.
+ if (locale != null) {
+ // Override type, if necessary for i18n.
+ int newType = input.getInputType();
+ final int newClass = newType & EditorInfo.TYPE_MASK_CLASS;
+ if (newClass != EditorInfo.TYPE_CLASS_NUMBER) {
+ // The class is different from the original class. So we need to override
+ // 'type'. But we want to keep the password flag if it's there.
+ if ((type & EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD) != 0) {
+ newType |= EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
+ }
+ type = newType;
+ }
}
} else if (cls == EditorInfo.TYPE_CLASS_DATETIME) {
- final Locale locale = mUseInternationalizedInput ? getTextLocale() : null;
+ final Locale locale = getCustomLocaleForKeyListenerOrNull();
switch (type & EditorInfo.TYPE_MASK_VARIATION) {
case EditorInfo.TYPE_DATETIME_VARIATION_DATE:
input = DateKeyListener.getInstance(locale);
@@ -5884,6 +5926,9 @@
* Change "hint" locales associated with the text view, which will be reported to an IME with
* {@link EditorInfo#hintLocales} when it has focus.
*
+ * Starting with Android O, this also causes internationalized listeners to be created (or
+ * change locale) based on the first locale in the input locale list.
+ *
* <p><strong>Note:</strong> If you want new "hint" to take effect immediately you need to
* call {@link InputMethodManager#restartInput(View)}.</p>
* @param hintLocales List of the languages that the user is supposed to switch to no matter
@@ -5895,6 +5940,9 @@
createEditorIfNeeded();
mEditor.createInputContentTypeIfNeeded();
mEditor.mInputContentType.imeHintLocales = hintLocales;
+ if (mUseInternationalizedInput) {
+ changeListenerLocaleTo(hintLocales == null ? null : hintLocales.get(0));
+ }
}
/**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fe38605..7fbfb8b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -114,7 +114,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 153 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 154 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -5463,7 +5463,6 @@
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
- LongSamplingCounter mCpuPower;
LongSamplingCounter[][] mCpuClusterSpeed;
/**
@@ -5474,7 +5473,7 @@
/**
* The statistics we have collected for this uid's syncs.
*/
- final OverflowArrayMap<StopwatchTimer> mSyncStats;
+ final OverflowArrayMap<DualTimer> mSyncStats;
/**
* The statistics we have collected for this uid's jobs.
@@ -5511,17 +5510,16 @@
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- mCpuPower = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@Override public Wakelock instantiateObject() {
return new Wakelock(mBsi, Uid.this);
}
};
- mSyncStats = mBsi.new OverflowArrayMap<StopwatchTimer>(uid) {
- @Override public StopwatchTimer instantiateObject() {
- return new StopwatchTimer(mBsi.mClocks, Uid.this, SYNC, null,
- mBsi.mOnBatteryTimeBase);
+ mSyncStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
+ @Override public DualTimer instantiateObject() {
+ return new DualTimer(mBsi.mClocks, Uid.this, SYNC, null,
+ mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
};
mJobStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
@@ -6162,11 +6160,6 @@
}
@Override
- public long getCpuPowerMaUs(int which) {
- return mCpuPower.getCountLocked(which);
- }
-
- @Override
public long getTimeAtCpuSpeed(int cluster, int step, int which) {
if (mCpuClusterSpeed != null) {
if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
@@ -6311,7 +6304,6 @@
mUserCpuTime.reset(false);
mSystemCpuTime.reset(false);
- mCpuPower.reset(false);
if (mCpuClusterSpeed != null) {
for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
@@ -6338,9 +6330,9 @@
}
}
mWakelockStats.cleanup();
- final ArrayMap<String, StopwatchTimer> syncStats = mSyncStats.getMap();
+ final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
for (int is=syncStats.size()-1; is>=0; is--) {
- StopwatchTimer timer = syncStats.valueAt(is);
+ DualTimer timer = syncStats.valueAt(is);
if (timer.reset(false)) {
syncStats.removeAt(is);
timer.detach();
@@ -6477,7 +6469,6 @@
mUserCpuTime.detach();
mSystemCpuTime.detach();
- mCpuPower.detach();
if (mCpuClusterSpeed != null) {
for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
@@ -6510,12 +6501,12 @@
wakelock.writeToParcelLocked(out, elapsedRealtimeUs);
}
- final ArrayMap<String, StopwatchTimer> syncStats = mSyncStats.getMap();
+ final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
int NS = syncStats.size();
out.writeInt(NS);
for (int is=0; is<NS; is++) {
out.writeString(syncStats.keyAt(is));
- StopwatchTimer timer = syncStats.valueAt(is);
+ DualTimer timer = syncStats.valueAt(is);
Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
}
@@ -6677,7 +6668,6 @@
mUserCpuTime.writeToParcel(out);
mSystemCpuTime.writeToParcel(out);
- mCpuPower.writeToParcel(out);
if (mCpuClusterSpeed != null) {
out.writeInt(1);
@@ -6734,8 +6724,8 @@
for (int j = 0; j < numSyncs; j++) {
String syncName = in.readString();
if (in.readInt() != 0) {
- mSyncStats.add(syncName,
- new StopwatchTimer(mBsi.mClocks, Uid.this, SYNC, null, timeBase, in));
+ mSyncStats.add(syncName, new DualTimer(mBsi.mClocks, Uid.this, SYNC, null,
+ mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
}
}
@@ -6913,7 +6903,6 @@
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
- mCpuPower = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
if (in.readInt() != 0) {
int numCpuClusters = in.readInt();
@@ -8002,7 +7991,7 @@
}
public void readSyncSummaryFromParcelLocked(String name, Parcel in) {
- StopwatchTimer timer = mSyncStats.instantiateObject();
+ DualTimer timer = mSyncStats.instantiateObject();
timer.readSummaryFromParcelLocked(in);
mSyncStats.add(name, timer);
}
@@ -8055,14 +8044,14 @@
}
public void noteStartSyncLocked(String name, long elapsedRealtimeMs) {
- StopwatchTimer t = mSyncStats.startObject(name);
+ DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
}
}
public void noteStopSyncLocked(String name, long elapsedRealtimeMs) {
- StopwatchTimer t = mSyncStats.stopObject(name);
+ DualTimer t = mSyncStats.stopObject(name);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
}
@@ -9705,8 +9694,7 @@
mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
new KernelUidCpuTimeReader.Callback() {
@Override
- public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs,
- long powerMaUs) {
+ public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) {
final Uid u = getUidStatsLocked(mapUid(uid));
// Accumulate the total system and user time.
@@ -9720,7 +9708,7 @@
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
- sb.append(" p=").append(powerMaUs / 1000).append("mAms\n");
+ sb.append("\n");
}
if (numWakelocksF > 0) {
@@ -9736,13 +9724,11 @@
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
- sb.append(" p=").append(powerMaUs / 1000).append("mAms");
Slog.d(TAG, sb.toString());
}
u.mUserCpuTime.addCountLocked(userTimeUs);
u.mSystemCpuTime.addCountLocked(systemTimeUs);
- u.mCpuPower.addCountLocked(powerMaUs);
// Add the cpu speeds to this UID. These are used as a ratio
// for computing the power this UID used.
@@ -11036,7 +11022,6 @@
u.mUserCpuTime.readSummaryFromParcelLocked(in);
u.mSystemCpuTime.readSummaryFromParcelLocked(in);
- u.mCpuPower.readSummaryFromParcelLocked(in);
if (in.readInt() != 0) {
final int numClusters = in.readInt();
@@ -11432,7 +11417,6 @@
u.mUserCpuTime.writeSummaryFromParcelLocked(out);
u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
- u.mCpuPower.writeSummaryFromParcelLocked(out);
if (u.mCpuClusterSpeed != null) {
out.writeInt(1);
@@ -11503,7 +11487,7 @@
}
}
- final ArrayMap<String, StopwatchTimer> syncStats = u.mSyncStats.getMap();
+ final ArrayMap<String, DualTimer> syncStats = u.mSyncStats.getMap();
int NS = syncStats.size();
out.writeInt(NS);
for (int is=0; is<NS; is++) {
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index e8919ed..181e1ac 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -50,14 +50,12 @@
* @param uid UID of the app
* @param userTimeUs time spent executing in user space in microseconds
* @param systemTimeUs time spent executing in kernel space in microseconds
- * @param powerMaUs power consumed executing, in milli-ampere microseconds
*/
- void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs, long powerMaUs);
+ void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
}
private SparseLongArray mLastUserTimeUs = new SparseLongArray();
private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
- private SparseLongArray mLastPowerMaUs = new SparseLongArray();
private long mLastTimeReadUs = 0;
/**
@@ -77,26 +75,18 @@
final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
final long userTimeUs = Long.parseLong(splitter.next(), 10);
final long systemTimeUs = Long.parseLong(splitter.next(), 10);
- final long powerMaUs;
- if (splitter.hasNext()) {
- powerMaUs = Long.parseLong(splitter.next(), 10) / 1000;
- } else {
- powerMaUs = 0;
- }
// Only report if there is a callback and if this is not the first read.
if (callback != null && mLastTimeReadUs != 0) {
long userTimeDeltaUs = userTimeUs;
long systemTimeDeltaUs = systemTimeUs;
- long powerDeltaMaUs = powerMaUs;
int index = mLastUserTimeUs.indexOfKey(uid);
if (index >= 0) {
userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
- powerDeltaMaUs -= mLastPowerMaUs.valueAt(index);
final long timeDiffUs = nowUs - mLastTimeReadUs;
- if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0 || powerDeltaMaUs < 0) {
+ if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) {
StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
sb.append(uid).append("!\n");
sb.append("Time between reads: ");
@@ -106,36 +96,28 @@
TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
- sb.append(" p=").append(mLastPowerMaUs.valueAt(index) / 1000);
- sb.append("mAms\n");
- sb.append("Current times: u=");
+ sb.append("\nCurrent times: u=");
TimeUtils.formatDuration(userTimeUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeUs / 1000, sb);
- sb.append(" p=").append(powerMaUs / 1000);
- sb.append("mAms\n");
- sb.append("Delta: u=");
+ sb.append("\nDelta: u=");
TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
sb.append(" s=");
TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
- sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
Slog.e(TAG, sb.toString());
userTimeDeltaUs = 0;
systemTimeDeltaUs = 0;
- powerDeltaMaUs = 0;
}
}
- if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0 || powerDeltaMaUs != 0) {
- callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs,
- powerDeltaMaUs);
+ if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
+ callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
}
}
mLastUserTimeUs.put(uid, userTimeUs);
mLastSystemTimeUs.put(uid, systemTimeUs);
- mLastPowerMaUs.put(uid, powerMaUs);
}
} catch (IOException e) {
Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
@@ -152,7 +134,6 @@
if (index >= 0) {
mLastUserTimeUs.removeAt(index);
mLastSystemTimeUs.removeAt(index);
- mLastPowerMaUs.removeAt(index);
}
try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 44fa99d..142effb 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -186,8 +186,8 @@
private static void preloadOpenGL() {
String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
- if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false) ||
- driverPackageName == null || driverPackageName.isEmpty()) {
+ if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false) &&
+ (driverPackageName == null || driverPackageName.isEmpty())) {
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
}
}
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index a208ccb..e349f3d 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -55,4 +55,25 @@
&& maskedEquals(a.getMostSignificantBits(), b.getMostSignificantBits(),
mask.getMostSignificantBits());
}
+
+ public static int[] unpackBits(long val) {
+ int size = Long.bitCount(val);
+ int[] result = new int[size];
+ int index = 0;
+ int bitPos = 0;
+ while (val > 0) {
+ if ((val & 1) == 1) result[index++] = bitPos;
+ val = val >> 1;
+ bitPos++;
+ }
+ return result;
+ }
+
+ public static long packBits(int[] bits) {
+ long packed = 0;
+ for (int b : bits) {
+ packed |= (1 << b);
+ }
+ return packed;
+ }
}
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 4659d3c..ce89501 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -121,12 +121,15 @@
final String[] pkgs = context.getPackageManager().getPackagesForUid(uid);
if (pkgs != null) {
for (String pkg : pkgs) {
- if (appOps.checkOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, uid,
- pkg) == AppOpsManager.MODE_ALLOWED) {
- appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, uid, pkg);
- if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with "
- + "android:get_usage_stats access");
- return true;
+ switch (appOps.checkOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, uid, pkg)) {
+ case AppOpsManager.MODE_ALLOWED:
+ if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with "
+ + "android:get_usage_stats allowed");
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with "
+ + "android:get_usage_stats default");
+ return true;
}
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 33fabfc..96285cd 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -96,6 +96,7 @@
android_os_SystemProperties.cpp \
android_os_Trace.cpp \
android_os_UEventObserver.cpp \
+ android_os_VintfObject.cpp \
android_net_LocalSocketImpl.cpp \
android_net_NetUtils.cpp \
android_net_TrafficStats.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e237ce9..8ca4794 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -159,6 +159,7 @@
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_SELinux(JNIEnv* env);
+extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_seccomp(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
@@ -1302,6 +1303,7 @@
REG_JNI(register_android_os_HwBlob),
REG_JNI(register_android_os_HwParcel),
REG_JNI(register_android_os_HwRemoteBinder),
+ REG_JNI(register_android_os_VintfObject),
REG_JNI(register_android_nio_utils),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_Graphics),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 3010dc1..fc90fb3 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -45,21 +45,24 @@
constexpr jint RESOLVE_BY_FONT_TABLE = -1;
struct NativeFamilyBuilder {
+ NativeFamilyBuilder(uint32_t langId, int variant)
+ : langId(langId), variant(variant), allowUnsupportedFont(false) {}
uint32_t langId;
int variant;
+ bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
- NativeFamilyBuilder* builder = new NativeFamilyBuilder();
+ NativeFamilyBuilder* builder;
if (lang != nullptr) {
ScopedUtfChars str(env, lang);
- builder->langId = minikin::FontStyle::registerLanguageList(str.c_str());
+ builder = new NativeFamilyBuilder(
+ minikin::FontStyle::registerLanguageList(str.c_str()), variant);
} else {
- builder->langId = minikin::FontStyle::registerLanguageList("");
+ builder = new NativeFamilyBuilder(minikin::FontStyle::registerLanguageList(""), variant);
}
- builder->variant = variant;
return reinterpret_cast<jlong>(builder);
}
@@ -67,12 +70,22 @@
if (builderPtr == 0) {
return 0;
}
+ std::unique_ptr<NativeFamilyBuilder> builder(
+ reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+ std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
+ builder->langId, builder->variant, std::move(builder->fonts));
+ if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+ return 0;
+ }
+ return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
+ if (builderPtr == 0) {
+ return;
+ }
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- FontFamilyWrapper* family = new FontFamilyWrapper(
- std::make_shared<minikin::FontFamily>(
- builder->langId, builder->variant, std::move(builder->fonts)));
- delete builder;
- return reinterpret_cast<jlong>(family);
+ builder->allowUnsupportedFont = true;
}
static void FontFamily_abort(jlong builderPtr) {
@@ -258,6 +271,7 @@
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
+ { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index d0b07d0..95d43c6 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -42,6 +42,13 @@
return reinterpret_cast<jlong>(face);
}
+static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance,
+ jint weight, jboolean italic) {
+ Typeface* baseTypeface = reinterpret_cast<Typeface*>(nativeInstance);
+ return reinterpret_cast<jlong>(
+ Typeface::createFromTypefaceWithStyle(baseTypeface, weight, italic));
+}
+
static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle,
jobject listOfAxis) {
std::vector<minikin::FontVariation> variations;
@@ -75,6 +82,11 @@
return face->fSkiaStyle;
}
+static jint Typeface_getBaseWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
+ Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
+ return face->fBaseWeight;
+}
+
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray) {
ScopedLongArrayRO families(env, familyArray);
std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
@@ -113,11 +125,14 @@
static const JNINativeMethod gTypefaceMethods[] = {
{ "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
+ { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
+ (void*)Typeface_createFromTypefaceWithExactStyle },
{ "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
(void*)Typeface_createFromTypefaceWithVariation },
{ "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
{ "nativeUnref", "(J)V", (void*)Typeface_unref },
{ "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
+ { "nativeGetBaseWeight", "(J)I", (void*)Typeface_getBaseWeight },
{ "nativeCreateFromArray", "([J)J",
(void*)Typeface_createFromArray },
{ "nativeSetDefault", "(J)V", (void*)Typeface_setDefault },
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
new file mode 100644
index 0000000..1883ecb
--- /dev/null
+++ b/core/jni/android_os_VintfObject.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VintfObject"
+//#define LOG_NDEBUG 0
+
+#include <JNIHelp.h>
+#include <vintf/VintfObject.h>
+#include <vintf/parse_xml.h>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using vintf::HalManifest;
+using vintf::RuntimeInfo;
+using vintf::VintfObject;
+using vintf::gHalManifestConverter;
+
+static jstring android_os_VintfObject_getDeviceManifest(JNIEnv* env, jclass clazz)
+{
+ const HalManifest *manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr) {
+ return nullptr;
+ }
+ std::string xml = gHalManifestConverter(*manifest);
+ return env->NewStringUTF(xml.c_str());
+}
+
+static jstring android_os_VintfObject_getFrameworkManifest(JNIEnv* env, jclass clazz)
+{
+ const HalManifest *manifest = VintfObject::GetFrameworkHalManifest();
+ if (manifest == nullptr) {
+ return nullptr;
+ }
+ std::string xml = gHalManifestConverter(*manifest);
+ return env->NewStringUTF(xml.c_str());
+}
+
+static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArray packageInfo) {
+ size_t count = env->GetArrayLength(packageInfo);
+ std::vector<std::string> cPackageInfo{count};
+ for (size_t i = 0; i < count; ++i) {
+ jstring element = (jstring)env->GetObjectArrayElement(packageInfo, i);
+ const char *cString = env->GetStringUTFChars(element, NULL /* isCopy */);
+ cPackageInfo[i] = cString;
+ env->ReleaseStringUTFChars(element, cString);
+ }
+ int32_t status = VintfObject::CheckCompatibility(cPackageInfo, false /* mount */);
+ return status;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gVintfObjectMethods[] = {
+ {"getDeviceManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getDeviceManifest},
+ {"getFrameworkManifest", "()Ljava/lang/String;", (void*)android_os_VintfObject_getFrameworkManifest},
+ {"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify},
+};
+
+const char* const kVintfObjectPathName = "android/os/VintfObject";
+
+int register_android_os_VintfObject(JNIEnv* env)
+{
+ return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
+ NELEM(gVintfObjectMethods));
+}
+
+};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8895877..f92a46b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -37,6 +37,7 @@
<protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
<protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_ADDED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
<protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
@@ -491,7 +492,9 @@
<protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />
<protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
+ <protected-broadcast android:name="android.accounts.action.ACCOUNT_REMOVED" />
<protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" />
+
<protected-broadcast android:name="com.android.sync.SYNC_CONN_STATUS_CHANGED" />
<protected-broadcast android:name="com.android.phone.SIP_INCOMING_CALL" />
@@ -535,6 +538,7 @@
<protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT" />
<protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED" />
<protected-broadcast android:name="android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
+ <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
diff --git a/core/tests/coretests/src/android/database/PageViewCursorTest.java b/core/tests/coretests/src/android/database/PageViewCursorTest.java
deleted file mode 100644
index fba6aaf..0000000
--- a/core/tests/coretests/src/android/database/PageViewCursorTest.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database;
-
-import static com.android.internal.util.ArrayUtils.contains;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-import android.util.MathUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.Random;
-
-@RunWith(AndroidJUnit4.class)
-public class PageViewCursorTest {
-
- private static final int ITEM_COUNT = 20;
-
- private static final String NAME_COLUMN = "name";
- private static final String NUM_COLUMN = "num";
-
- private static final String[] COLUMNS = new String[] {
- NAME_COLUMN,
- NUM_COLUMN
- };
-
- private static final String[] NAMES = new String[] {
- "000",
- "111",
- "222",
- "333",
- "444",
- "555",
- "666",
- "777",
- "888",
- "999",
- "aaa",
- "bbb",
- "ccc",
- "ddd",
- "eee",
- "fff",
- "ggg",
- "hhh",
- "iii",
- "jjj"
- };
-
- private MatrixCursor mDelegate;
- private PageViewCursor mCursor;
-
- @Before
- public void setUp() {
- Random rand = new Random();
-
- mDelegate = new MatrixCursor(COLUMNS);
- for (int i = 0; i < ITEM_COUNT; i++) {
- MatrixCursor.RowBuilder row = mDelegate.newRow();
- row.add(NAME_COLUMN, NAMES[i]);
- row.add(NUM_COLUMN, rand.nextInt());
- }
-
- mCursor = new PageViewCursor(mDelegate, createArgs(10, 5));
- }
-
- @Test
- public void testPage_Size() {
- assertEquals(5, mCursor.getCount());
- }
-
- @Test
- public void testPage_TotalSize() {
- assertEquals(ITEM_COUNT, mCursor.getExtras().getInt(ContentResolver.EXTRA_TOTAL_SIZE));
- }
-
- @Test
- public void testPage_OffsetExceedsCursorCount_EffectivelyEmptyCursor() {
- mCursor = new PageViewCursor(mDelegate, createArgs(ITEM_COUNT * 2, 5));
- assertEquals(0, mCursor.getCount());
- }
-
- @Test
- public void testMoveToPosition() {
- assertTrue(mCursor.moveToPosition(0));
- assertEquals(NAMES[10], mCursor.getString(0));
- assertTrue(mCursor.moveToPosition(1));
- assertEquals(NAMES[11], mCursor.getString(0));
- assertTrue(mCursor.moveToPosition(4));
- assertEquals(NAMES[14], mCursor.getString(0));
-
- // and then back down again for good measure.
- assertTrue(mCursor.moveToPosition(1));
- assertEquals(NAMES[11], mCursor.getString(0));
- assertTrue(mCursor.moveToPosition(0));
- assertEquals(NAMES[10], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToPosition_MoveToSamePosition_NoOp() {
- assertTrue(mCursor.moveToPosition(1));
- assertEquals(NAMES[11], mCursor.getString(0));
- assertTrue(mCursor.moveToPosition(1));
- assertEquals(NAMES[11], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToPosition_PositionOutOfBounds_MovesToBeforeFirst() {
- assertTrue(mCursor.moveToPosition(0));
- assertEquals(NAMES[10], mCursor.getString(0));
-
- // move before
- assertFalse(mCursor.moveToPosition(-12));
- assertTrue(mCursor.isBeforeFirst());
- }
-
- @Test
- public void testMoveToPosition_PositionOutOfBounds_MovesToAfterLast() {
- assertTrue(mCursor.moveToPosition(0));
- assertEquals(NAMES[10], mCursor.getString(0));
-
- assertFalse(mCursor.moveToPosition(222));
- assertTrue(mCursor.isAfterLast());
- }
-
- @Test
- public void testPosition() {
- assertEquals(-1, mCursor.getPosition());
- }
-
- @Test
- public void testIsBeforeFirst() {
- assertTrue(mCursor.isBeforeFirst());
- mCursor.moveToFirst();
- assertFalse(mCursor.isBeforeFirst());
- }
-
- @Test
- public void testCount_ZeroForEmptyCursor() {
- mCursor = new PageViewCursor(mDelegate, createArgs(0, 0));
- assertEquals(0, mCursor.getCount());
- }
-
- @Test
- public void testIsBeforeFirst_TrueForEmptyCursor() {
- mCursor = new PageViewCursor(mDelegate, createArgs(0, 0));
- assertTrue(mCursor.isBeforeFirst());
- }
-
- @Test
- public void testIsAfterLast() {
- assertFalse(mCursor.isAfterLast());
- mCursor.moveToLast();
- mCursor.moveToNext();
- assertTrue(mCursor.isAfterLast());
- }
-
- @Test
- public void testIsAfterLast_TrueForEmptyCursor() {
- mCursor = new PageViewCursor(mDelegate, createArgs(0, 0));
- assertTrue(mCursor.isAfterLast());
- }
-
- @Test
- public void testIsFirst() {
- assertFalse(mCursor.isFirst());
- mCursor.moveToFirst();
- assertTrue(mCursor.isFirst());
- }
-
- @Test
- public void testIsLast() {
- assertFalse(mCursor.isLast());
- mCursor.moveToLast();
- assertTrue(mCursor.isLast());
- }
-
- @Test
- public void testMove() {
- // note that initial position is -1, so moving
- // 2 will only put as at 1.
- mCursor.move(2);
- assertEquals(NAMES[11], mCursor.getString(0));
- mCursor.move(-1);
- assertEquals(NAMES[10], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToFist() {
- mCursor.moveToPosition(3);
- mCursor.moveToFirst();
- assertEquals(NAMES[10], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToLast() {
- mCursor.moveToLast();
- assertEquals(NAMES[14], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToNext() {
- // default position is -1, so next is 0.
- mCursor.moveToNext();
- assertEquals(NAMES[10], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToNext_AfterLastReturnsFalse() {
- mCursor.moveToLast();
- assertFalse(mCursor.moveToNext());
- }
-
- @Test
- public void testMoveToPrevious() {
- mCursor.moveToPosition(3);
- mCursor.moveToPrevious();
- assertEquals(NAMES[12], mCursor.getString(0));
- }
-
- @Test
- public void testMoveToPrevious_BeforeFirstReturnsFalse() {
- assertFalse(mCursor.moveToPrevious());
- }
-
- @Test
- public void testWindow_ReadPastEnd() {
- assertFalse(mCursor.moveToPosition(10));
- }
-
- @Test
- public void testLimitOutOfBounds() {
- mCursor = new PageViewCursor(mDelegate, createArgs(5, 100));
- assertEquals(15, mCursor.getCount());
- }
-
- @Test
- public void testOffsetOutOfBounds_EmptyResult() {
- mCursor = new PageViewCursor(mDelegate, createArgs(100000, 100));
- assertEquals(0, mCursor.getCount());
- }
-
- @Test
- public void testAddsExtras() {
- mCursor = new PageViewCursor(mDelegate, createArgs(5, 100));
- assertTrue(mCursor.getExtras().getBoolean(PageViewCursor.EXTRA_AUTO_PAGED));
- String[] honoredArgs = mCursor.getExtras()
- .getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
- assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET));
- assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT));
- }
-
- @Test
- public void testAddsExtras_OnlyOffset() {
- Bundle args = new Bundle();
- args.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
- mCursor = new PageViewCursor(mDelegate, args);
- String[] honoredArgs = mCursor.getExtras()
- .getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
- assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET));
- assertFalse(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT));
- }
-
- @Test
- public void testAddsExtras_OnlyLimit() {
- Bundle args = new Bundle();
- args.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
- mCursor = new PageViewCursor(mDelegate, args);
- String[] honoredArgs = mCursor.getExtras()
- .getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
- assertFalse(contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET));
- assertTrue(contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT));
- }
-
- @Test
- public void testGetWindow() {
- mCursor = new PageViewCursor(mDelegate, createArgs(5, 5));
- CursorWindow window = mCursor.getWindow();
- assertEquals(5, window.getNumRows());
- }
-
- @Test
- public void testWraps() {
- Bundle args = createArgs(5, 5);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(wrapped instanceof PageViewCursor);
- assertEquals(5, wrapped.getCount());
- }
-
- @Test
- public void testWraps_NullExtras() {
- Bundle args = createArgs(5, 5);
- mDelegate.setExtras(null);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(wrapped instanceof PageViewCursor);
- assertEquals(5, wrapped.getCount());
- }
-
- @Test
- public void testWraps_WithJustOffset() {
- Bundle args = new Bundle();
- args.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(wrapped instanceof PageViewCursor);
- assertEquals(15, wrapped.getCount());
- }
-
- @Test
- public void testWraps_WithJustLimit() {
- Bundle args = new Bundle();
- args.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(wrapped instanceof PageViewCursor);
- assertEquals(5, wrapped.getCount());
- }
-
- @Test
- public void testNoWrap_WithoutPagingArgs() {
- Cursor wrapped = PageViewCursor.wrap(mDelegate, Bundle.EMPTY);
- assertTrue(mDelegate == wrapped);
- }
-
- @Test
- public void testNoWrap_CursorsHasExistingPaging_ByTotalSize() {
- Bundle extras = new Bundle();
- extras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, 5);
- mDelegate.setExtras(extras);
-
- Bundle args = createArgs(5, 5);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(mDelegate == wrapped);
- }
-
- @Test
- public void testNoWrap_CursorsHasExistingPaging_ByHonoredArgs() {
- Bundle extras = new Bundle();
- extras.putStringArray(
- ContentResolver.EXTRA_HONORED_ARGS,
- new String[] {
- ContentResolver.QUERY_ARG_OFFSET,
- ContentResolver.QUERY_ARG_LIMIT
- });
- mDelegate.setExtras(extras);
-
- Bundle args = createArgs(5, 5);
- Cursor wrapped = PageViewCursor.wrap(mDelegate, args);
- assertTrue(mDelegate == wrapped);
- }
-
- private static Bundle createArgs(int offset, int limit) {
- Bundle args = new Bundle();
- args.putInt(ContentResolver.QUERY_ARG_OFFSET, offset);
- args.putInt(ContentResolver.QUERY_ARG_LIMIT, limit);
- return args;
- }
-
- private void assertStringAt(int row, int column, String expected) {
- mCursor.moveToPosition(row);
- assertEquals(expected, mCursor.getString(column));
- }
-}
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
new file mode 100644
index 0000000..aaaf55c
--- /dev/null
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -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.
+ */
+
+package android.os;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class VintfObjectTest extends TestCase {
+ public void testReport() {
+ String[] xmls = VintfObject.report();
+ assertTrue(xmls.length > 0);
+ // From /system/manifest.xml
+ assertTrue(String.join("", xmls).contains(
+ "<manifest version=\"1.0\" type=\"framework\">"));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index 2b0ae21..d8de70c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -249,4 +249,65 @@
assertEquals(2, bi.getUidStats().get(UID).getJobStats().size());
bi.noteJobFinishLocked(jobName2, UID);
}
+
+ @SmallTest
+ public void testSyncs() throws Exception {
+ final MockClocks clocks = new MockClocks();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ final String syncName = "sync_name";
+ long curr = 0; // realtime in us
+
+ // On battery
+ curr = 1000 * (clocks.realtime = clocks.uptime = 100);
+ bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ // App in foreground
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+
+ // Start timer
+ curr = 1000 * (clocks.realtime = clocks.uptime = 151);
+ bi.noteSyncStartLocked(syncName, UID);
+
+ // Stop timer
+ curr = 1000 * (clocks.realtime = clocks.uptime = 161);
+ bi.noteSyncFinishLocked(syncName, UID);
+
+ // Move to background
+ curr = 1000 * (clocks.realtime = clocks.uptime = 202);
+ bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+ // Start timer
+ curr = 1000 * (clocks.realtime = clocks.uptime = 254);
+ bi.noteSyncStartLocked(syncName, UID);
+
+ // Off battery
+ curr = 1000 * (clocks.realtime = clocks.uptime = 305);
+ bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+
+ // Stop timer
+ curr = 1000 * (clocks.realtime = clocks.uptime = 409);
+ bi.noteSyncFinishLocked(syncName, UID);
+
+ // Test
+ curr = 1000 * (clocks.realtime = clocks.uptime = 657);
+ final ArrayMap<String, ? extends BatteryStats.Timer> syncs =
+ bi.getUidStats().get(UID).getSyncStats();
+ assertEquals(1, syncs.size());
+ BatteryStats.Timer timer = syncs.valueAt(0);
+ BatteryStats.Timer bgTimer = timer.getSubTimer();
+ long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED);
+ int count = timer.getCountLocked(STATS_SINCE_CHARGED);
+ int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED);
+ long bgTime = bgTimer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED);
+ assertEquals((161 - 151 + 305 - 254) * 1000, time);
+ assertEquals(2, count);
+ assertEquals(1, bgCount);
+ assertEquals((305 - 254) * 1000, bgTime);
+
+ // Test that a second sync is separate.
+ curr = 1000 * (clocks.realtime = clocks.uptime = 3000);
+ final String syncName2 = "second_sync";
+ bi.noteSyncStartLocked(syncName2, UID);
+ assertEquals(2, bi.getUidStats().get(UID).getSyncStats().size());
+ bi.noteSyncFinishLocked(syncName2, UID);
+ }
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 3dab1f7..39b0bc7 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -277,6 +277,9 @@
<font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
+ <family lang="und-Adlm">
+ <font weight="400" style="normal">NotoSansAdlam-Regular.ttf</font>
+ </family>
<family lang="und-Avst">
<font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
</family>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 4fc63ea..8f78319 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -885,8 +885,32 @@
}
/**
- * Returns the name of this color space. The name is never null
- * and contains always at least 1 character.
+ * <p>Returns the name of this color space. The name is never null
+ * and contains always at least 1 character.</p>
+ *
+ * <p>Color space names are recommended to be unique but are not
+ * guaranteed to be. There is no defined format but the name usually
+ * falls in one of the following categories:</p>
+ * <ul>
+ * <li>Generic names used to identify color spaces in non-RGB
+ * color models. For instance: {@link Named#CIE_LAB Generic L*a*b*}.</li>
+ * <li>Names tied to a particular specification. For instance:
+ * {@link Named#SRGB sRGB IEC61966-2.1} or
+ * {@link Named#ACES SMPTE ST 2065-1:2012 ACES}.</li>
+ * <li>Ad-hoc names, often generated procedurally or by the user
+ * during a calibration workflow. These names often contain the
+ * make and model of the display.</li>
+ * </ul>
+ *
+ * <p>Because the format of color space names is not defined, it is
+ * not recommended to programmatically identify a color space by its
+ * name alone. Names can be used as a first approximation.</p>
+ *
+ * <p>It is however perfectly acceptable to display color space names to
+ * users in a UI, or in debuggers and logs. When displaying a color space
+ * name to the user, it is recommended to add extra information to avoid
+ * ambiguities: color model, a representation of the color space's gamut,
+ * white point, etc.</p>
*
* @return A non-null String of length >= 1
*/
@@ -2079,8 +2103,11 @@
throw new IllegalArgumentException("Parameters cannot be NaN");
}
- if (!(d >= 0.0 && d <= 1.0 + Math.ulp(1.0))) {
- throw new IllegalArgumentException("Parameter d must be in the range [0..1]");
+ // Next representable float after 1.0
+ // We use doubles here but the representation inside our native code is often floats
+ if (!(d >= 0.0 && d <= 1.0f + Math.ulp(1.0f))) {
+ throw new IllegalArgumentException("Parameter d must be in the range [0..1], " +
+ "was " + d);
}
if (d == 0.0 && (a == 0.0 || g == 0.0)) {
@@ -2495,7 +2522,7 @@
x -> Math.pow(x < 0.0 ? 0.0 : x, gamma),
min, max, id);
mTransferParameters = gamma == 1.0 ?
- new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0), gamma) :
+ new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0f), gamma) :
new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma);
}
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 4b1b0c6..d9a77e7 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -52,12 +52,19 @@
mBuilderPtr = nInitBuilder(lang, variant);
}
- public void freeze() {
+ /**
+ * Finalize the FontFamily creation.
+ *
+ * @return boolean returns false if some error happens in native code, e.g. broken font file is
+ * passed, etc.
+ */
+ public boolean freeze() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen");
}
mNativePtr = nCreateFamily(mBuilderPtr);
mBuilderPtr = 0;
+ return mNativePtr != 0;
}
public void abortCreation() {
@@ -143,6 +150,25 @@
isItalic);
}
+ /**
+ * Allow creating unsupported FontFamily.
+ *
+ * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
+ * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
+ * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
+ * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
+ * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
+ * the top of the fallback chain but is never used. if we don't create this empty FontFamily
+ * and put it at top, bad things (performance regressions, unexpected glyph selection) will
+ * happen.
+ */
+ public void allowUnsupportedFont() {
+ if (mBuilderPtr == 0) {
+ throw new IllegalStateException("Unable to allow unsupported font.");
+ }
+ nAllowUnsupportedFont(mBuilderPtr);
+ }
+
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -154,6 +180,9 @@
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
+ private static native void nAllowUnsupportedFont(long builderPtr);
+
+ @CriticalNative
private static native void nAbort(long mBuilderPtr);
@CriticalNative
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 21533f8..2aca782 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -108,6 +108,7 @@
/**
* Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
*/
+ @GuardedBy("sLock")
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
static Typeface sDefaultTypeface;
@@ -129,6 +130,7 @@
public static final int BOLD_ITALIC = 3;
private int mStyle = 0;
+ private int mBaseWeight = 0;
// Value for weight and italic. Indicates the value is resolved by font metadata.
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -180,7 +182,9 @@
if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return null;
+ }
FontFamily[] families = {fontFamily};
typeface = createFromFamiliesWithDefault(families);
sDynamicTypefaceCache.put(key, typeface);
@@ -241,6 +245,10 @@
return null;
}
}
+ // Due to backward compatibility, even if the font is not supported by our font stack,
+ // we need to place the empty font at the first place. The typeface with empty font
+ // behaves different from default typeface especially in fallback font selection.
+ fontFamily.allowUnsupportedFont();
fontFamily.freeze();
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain);
@@ -292,6 +300,7 @@
* request. May not be null.
* @param callback A callback that will be triggered when results are obtained. May not be null.
*/
+ @Deprecated
public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
// Check the cache first
// TODO: would the developer want to avoid a cache hit and always ask for the freshest
@@ -392,7 +401,11 @@
IoUtils.closeQuietly(fd);
}
}
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ callback.onTypefaceRequestFailed(
+ FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+ return;
+ }
Typeface typeface = Typeface.createFromFamiliesWithDefault(new FontFamily[] { fontFamily });
synchronized (sDynamicTypefaceCache) {
String key = createProviderUid(request.getProviderAuthority(), request.getQuery());
@@ -404,6 +417,7 @@
/**
* Interface used to receive asynchronously fetched typefaces.
*/
+ @Deprecated
public interface FontRequestCallback {
/**
* Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
@@ -524,6 +538,7 @@
private FontsContract.FontInfo[] mFonts;
private Map<Uri, ByteBuffer> mFontBuffers;
+
private String mFallbackFamilyName;
private int mWeight = RESOLVE_BY_FONT_TABLE;
@@ -664,6 +679,33 @@
}
/**
+ * Sets a fallback family name.
+ *
+ * By specifying a fallback family name, a fallback Typeface will be returned if the
+ * {@link #build} method fails to create a Typeface from the provided font. The fallback
+ * family will be resolved with the provided weight and italic information specified by
+ * {@link #setWeight} and {@link #setItalic}.
+ *
+ * If {@link #setWeight} is not called, the fallback family keeps the default weight.
+ * Similary, if {@link #setItalic} is not called, the fallback family keeps the default
+ * italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
+ * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
+ * terms of fallback. The default weight and italic information are overridden by calling
+ * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed
+ * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text
+ * will render as sans serif bold.
+ *
+ * @param familyName A family name to be used for fallback if the provided font can not be
+ * used. By passing {@code null}, build() returns {@code null}.
+ * If {@link #setFallback} is not called on the builder, {@code null}
+ * is assumed.
+ */
+ public Builder setFallback(@Nullable String familyName) {
+ mFallbackFamilyName = familyName;
+ return this;
+ }
+
+ /**
* Creates a unique id for a given AssetManager and asset path.
*
* @param mgr AssetManager instance
@@ -695,6 +737,54 @@
return builder.toString();
}
+ private static final Object sLock = new Object();
+ // TODO: Unify with Typeface.sTypefaceCache.
+ @GuardedBy("sLock")
+ private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
+ new LongSparseArray<>(3);
+
+ private Typeface resolveFallbackTypeface() {
+ if (mFallbackFamilyName == null) {
+ return null;
+ }
+
+ Typeface base = sSystemFontMap.get(mFallbackFamilyName);
+ if (base == null) {
+ base = sDefaultTypeface;
+ }
+
+ if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
+ return base;
+ }
+
+ final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight;
+ final boolean italic =
+ (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
+ final int key = weight << 1 | (italic ? 1 : 0);
+
+ Typeface typeface;
+ synchronized(sLock) {
+ SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
+ if (innerCache != null) {
+ typeface = innerCache.get(key);
+ if (typeface != null) {
+ return typeface;
+ }
+ }
+
+ typeface = new Typeface(
+ nativeCreateFromTypefaceWithExactStyle(
+ base.native_instance, weight, italic));
+
+ if (innerCache == null) {
+ innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
+ sTypefaceCache.put(base.native_instance, innerCache);
+ }
+ innerCache.put(key, typeface);
+ }
+ return typeface;
+ }
+
/**
* Generates new Typeface from specified configuration.
*
@@ -710,26 +800,30 @@
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
fontFamily.abortCreation();
- return null;
+ return resolveFallbackTypeface();
}
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return resolveFallbackTypeface();
+ }
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
} catch (IOException e) {
- return null;
+ return resolveFallbackTypeface();
}
} else if (mAssetManager != null) { // set source by setSourceFromAsset()
final String key = createAssetUid(mAssetManager, mPath, mTtcIndex, mAxes);
- synchronized (sDynamicTypefaceCache) {
+ synchronized (sLock) {
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
fontFamily.abortCreation();
- return null;
+ return resolveFallbackTypeface();
}
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return resolveFallbackTypeface();
+ }
FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families);
sDynamicTypefaceCache.put(key, typeface);
@@ -739,9 +833,11 @@
final FontFamily fontFamily = new FontFamily();
if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
fontFamily.abortCreation();
- return null;
+ return resolveFallbackTypeface();
}
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return resolveFallbackTypeface();
+ }
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
} else if (mFonts != null) {
@@ -868,12 +964,34 @@
throw new NullPointerException(); // for backward compatibility
}
if (sFallbackFonts != null) {
- Typeface typeface = new Builder(mgr, path).build();
- if (typeface != null) {
- return typeface;
+ synchronized (sLock) {
+ Typeface typeface = new Builder(mgr, path).build();
+ if (typeface != null) return typeface;
+
+ final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
+ null /* axes */);
+ typeface = sDynamicTypefaceCache.get(key);
+ if (typeface != null) return typeface;
+
+ final FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
+ 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ null /* axes */)) {
+ // Due to backward compatibility, even if the font is not supported by our font
+ // stack, we need to place the empty font at the first place. The typeface with
+ // empty font behaves different from default typeface especially in fallback
+ // font selection.
+ fontFamily.allowUnsupportedFont();
+ fontFamily.freeze();
+ final FontFamily[] families = { fontFamily };
+ typeface = createFromFamiliesWithDefault(families);
+ sDynamicTypefaceCache.put(key, typeface);
+ return typeface;
+ } else {
+ fontFamily.abortCreation();
+ }
}
}
- // For the compatibility reasons, throw runtime exception if failed to create Typeface.
throw new RuntimeException("Font asset not found " + path);
}
@@ -908,17 +1026,20 @@
* @return The new typeface.
*/
public static Typeface createFromFile(@Nullable String path) {
- if (path == null) {
- // For the compatibility reasons, need to throw NPE if the argument is null.
- // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileNameNull
- throw new NullPointerException();
- }
if (sFallbackFonts != null) {
- Typeface typeface = new Builder(path).build();
- if (typeface != null) {
- // For the compatibility reasons, throw runtime exception if failed to create
- // Typeface.
- return typeface;
+ final FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
+ // Due to backward compatibility, even if the font is not supported by our font
+ // stack, we need to place the empty font at the first place. The typeface with
+ // empty font behaves different from default typeface especially in fallback font
+ // selection.
+ fontFamily.allowUnsupportedFont();
+ fontFamily.freeze();
+ FontFamily[] families = { fontFamily };
+ return createFromFamiliesWithDefault(families);
+ } else {
+ fontFamily.abortCreation();
}
}
throw new RuntimeException("Font not found " + path);
@@ -962,6 +1083,7 @@
native_instance = ni;
mStyle = nativeGetStyle(ni);
+ mBaseWeight = nativeGetBaseWeight(ni);
}
private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
@@ -986,7 +1108,11 @@
Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
}
}
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ // Treat as system error since reaching here means that a system pre-installed font
+ // can't be used by our font stack.
+ Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
+ }
return fontFamily;
}
@@ -1127,12 +1253,15 @@
}
private static native long nativeCreateFromTypeface(long native_instance, int style);
+ private static native long nativeCreateFromTypefaceWithExactStyle(
+ long native_instance, int weight, boolean italic);
// TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
private static native long nativeCreateFromTypefaceWithVariation(
long native_instance, List<FontVariationAxis> axes);
private static native long nativeCreateWeightAlias(long native_instance, int weight);
private static native void nativeUnref(long native_instance);
private static native int nativeGetStyle(long native_instance);
+ private static native int nativeGetBaseWeight(long native_instance);
private static native long nativeCreateFromArray(long[] familyArray);
private static native void nativeSetDefault(long native_instance);
private static native int[] nativeGetSupportedAxes(long native_instance);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c1cad7d..ed6e522 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
#include "hwui/MinikinUtils.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkCanvasStateUtils.h>
#include <SkColorSpaceXformCanvas.h>
#include <SkDrawable.h>
#include <SkDeque.h>
@@ -411,6 +412,30 @@
}
// ----------------------------------------------------------------------------
+// Canvas state operations: Capture
+// ----------------------------------------------------------------------------
+
+SkCanvasState* SkiaCanvas::captureCanvasState() const {
+ SkCanvas* canvas = mCanvas;
+ if (mCanvasOwned) {
+ // Important to use the underlying SkCanvas, not the wrapper.
+ canvas = mCanvasOwned.get();
+ }
+
+ // Workarounds for http://crbug.com/271096: SW draw only supports
+ // translate & scale transforms, and a simple rectangular clip.
+ // (This also avoids significant wasted time in calling
+ // SkCanvasStateUtils::CaptureCanvasState when the clip is complex).
+ if (!canvas->isClipRect() ||
+ (canvas->getTotalMatrix().getType() &
+ ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask))) {
+ return nullptr;
+ }
+
+ return SkCanvasStateUtils::CaptureCanvasState(canvas);
+}
+
+// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 13f979c..aeecafa 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -102,6 +102,8 @@
virtual SkDrawFilter* getDrawFilter() override;
virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+ virtual SkCanvasState* captureCanvasState() const override;
+
virtual void drawColor(int color, SkBlendMode mode) override;
virtual void drawPaint(const SkPaint& paint) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index ed32832..86af678 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -27,6 +27,8 @@
#include <SkCanvas.h>
#include <SkMatrix.h>
+class SkCanvasState;
+
namespace minikin {
class Layout;
}
@@ -200,6 +202,9 @@
virtual SkDrawFilter* getDrawFilter() = 0;
virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+ // WebView only
+ virtual SkCanvasState* captureCanvasState() const { return nullptr; }
+
// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 4b8575a..86709ee 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -71,6 +71,18 @@
return result;
}
+Typeface* Typeface::createFromTypefaceWithStyle(Typeface* base, int weight, bool italic) {
+ Typeface* resolvedFace = Typeface::resolveDefault(base);
+ Typeface* result = new Typeface();
+ if (result != nullptr) {
+ result->fFontCollection = resolvedFace->fFontCollection;
+ result->fBaseWeight = weight;
+ result->fStyle = minikin::FontStyle(weight / 100, italic);
+ result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ }
+ return result;
+}
+
Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src,
const std::vector<minikin::FontVariation>& variations) {
Typeface* resolvedFace = Typeface::resolveDefault(src);
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 19a4f6c5..27ee4a2 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -42,6 +42,8 @@
static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
+ static Typeface* createFromTypefaceWithStyle(Typeface* base, int weight, bool italic);
+
static Typeface* createFromTypefaceWithVariation(Typeface* src,
const std::vector<minikin::FontVariation>& variations);
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index d26eb59..68a0869 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -190,9 +190,32 @@
}
const Vector3 lightPos = SkiaPipeline::getLightCenter();
SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
- SkShadowUtils::DrawShadow(canvas, *casterPath, casterZValue, skiaLightPos,
+ if (shadowMatrix.hasPerspective() || revealClipPath || clippedToBounds) {
+ std::function<SkScalar(SkScalar, SkScalar)> casterHeightFunc;
+ if (shadowMatrix.hasPerspective()) {
+ // get the matrix with the full 3D transform
+ mat4 zMatrix;
+ caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true);
+ SkScalar A = zMatrix[2];
+ SkScalar B = zMatrix[6];
+ SkScalar C = zMatrix[mat4::kTranslateZ];
+ casterHeightFunc = [A, B, C](SkScalar x, SkScalar y) {
+ return A*x + B*y + C; // casterZValue already baked into C
+ };
+ } else {
+ casterHeightFunc = [casterZValue] (SkScalar, SkScalar) {
+ return casterZValue;
+ };
+ }
+
+ SkShadowUtils::DrawUncachedShadow(canvas, *casterPath, casterHeightFunc, skiaLightPos,
SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
+ } else {
+ SkShadowUtils::DrawShadow(canvas, *casterPath, casterZValue, skiaLightPos,
+ SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
+ casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
+ }
}
}; // namespace skiapipeline
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 44476af..0d1eba4 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
#include <RecordingCanvas.h>
#include <SkBlurDrawLooper.h>
+#include <SkCanvasStateUtils.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
@@ -128,3 +129,32 @@
canvas.asSkCanvas()->drawPicture(picture);
ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
}
+
+TEST(SkiaCanvas, captureCanvasState) {
+ // Create a software canvas.
+ SkImageInfo info = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ skBitmap.eraseColor(0);
+ SkiaCanvas canvas(skBitmap);
+
+ // Translate, then capture and verify the CanvasState.
+ canvas.translate(1.0f, 1.0f);
+ SkCanvasState* state = canvas.captureCanvasState();
+ ASSERT_NE(state, nullptr);
+ std::unique_ptr<SkCanvas> newCanvas = SkCanvasStateUtils::MakeFromCanvasState(state);
+ ASSERT_NE(newCanvas.get(), nullptr);
+ newCanvas->translate(-1.0f, -1.0f);
+ ASSERT_TRUE(newCanvas->getTotalMatrix().isIdentity());
+ SkCanvasStateUtils::ReleaseCanvasState(state);
+
+ // Create a picture canvas.
+ SkPictureRecorder recorder;
+ SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+ SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+ state = picCanvas.captureCanvasState();
+
+ // Verify that we cannot get the CanvasState.
+ ASSERT_EQ(state, nullptr);
+}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index aac9727..de85c16 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -670,6 +670,8 @@
* related to L5 will be filled.
*
* <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
*/
public float getCarrierFrequencyHz() {
return mCarrierFrequencyHz;
@@ -888,10 +890,10 @@
}
/**
- * Returns {@code true} if {@link #getAutomaticGainControlLevelInDb()} is available,
+ * Returns {@code true} if {@link #getAutomaticGainControlLevelDb()} is available,
* {@code false} otherwise.
*/
- public boolean hasAutomaticGainControlLevelInDb() {
+ public boolean hasAutomaticGainControlLevelDb() {
return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
}
@@ -908,9 +910,9 @@
* components) may also affect the typical output of of this value on any given hardware design
* in an open sky test - the important aspect of this output is that changes in this value are
* indicative of changes on input signal power in the frequency band for this measurement.
- * <p>The value is only available if {@link #hasAutomaticGainControlLevelInDb()} is {@code true}
+ * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
*/
- public double getAutomaticGainControlLevelInDb() {
+ public double getAutomaticGainControlLevelDb() {
return mAutomaticGainControlLevelInDb;
}
@@ -1064,7 +1066,7 @@
builder.append(String.format(
format,
"AgcLevelDb",
- hasAutomaticGainControlLevelInDb() ? mAutomaticGainControlLevelInDb : null));
+ hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
return builder.toString();
}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index e90a174..a44bc5b 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -224,7 +224,7 @@
*
* @param satIndex the index of the satellite in the list.
*/
- public boolean hasCarrierFrequency(int satIndex) {
+ public boolean hasCarrierFrequencyHz(int satIndex) {
return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
}
@@ -239,7 +239,11 @@
* will be reported for this same satellite, in one all the values related to L1 will be filled,
* and in the other all of the values related to L5 will be filled.
*
- * <p>The value is only available if {@link #hasCarrierFrequency(int satIndex)} is {@code true}.
+ * <p>The value is only available if {@link #hasCarrierFrequencyHz(int satIndex)} is {@code true}.
+ *
+ * @param satIndex the index of the satellite in the list.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
*/
public float getCarrierFrequencyHz(int satIndex) {
return mCarrierFrequencies[satIndex];
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 4c39c30..6bc5e91 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -242,8 +242,7 @@
}
public boolean getInEmergency() {
- boolean isInEmergencyCallback = Boolean.parseBoolean(
- SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+ boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
return mIsInEmergency || isInEmergencyCallback;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index b8d1d12..88b1c5f 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -474,6 +474,9 @@
/**
* This event type indicates that the licensed usage duration for keys in a session
* has expired. The keys are no longer valid.
+ * @deprecated Use {@link OnKeyStatusChangeListener#onKeyStatusChange}
+ * and check for {@link MediaDrm.KeyStatus#STATUS_EXPIRED} in the {@link MediaDrm.KeyStatus}
+ * instead.
*/
public static final int EVENT_KEY_EXPIRED = 3;
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index af4a5be..b076bb6 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -107,6 +107,7 @@
List<DvbDeviceInfo> getDvbDeviceList();
ParcelFileDescriptor openDvbDevice(in DvbDeviceInfo info, int device);
- // For preview programs
+ // For preview channels and programs
void sendTvInputNotifyIntent(in Intent intent, int userId);
+ void requestChannelBrowsable(in Uri channelUri, int userId);
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 6635b5f..15f27a1 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -21,9 +21,11 @@
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -75,8 +77,9 @@
private static final String PATH_PASSTHROUGH = "passthrough";
/**
- * Activity Action: sent by an application telling the system to set the given channel as
- * browsable. This is only relevant to channels with {@link Channels#TYPE_PREVIEW} type.
+ * Broadcast Action: sent when an application requests the system to make the given channel
+ * browsable. The operation is performed in the background without user interaction. This
+ * is only relevant to channels with {@link Channels#TYPE_PREVIEW} type.
*
* <p>The intent must contain the following bundle parameters:
* <ul>
@@ -84,9 +87,26 @@
* integer.</li>
* <li>{@link #EXTRA_PACKAGE_NAME}: the package name of the requesting application.</li>
* </ul>
+ * @hide
*/
- public static final String ACTION_MAKE_CHANNEL_BROWSABLE =
- "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
+ @SystemApi
+ public static final String ACTION_CHANNEL_BROWSABLE_REQUESTED =
+ "android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED";
+
+ /**
+ * Activity Action: sent by an application telling the system to make the given channel
+ * browsable with user interaction. The system may show UI to ask user to approve the channel.
+ * This is only relevant to channels with {@link Channels#TYPE_PREVIEW} type. Use
+ * {@link Activity#startActivityForResult} to get the result of the request.
+ *
+ * <p>The intent must contain the following bundle parameters:
+ * <ul>
+ * <li>{@link #EXTRA_CHANNEL_ID}: ID for the {@link Channels#TYPE_PREVIEW} channel as a long
+ * integer.</li>
+ * </ul>
+ */
+ public static final String ACTION_REQUEST_CHANNEL_BROWSABLE =
+ "android.media.tv.action.REQUEST_CHANNEL_BROWSABLE";
/**
* Broadcast Action: sent by the system to tell the target TV input that one of its preview
@@ -146,10 +166,16 @@
public static final String ACTION_INITIALIZE_PROGRAMS =
"android.media.tv.action.INITIALIZE_PROGRAMS";
- /** The key for a bundle parameter containing a channel ID as a long integer */
+ /**
+ * The key for a bundle parameter containing a channel ID as a long integer
+ */
public static final String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
- /** The key for a bundle parameter containing a package name as a string. */
+ /**
+ * The key for a bundle parameter containing a package name as a string.
+ * @hide
+ */
+ @SystemApi
public static final String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
/** The key for a bundle parameter containing a program ID as a long integer. */
@@ -161,6 +187,38 @@
"android.media.tv.extra.WATCH_NEXT_PROGRAM_ID";
/**
+ * The key for a bundle parameter containing the result code of a method call as an integer.
+ *
+ * @see #RESULT_OK
+ * @see #RESULT_ERROR_IO
+ * @see #RESULT_ERROR_INVALID_ARGUMENT
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_RESULT_CODE = "android.media.tv.extra.RESULT_CODE";
+
+ /**
+ * The result code for a successful execution without error.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_OK = 0;
+
+ /**
+ * The result code for a failure from I/O operation.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_ERROR_IO = 1;
+
+ /**
+ * The result code for a failure from invalid argument.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_ERROR_INVALID_ARGUMENT = 2;
+
+ /**
* 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:
@@ -209,6 +267,78 @@
public static final String METHOD_ADD_COLUMN = "add_column";
/**
+ * The method name to get all the blocked packages. When a package is blocked, all the data for
+ * preview programs/channels and watch next programs belonging to this package in the content
+ * provider will be cleared. Once a package is blocked, {@link SecurityException} will be thrown
+ * for all the requests to preview programs/channels and watch next programs via
+ * {@link android.content.ContentProvider} from it.
+ *
+ * <p>The returned {@link android.os.Bundle} will include all the blocked package names with the
+ * key {@link #EXTRA_BLOCKED_PACKAGES}.
+ *
+ * @see ContentResolver#call(Uri, String, String, Bundle)
+ * @see #EXTRA_BLOCKED_PACKAGES
+ * @see #METHOD_BLOCK_PACKAGE
+ * @see #METHOD_UNBLOCK_PACKAGE
+ * @hide
+ */
+ @SystemApi
+ public static final String METHOD_GET_BLOCKED_PACKAGES = "get_blocked_packages";
+
+ /**
+ * The method name to block the access from the given package. When a package is blocked, all
+ * the data for preview programs/channels and watch next programs belonging to this package in
+ * the content provider will be cleared. Once a package is blocked, {@link SecurityException}
+ * will be thrown for all the requests to preview programs/channels and watch next programs via
+ * {@link android.content.ContentProvider} from it.
+ *
+ * <p>The method caller must provide the following parameter:
+ * <ul>
+ * <li>{@code arg}: The package name to be added as blocked package {@link String}.</li>
+ * </ul>
+ *
+ * <p>The returned {@link android.os.Bundle} will include an integer code denoting whether the
+ * execution is successful or not with the key {@link #EXTRA_RESULT_CODE}. If {@code arg} is
+ * empty, the result code will be {@link #RESULT_ERROR_INVALID_ARGUMENT}. If success, the result
+ * code will be {@link #RESULT_OK}. Otherwise, the result code will be {@link #RESULT_ERROR_IO}.
+ *
+ * @see ContentResolver#call(Uri, String, String, Bundle)
+ * @see #EXTRA_RESULT_CODE
+ * @see #METHOD_GET_BLOCKED_PACKAGES
+ * @see #METHOD_UNBLOCK_PACKAGE
+ * @hide
+ */
+ @SystemApi
+ public static final String METHOD_BLOCK_PACKAGE = "block_package";
+
+ /**
+ * The method name to unblock the access from the given package. When a package is blocked, all
+ * the data for preview programs/channels and watch next programs belonging to this package in
+ * the content provider will be cleared. Once a package is blocked, {@link SecurityException}
+ * will be thrown for all the requests to preview programs/channels and watch next programs via
+ * {@link android.content.ContentProvider} from it.
+ *
+ * <p>The method caller must provide the following parameter:
+ * <ul>
+ * <li>{@code arg}: The package name to be removed from blocked list as a {@link String}.
+ * </li>
+ * </ul>
+ *
+ * <p>The returned {@link android.os.Bundle} will include an integer code denoting whether the
+ * execution is successful or not with the key {@link #EXTRA_RESULT_CODE}. If {@code arg} is
+ * empty, the result code will be {@link #RESULT_ERROR_INVALID_ARGUMENT}. If success, the result
+ * code will be {@link #RESULT_OK}. Otherwise, the result code will be {@link #RESULT_ERROR_IO}.
+ *
+ * @see ContentResolver#call(Uri, String, String, Bundle)
+ * @see #EXTRA_RESULT_CODE
+ * @see #METHOD_GET_BLOCKED_PACKAGES
+ * @see #METHOD_BLOCK_PACKAGE
+ * @hide
+ */
+ @SystemApi
+ public static final String METHOD_UNBLOCK_PACKAGE = "unblock_package";
+
+ /**
* The key for a returned {@link Bundle} value containing existing column names in the given
* table as an {@link ArrayList} of {@link String}.
*
@@ -253,6 +383,16 @@
public static final String EXTRA_DEFAULT_VALUE = "android.media.tv.extra.DEFAULT_VALUE";
/**
+ * The key for a returned {@link Bundle} value containing all the blocked package names as an
+ * {@link ArrayList} of {@link String}.
+ *
+ * @see #METHOD_GET_BLOCKED_PACKAGES
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_BLOCKED_PACKAGES = "android.media.tv.extra.BLOCKED_PACKAGES";
+
+ /**
* An optional query, update or delete URI parameter that allows the caller to specify TV input
* ID to filter channels.
* @hide
@@ -565,6 +705,24 @@
return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PROGRAM);
}
+ /**
+ * Requests to make a channel browsable.
+ *
+ * <p>Once called, the system will review the request and make the channel browsable based on
+ * its policy. The first request from a package is guaranteed to be approved. This is only
+ * relevant to channels with {@link Channels#TYPE_PREVIEW} type.
+ *
+ * @param context The context for accessing content provider.
+ * @param channelId The channel ID to be browsable.
+ * @see Channels#COLUMN_BROWSABLE
+ */
+ public static void requestChannelBrowsable(Context context, long channelId) {
+ TvInputManager manager = (TvInputManager) context.getSystemService(
+ Context.TV_INPUT_SERVICE);
+ if (manager != null) {
+ manager.requestChannelBrowsable(buildChannelUri(channelId));
+ }
+ }
private TvContract() {}
@@ -2184,19 +2342,6 @@
*/
public static final String COLUMN_TRANSIENT = "transient";
- /**
- * The flag indicating whether this TV channel is approved to be shown by the system.
- *
- * <p>A value of 1 indicates that the channel is approved to be shown by the system, and a
- * value of 0 indicates that the channel is blocked by system. If not specified, this value
- * is set to 0 (not approved) by default.
- *
- * <p>Type: INTEGER (boolean)
- * @hide
- */
- @SystemApi
- public static final String COLUMN_SYSTEM_APPROVED = "system_approved";
-
private Channels() {}
/**
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 276a0dc..68ee02c 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1681,6 +1681,23 @@
}
/**
+ * Requests to make a channel browsable.
+ *
+ * <p>Once called, the system will review the request and make the channel browsable based on
+ * its policy. The first request from a package is guaranteed to be approved.
+ *
+ * @param channelUri The URI for the channel to be browsable.
+ * @hide
+ */
+ public void requestChannelBrowsable(Uri channelUri) {
+ try {
+ mService.requestChannelBrowsable(channelUri, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 34f2822..82ffa68 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -431,6 +431,8 @@
<string name="mobile_data_always_on">Mobile data always active</string>
<!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
+ <!-- Setting Checkbox title for enabling Bluetooth inband ringing -->
+ <string name="bluetooth_enable_inband_ringing">Enable in-band ringing</string>
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
@@ -512,6 +514,9 @@
<string name="verify_apps_over_usb_summary">Check apps installed via ADB/ADT for harmful behavior.</string>
<!-- Summary of checkbox for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
+ <!-- Summary of checkbox for enabling Bluetooth inband ringing -->
+ <string name="bluetooth_enable_inband_ringing_summary">Allow ringtones on the phone to be played on Bluetooth headsets</string>
+
<!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
<string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
index 5b2541c..2d8defa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
@@ -25,22 +25,21 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.text.format.Formatter;
-import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.graph.UsageView;
public class BatteryInfo {
- public String mChargeLabelString;
- public int mBatteryLevel;
- public boolean mDischarging = true;
+ public String chargeLabelString;
+ public int batteryLevel;
+ public boolean discharging = true;
public long remainingTimeUs = 0;
public String batteryPercentString;
public String remainingLabel;
public String statusLabel;
- private BatteryStats mStats;
private boolean mCharging;
+ private BatteryStats mStats;
private long timePeriod;
public interface Callback {
@@ -132,10 +131,11 @@
BatteryStats stats, long elapsedRealtimeUs, boolean shortString) {
BatteryInfo info = new BatteryInfo();
info.mStats = stats;
- info.mBatteryLevel = Utils.getBatteryLevel(batteryBroadcast);
- info.batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
+ info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
+ info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
final Resources resources = context.getResources();
+
info.statusLabel = Utils.getBatteryStatus(resources, batteryBroadcast);
if (!info.mCharging) {
final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
@@ -147,20 +147,20 @@
shortString ? R.string.power_remaining_duration_only_short
: R.string.power_remaining_duration_only,
timeString);
- info.mChargeLabelString = resources.getString(
+ info.chargeLabelString = resources.getString(
shortString ? R.string.power_discharging_duration_short
: R.string.power_discharging_duration,
info.batteryPercentString, timeString);
} else {
info.remainingLabel = null;
- info.mChargeLabelString = info.batteryPercentString;
+ info.chargeLabelString = info.batteryPercentString;
}
} else {
final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
+ info.discharging = false;
if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
- info.mDischarging = false;
info.remainingTimeUs = chargeTime;
String timeString = Formatter.formatShortElapsedTime(context,
chargeTime / 1000);
@@ -168,13 +168,13 @@
: R.string.power_charging_duration;
info.remainingLabel = resources.getString(
R.string.power_remaining_charging_duration_only, timeString);
- info.mChargeLabelString = resources.getString(resId, info.batteryPercentString,
- timeString);
+ info.chargeLabelString = resources.getString(
+ resId, info.batteryPercentString, timeString);
} else {
final String chargeStatusLabel = resources.getString(
R.string.battery_info_status_charging_lower);
info.remainingLabel = null;
- info.mChargeLabelString = resources.getString(
+ info.chargeLabelString = resources.getString(
R.string.power_charging, info.batteryPercentString, chargeStatusLabel);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 8833fb8c..8a86c13 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -984,18 +984,27 @@
mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
mBackgroundHandler.post(() -> {
- final StorageStats stats = mStats.queryStatsForPackage(
- mCurComputingSizeUuid, mCurComputingSizePkg,
- UserHandle.of(mCurComputingSizeUserId));
- final PackageStats legacyStats = new PackageStats(
- mCurComputingSizePkg, mCurComputingSizeUserId);
- legacyStats.codeSize = stats.getCodeBytes();
- legacyStats.dataSize = stats.getDataBytes();
- legacyStats.cacheSize = stats.getCacheBytes();
try {
- mStatsObserver.onGetStatsCompleted(legacyStats, true);
- } catch (RemoteException ignored) {
+ final StorageStats stats = mStats.queryStatsForPackage(
+ mCurComputingSizeUuid, mCurComputingSizePkg,
+ UserHandle.of(mCurComputingSizeUserId));
+ final PackageStats legacyStats = new PackageStats(
+ mCurComputingSizePkg, mCurComputingSizeUserId);
+ legacyStats.codeSize = stats.getCodeBytes();
+ legacyStats.dataSize = stats.getDataBytes();
+ legacyStats.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacyStats, true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"An exception occurred while fetching app size", e);
+ try {
+ mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
}
+
});
}
if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
@@ -1493,7 +1502,8 @@
@Override
public boolean filterApp(AppEntry entry) {
- return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ return !AppUtils.isInstant(entry.info)
+ && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
}
};
@@ -1601,19 +1611,36 @@
}
};
- public static final AppFilter FILTER_OTHER_APPS = new AppFilter() {
+ public static final AppFilter FILTER_MOVIES = new AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(AppEntry entry) {
- boolean isCategorized;
+ boolean isMovieApp;
synchronized(entry) {
- isCategorized = entry.info.category == ApplicationInfo.CATEGORY_AUDIO ||
- entry.info.category == ApplicationInfo.CATEGORY_GAME;
+ isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
}
- return !isCategorized;
+ return isMovieApp;
}
};
+
+ public static final AppFilter FILTER_OTHER_APPS =
+ new AppFilter() {
+ @Override
+ public void init() {}
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isCategorized;
+ synchronized (entry) {
+ isCategorized =
+ FILTER_AUDIO.filterApp(entry)
+ || FILTER_GAMES.filterApp(entry)
+ || FILTER_MOVIES.filterApp(entry);
+ }
+ return !isCategorized;
+ }
+ };
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 61ca13d..9d6505b 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -40,9 +40,15 @@
public static final String TAG = BatteryMeterDrawableBase.class.getSimpleName();
protected final Context mContext;
+ protected final Paint mFramePaint;
+ protected final Paint mBatteryPaint;
+ protected final Paint mWarningTextPaint;
+ protected final Paint mTextPaint;
+ protected final Paint mBoltPaint;
+ protected final Paint mPlusPaint;
private int mLevel = -1;
- private boolean mPluggedIn;
+ private boolean mCharging;
private boolean mPowerSaveEnabled;
private boolean mShowPercent;
@@ -59,8 +65,6 @@
private float mButtonHeightFraction;
private float mSubpixelSmoothingLeft;
private float mSubpixelSmoothingRight;
- private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint,
- mPlusPaint;
private float mTextHeight, mWarningTextHeight;
private int mIconTint = Color.WHITE;
private float mOldDarkIntensity = -1f;
@@ -180,16 +184,24 @@
postInvalidate();
}
- public void setPluggedIn(boolean val) {
- mPluggedIn = val;
+ public void setCharging(boolean val) {
+ mCharging = val;
postInvalidate();
}
+ public boolean getCharging() {
+ return mCharging;
+ }
+
public void setBatteryLevel(int val) {
mLevel = val;
postInvalidate();
}
+ public int getBatteryLevel() {
+ return mLevel;
+ }
+
public void setPowerSave(boolean val) {
mPowerSaveEnabled = val;
postInvalidate();
@@ -314,7 +326,7 @@
mFrame.bottom -= mSubpixelSmoothingRight;
// set the battery charging color
- mBatteryPaint.setColor(mPluggedIn ? mChargeColor : getColorForLevel(level));
+ mBatteryPaint.setColor(mCharging ? mChargeColor : getColorForLevel(level));
if (level >= FULL) {
drawFrac = 1f;
@@ -337,7 +349,7 @@
mShapePath.lineTo(mButtonFrame.left, mFrame.top);
mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
- if (mPluggedIn) {
+ if (mCharging) {
// define the bolt shape
final float bl = mFrame.left + mFrame.width() / 4f;
final float bt = mFrame.top + mFrame.height() / 6f;
@@ -408,7 +420,7 @@
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
- if (!mPluggedIn && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
+ if (!mCharging && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
@@ -436,7 +448,7 @@
mShapePath.op(mClipPath, Path.Op.INTERSECT);
c.drawPath(mShapePath, mBatteryPaint);
- if (!mPluggedIn && !mPowerSaveEnabled) {
+ if (!mCharging && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f;
@@ -467,4 +479,8 @@
public int getOpacity() {
return 0;
}
+
+ public int getCriticalLevel() {
+ return mCriticalLevel;
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 6a029f0..fed18fa 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -174,6 +174,13 @@
}
@Test
+ public void testOtherAppsRejectsLegacyGame() {
+ mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
+
+ assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
+ }
+
+ @Test
public void testInstantFilterAcceptsInstantApp() {
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_INSTANT.filterApp(mEntry)).isTrue();
@@ -194,10 +201,40 @@
}
@Test
+ public void testFilterWithDomainUrls() {
+ mEntry.info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+ // should included updated system apps
+ when(mEntry.info.isInstantApp()).thenReturn(false);
+ assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
+ .isTrue();
+ mEntry.info.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+ assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
+ .isFalse();
+ mEntry.info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+ when(mEntry.info.isInstantApp()).thenReturn(true);
+ assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
+ .isFalse();
+ }
+
+ @Test
public void testDisabledFilterRejectsInstantApp() {
mEntry.info.enabled = false;
assertThat(ApplicationsState.FILTER_DISABLED.filterApp(mEntry)).isTrue();
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_DISABLED.filterApp(mEntry)).isFalse();
}
+
+ @Test
+ public void testVideoFilterAcceptsCategorizedVideo() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_VIDEO;
+
+ assertThat(ApplicationsState.FILTER_MOVIES.filterApp(mEntry)).isTrue();
+ }
+
+ @Test
+ public void testVideosFilterRejectsNotVideo() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
+
+ assertThat(ApplicationsState.FILTER_MOVIES.filterApp(mEntry)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 4de2c12..83667ea 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -55,12 +55,12 @@
final int levels[] = { 0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100 };
final boolean bools[] = { false, true };
for (int l : levels) {
- for (boolean plugged : bools) {
+ for (boolean charging : bools) {
for (boolean saver : bools) {
for (boolean percent : bools) {
mBatteryDrawable.setBatteryLevel(l);
mBatteryDrawable.setPowerSave(saver);
- mBatteryDrawable.setPluggedIn(plugged);
+ mBatteryDrawable.setCharging(charging);
mBatteryDrawable.setShowPercent(percent);
mBatteryDrawable.draw(canvas);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java
index 962c4e7..69efc9e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java
@@ -32,6 +32,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
@@ -44,10 +45,12 @@
private static final String STATUS_FULL = "Full";
private static final String STATUS_CHARGING_NO_TIME = "Charging";
private static final String STATUS_CHARGING_TIME = "Charging - 2h left";
+ private static final int PLUGGED_IN = 1;
private static final long REMAINING_TIME_NULL = -1;
private static final long REMAINING_TIME = 2;
private Intent mDisChargingBatteryBroadcast;
private Intent mChargingBatteryBroadcast;
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStats mBatteryStats;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -95,7 +98,7 @@
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false);
- assertThat(info.mChargeLabelString).isEqualTo(STATUS_CHARGING_TIME);
+ assertThat(info.chargeLabelString).isEqualTo(STATUS_CHARGING_TIME);
}
@Test
@@ -104,6 +107,14 @@
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false);
- assertThat(info.mChargeLabelString).isEqualTo(STATUS_CHARGING_NO_TIME);
+ assertThat(info.chargeLabelString).isEqualTo(STATUS_CHARGING_NO_TIME);
+ }
+
+ @Test
+ public void testGetBatteryInfo_pluggedIn_dischargingFalse() {
+ BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
+ mBatteryStats, SystemClock.elapsedRealtime() * 1000, true);
+
+ assertThat(info.discharging).isEqualTo(false);
}
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 3ea22e9..39cba74 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -32,10 +32,8 @@
android:id="@+id/carrier_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ style="@style/Keyguard.TextView"
android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
android:visibility="gone"
androidprv:allCaps="@bool/kg_use_all_caps" />
@@ -46,10 +44,7 @@
android:layout_weight="1"
android:layout_marginTop="@dimen/eca_overlap"
android:text="@*android:string/lockscreen_emergency_call"
- style="?android:attr/buttonBarButtonStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
+ style="@style/Keyguard.TextView.EmergencyButton"
android:textAllCaps="@bool/kg_use_all_caps" />
</com.android.keyguard.EmergencyCarrierArea>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml
index 46aa39d..e1bf6cb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml
@@ -23,11 +23,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
+ style="@style/Keyguard.TextView"
android:id="@+id/keyguard_message_area"
android:singleLine="true"
android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearance"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
android:focusable="true" />
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index dede537..53a559f 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -19,6 +19,15 @@
<resources>
<!-- Keyguard PIN pad styles -->
+ <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
+ <item name="android:textColor">@*android:color/primary_device_default_light</item>
+ <item name="android:textSize">@dimen/kg_status_line_font_size</item>
+ </style>
+ <style name="Keyguard.TextView.EmergencyButton" parent="@android:style/DeviceDefault.ButtonBar">
+ <item name="android:textColor">@*android:color/primary_device_default_light</item>
+ <item name="android:textSize">@dimen/kg_status_line_font_size</item>
+ <item name="android:background">@null</item>
+ </style>
<style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
<item name="android:singleLine">true</item>
<item name="android:gravity">center_horizontal|center_vertical</item>
diff --git a/packages/SystemUI/res/drawable-hdpi/pip_dismiss_scrim.9.png b/packages/SystemUI/res/drawable-hdpi/pip_dismiss_scrim.9.png
index 6737c80..32f1ed7 100644
--- a/packages/SystemUI/res/drawable-hdpi/pip_dismiss_scrim.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/pip_dismiss_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/pip_dismiss_scrim.9.png b/packages/SystemUI/res/drawable-mdpi/pip_dismiss_scrim.9.png
index d9e33eb..33826a6 100644
--- a/packages/SystemUI/res/drawable-mdpi/pip_dismiss_scrim.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/pip_dismiss_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/pip_dismiss_scrim.9.png b/packages/SystemUI/res/drawable-xhdpi/pip_dismiss_scrim.9.png
index 64daf20..c1157f4 100644
--- a/packages/SystemUI/res/drawable-xhdpi/pip_dismiss_scrim.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/pip_dismiss_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/pip_dismiss_scrim.9.png b/packages/SystemUI/res/drawable-xxhdpi/pip_dismiss_scrim.9.png
index 49e0499..8d58a7e 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/pip_dismiss_scrim.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/pip_dismiss_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/pip_dismiss_scrim.9.png b/packages/SystemUI/res/drawable-xxxhdpi/pip_dismiss_scrim.9.png
index 0e6ca16..6c04d1a 100644
--- a/packages/SystemUI/res/drawable-xxxhdpi/pip_dismiss_scrim.9.png
+++ b/packages/SystemUI/res/drawable-xxxhdpi/pip_dismiss_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_data_off.xml b/packages/SystemUI/res/drawable/ic_data_off.xml
new file mode 100644
index 0000000..b97ddae
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_off.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M21.6,21.6L10.8,10.9L2.1,2.1L0.8,3.4l3.3,3.3C3.1,8.2 2.5,10.0 2.5,12.0c0.0,5.2 4.3,9.5 9.5,9.5c2.0,0.0 3.8,-0.6 5.3,-1.6l3.0,3.0L21.6,21.6zM9.6,12.2l0.7,0.7L9.6,12.9L9.6,12.2zM13.9,18.6c-0.2,0.2 -0.5,0.2 -0.6,0.0l-2.4,-3.7l1.5,0.0l2.4,2.4L13.9,18.6z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M12.0,2.5c-2.0,0.0 -3.8,0.6 -5.3,1.6l2.5,2.5L10.0,5.4c0.2,-0.2 0.5,-0.2 0.6,0.0L13.0,9.1l-1.4,0.0l2.0,2.0l0.6,0.0l0.0,0.6l5.6,5.6c1.0,-1.5 1.6,-3.3 1.6,-5.3C21.5,6.8 17.2,2.5 12.0,2.5z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_on.xml b/packages/SystemUI/res/drawable/ic_data_on.xml
new file mode 100644
index 0000000..a65dc79
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_on.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12.0,12.0m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0
+ M10.6,5.4c-0.2,-0.2 -0.5,-0.2 -0.6,0.0L7.6,9.1l2.0,0.0l0.0,3.8L11.0,12.900001L11.0,9.1l2.0,0.0L10.6,5.4z
+ M13.3,18.6c0.2,0.2 0.5,0.2 0.6,0.0l2.4,-3.7l-2.0,0.0l0.0,-3.8l-1.4,0.0l0.0,3.8l-2.0,0.0L13.3,18.6z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_unavailable.xml b/packages/SystemUI/res/drawable/ic_data_unavailable.xml
new file mode 100644
index 0000000..27a7697
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_unavailable.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ M12.0,12.0m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12.0,2.5c-5.246705,0.0 -9.5,4.253295 -9.5,9.5 0.0,5.246705 4.253295,9.5 9.5,9.5 2.771732,0.0 5.263364,-1.200342 7.0,-3.09375l0.0,-0.21875 0.0,-7.5 0.0,-1.0 1.0,0.0 1.1875,0.0C20.148497,5.5674677 16.442669,2.5 12.0,2.5zm9.1875,7.1875c-14.125,9.541667 -7.0625,4.770833 0.0,0.0z
+ M10.6,5.4C10.4,5.2 10.1,5.2 10.0,5.4L7.6,9.1l2.0,0.0l0.0,3.8L11.0,12.900001L11.0,9.1l2.0,0.0L10.6,5.4z
+ M13.3,18.6c0.2,0.2 0.5,0.2 0.6,0.0l2.4,-3.7l-2.0,0.0l0.0,-3.8l-1.4,0.0l0.0,3.8l-2.0,0.0l2.4,3.7z
+ M21.7,24.0c-0.5,0.0 -0.8,-0.1 -1.1,-0.4c-0.3,-0.3 -0.4,-0.6 -0.4,-1.0c0.0,-0.4 0.1,-0.8 0.4,-1.0c0.3,-0.3 0.7,-0.4 1.1,-0.4s0.8,0.1 1.1,0.4c0.3,0.3 0.4,0.6 0.4,1.0c0.0,0.4 -0.1,0.7 -0.4,1.0
+ C22.6,23.8 22.2,24 21.7,24z
+ M20.4,19.7l0.0,-8.5L23.0,11.2l0.0,8.5L20.4,19.7z
+ "
+ android:fillType="evenOdd"
+ android:fillColor="#231F20"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
index 34228d9..058f59f 100644
--- a/packages/SystemUI/res/layout/pip_dismiss_view.xml
+++ b/packages/SystemUI/res/layout/pip_dismiss_view.xml
@@ -27,6 +27,10 @@
android:layout_gravity="bottom|center_horizontal"
android:text="@string/pip_phone_dismiss_hint"
android:textColor="#FFFFFFFF"
- android:textSize="14sp" />
+ android:textSize="14sp"
+ android:shadowColor="@android:color/black"
+ android:shadowDx="-2"
+ android:shadowDy="2"
+ android:shadowRadius="0.01" />
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6837340..6d8a077 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -738,7 +738,7 @@
<dimen name="pip_dismiss_gradient_height">196dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
- <dimen name="pip_dismiss_text_bottom_margin">36dp</dimen>
+ <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
<!-- The shortest-edge size of the expanded PiP. -->
<dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d57e88c..911ef24 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -149,7 +149,7 @@
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mDrawable.setBatteryLevel(level);
- mDrawable.setPluggedIn(pluggedIn);
+ mDrawable.setCharging(pluggedIn);
mLevel = level;
updatePercentText();
setContentDescription(
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 429e859..f4d4a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -260,6 +261,9 @@
mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+ mProviders.put(AccessibilityManagerWrapper.class,
+ () -> new AccessibilityManagerWrapper(mContext));
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 0b0ea720..7fed3e8 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -31,6 +31,7 @@
import android.view.View;
import android.view.ViewConfiguration;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -130,6 +131,11 @@
}
};
+ @VisibleForTesting
+ ObjectAnimator getScaleAnimation() {
+ return mScaleAnimation;
+ }
+
private class ViewScaler {
ExpandableView mView;
@@ -176,7 +182,8 @@
mFlingAnimationUtils = new FlingAnimationUtils(context, EXPAND_DURATION);
}
- private void updateExpansion() {
+ @VisibleForTesting
+ void updateExpansion() {
if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()");
// are we scaling or dragging?
float span = mSGD.getCurrentSpan() - mInitialTouchSpan;
@@ -500,7 +507,8 @@
/**
* @return True if the view is expandable, false otherwise.
*/
- private boolean startExpanding(ExpandableView v, int expandType) {
+ @VisibleForTesting
+ boolean startExpanding(ExpandableView v, int expandType) {
if (!(v instanceof ExpandableNotificationRow)) {
return false;
}
@@ -535,7 +543,8 @@
* state
* @param velocity the velocity this was expanded/ collapsed with
*/
- private void finishExpanding(boolean forceAbort, float velocity) {
+ @VisibleForTesting
+ void finishExpanding(boolean forceAbort, float velocity) {
if (!mExpanding) return;
if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
@@ -571,7 +580,9 @@
public void onAnimationEnd(Animator animation) {
if (!mCancelled) {
mCallback.setUserExpandedChild(scaledView, expand);
- mScaler.setView(null);
+ if (!mExpanding) {
+ mScaler.setView(null);
+ }
} else {
mCallback.setExpansionCancelled(scaledView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 114a594..c565373 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -86,6 +86,7 @@
ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
mActivityManager);
+ mMenuController.hideMenu();
mNotificationController.onActivityUnpinned(topPipActivity);
SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 8209ee2..ecc275d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -181,7 +181,7 @@
return;
}
mDrawable.setBatteryLevel(100);
- mDrawable.setPluggedIn(false);
+ mDrawable.setCharging(false);
mDrawable.setPowerSave(true);
mDrawable.setShowPercent(false);
((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 9b3ee30..1151c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -35,8 +35,8 @@
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.qs.CellTileView;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -83,7 +83,7 @@
@Override
public QSIconView createTileView(Context context) {
- return new CellTileView(context);
+ return new SignalTileView(context);
}
@Override
@@ -118,15 +118,6 @@
}
final Resources r = mContext.getResources();
- final int iconId = cb.noSim ? R.drawable.ic_qs_no_sim
- : !cb.enabled || cb.airplaneModeEnabled ? R.drawable.ic_qs_signal_disabled
- : cb.mobileSignalIconId > 0 ? cb.mobileSignalIconId
- : R.drawable.ic_qs_signal_no_signal;
- if (cb.dataTypeIconId != 0) {
- state.icon = ResourceIcon.get(cb.dataTypeIconId);
- } else {
- state.icon = ResourceIcon.get(iconId);
- }
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
@@ -136,8 +127,16 @@
state.expandedAccessibilityClassName = Switch.class.getName();
state.value = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
- state.state = cb.airplaneModeEnabled ? Tile.STATE_UNAVAILABLE
+ state.icon = ResourceIcon.get(R.drawable.ic_data_unavailable);
+ state.state = cb.airplaneModeEnabled || !cb.enabled ? Tile.STATE_UNAVAILABLE
: state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ if (state.state == Tile.STATE_ACTIVE) {
+ state.icon = ResourceIcon.get(R.drawable.ic_data_on);
+ } else if (state.state == Tile.STATE_INACTIVE) {
+ state.icon = ResourceIcon.get(R.drawable.ic_data_off);
+ } else {
+ state.icon = ResourceIcon.get(R.drawable.ic_data_unavailable);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index b11b15a..6522e10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -22,7 +22,6 @@
import android.service.quicksettings.Tile;
import android.widget.Switch;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -32,7 +31,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
/** Quick settings tile: Location **/
public class LocationTile extends QSTileImpl<BooleanState> {
@@ -132,7 +131,7 @@
}
}
- private final class Callback implements LocationSettingsChangeCallback,
+ private final class Callback implements LocationChangeCallback,
KeyguardMonitor.Callback {
@Override
public void onLocationSettingsChanged(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index a444934..c499619 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -81,7 +81,7 @@
}
String location = args.getString("location");
if (location != null) {
- int iconId = location.equals("show") ? LocationControllerImpl.LOCATION_STATUS_ICON_ID
+ int iconId = location.equals("show") ? PhoneStatusBarPolicy.LOCATION_STATUS_ICON_ID
: 0;
updateSlot("location", null, iconId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 7562399..fa404b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -64,6 +64,7 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -78,6 +79,7 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -138,8 +140,8 @@
mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
mWindowManager = getContext().getSystemService(WindowManager.class);
mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
- mAccessibilityManager.addAccessibilityServicesStateChangeListener(
- this::updateAccessibilityServicesState);
+ Dependency.get(AccessibilityManagerWrapper.class).addCallback(
+ mAccessibilityListener);
mContentResolver = getContext().getContentResolver();
mMagnificationObserver = new MagnificationContentObserver(
getContext().getMainThreadHandler());
@@ -164,8 +166,8 @@
public void onDestroy() {
super.onDestroy();
mCommandQueue.removeCallbacks(this);
- mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
- this::updateAccessibilityServicesState);
+ Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
+ mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -625,6 +627,9 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
+ private final AccessibilityServicesStateChangeListener mAccessibilityListener =
+ this::updateAccessibilityServicesState;
+
private class MagnificationContentObserver extends ContentObserver {
public MagnificationContentObserver(Handler handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 53ec8c5..5910557 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -73,6 +73,8 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -86,11 +88,13 @@
* strictly doesn't need to.
*/
public class PhoneStatusBarPolicy implements Callback, Callbacks,
- RotationLockControllerCallback, Listener,
+ RotationLockControllerCallback, Listener, LocationChangeCallback,
ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location;
+
private final String mSlotCast;
private final String mSlotHotspot;
private final String mSlotBluetooth;
@@ -102,6 +106,7 @@
private final String mSlotRotate;
private final String mSlotHeadset;
private final String mSlotDataSaver;
+ private final String mSlotLocation;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -117,6 +122,7 @@
private final ZenModeController mZenController;
private final DeviceProvisionedController mProvisionedController;
private final KeyguardMonitor mKeyguardMonitor;
+ private final LocationController mLocationController;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
// Assume it's all good unless we hear otherwise. We don't always seem
@@ -147,6 +153,7 @@
mZenController = Dependency.get(ZenModeController.class);
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mLocationController = Dependency.get(LocationController.class);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -160,7 +167,7 @@
mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
-
+ mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -229,6 +236,7 @@
mNextAlarm.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
+ mLocationController.addCallback(this);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener);
@@ -252,6 +260,7 @@
mNextAlarm.removeCallback(mNextAlarmCallback);
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
+ mLocationController.removeCallback(this);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
mContext.unregisterReceiver(mIntentReceiver);
@@ -265,6 +274,21 @@
updateVolumeZen();
}
+ @Override
+ public void onLocationActiveChanged(boolean active) {
+ updateLocation();
+ }
+
+ // Updates the status view based on the current state of location requests.
+ private void updateLocation() {
+ if (mLocationController.isLocationActive()) {
+ mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
+ mContext.getString(R.string.accessibility_location_active));
+ } else {
+ mIconController.removeIcon(mSlotLocation);
+ }
+ }
+
private void updateAlarm() {
final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
new file mode 100644
index 0000000..dfa5cbd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+
+/**
+ * For mocking because AccessibilyManager is final for some reason...
+ */
+public class AccessibilityManagerWrapper implements
+ CallbackController<AccessibilityServicesStateChangeListener> {
+
+ private final AccessibilityManager mAccessibilityManager;
+
+ public AccessibilityManagerWrapper(Context context) {
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+ }
+
+ @Override
+ public void addCallback(AccessibilityServicesStateChangeListener listener) {
+ mAccessibilityManager.addAccessibilityServicesStateChangeListener(listener);
+ }
+
+ @Override
+ public void removeCallback(AccessibilityServicesStateChangeListener listener) {
+ mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 9a5f1b8..8e8a285 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -16,22 +16,29 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
-public interface LocationController extends CallbackController<LocationSettingsChangeCallback> {
+public interface LocationController extends CallbackController<LocationChangeCallback> {
+ boolean isLocationActive();
boolean isLocationEnabled();
boolean setLocationEnabled(boolean enabled);
/**
* A callback for change in location settings (the user has enabled/disabled location).
*/
- public interface LocationSettingsChangeCallback {
+ public interface LocationChangeCallback {
+ /**
+ * Called whenever location's state changes.
+ * @param active
+ */
+ default void onLocationActiveChanged(boolean active) {}
+
/**
* Called whenever location settings change.
*
* @param locationEnabled A value of true indicates that at least one type of location
* is enabled in settings.
*/
- void onLocationSettingsChanged(boolean locationEnabled);
+ default void onLocationSettingsChanged(boolean locationEnabled) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index cc61605..3f5f5a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -41,9 +41,6 @@
* A controller to manage changes of location related states and update the views accordingly.
*/
public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
- // The name of the placeholder corresponding to the location request status icon.
- // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml.
- public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location;
private static final int[] mHighPowerRequestAppOpArray
= new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
@@ -55,14 +52,12 @@
private boolean mAreActiveLocationRequests;
- private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks =
- new ArrayList<LocationSettingsChangeCallback>();
+ private ArrayList<LocationChangeCallback> mSettingsChangeCallbacks =
+ new ArrayList<LocationChangeCallback>();
private final H mHandler = new H();
- public final String mSlotLocation;
public LocationControllerImpl(Context context, Looper bgLooper) {
mContext = context;
- mSlotLocation = mContext.getString(com.android.internal.R.string.status_bar_location);
// Register to listen for changes in location settings.
IntentFilter filter = new IntentFilter();
@@ -76,18 +71,17 @@
// Examine the current location state and initialize the status view.
updateActiveLocationRequests();
- refreshViews();
}
/**
* Add a callback to listen for changes in location settings.
*/
- public void addCallback(LocationSettingsChangeCallback cb) {
+ public void addCallback(LocationChangeCallback cb) {
mSettingsChangeCallbacks.add(cb);
mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
}
- public void removeCallback(LocationSettingsChangeCallback cb) {
+ public void removeCallback(LocationChangeCallback cb) {
mSettingsChangeCallbacks.remove(cb);
}
@@ -130,6 +124,11 @@
return mode != Settings.Secure.LOCATION_MODE_OFF;
}
+ @Override
+ public boolean isLocationActive() {
+ return mAreActiveLocationRequests;
+ }
+
/**
* Returns true if the current user is restricted from using location.
*/
@@ -170,22 +169,12 @@
return false;
}
- // Updates the status view based on the current state of location requests.
- private void refreshViews() {
- if (mAreActiveLocationRequests) {
- mStatusBarManager.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
- 0, mContext.getString(R.string.accessibility_location_active));
- } else {
- mStatusBarManager.removeIcon(mSlotLocation);
- }
- }
-
// Reads the active location requests and updates the status view if necessary.
private void updateActiveLocationRequests() {
boolean hadActiveLocationRequests = mAreActiveLocationRequests;
mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
if (mAreActiveLocationRequests != hadActiveLocationRequests) {
- refreshViews();
+ mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
}
}
@@ -201,6 +190,7 @@
private final class H extends Handler {
private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
+ private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
@Override
public void handleMessage(Message msg) {
@@ -208,12 +198,21 @@
case MSG_LOCATION_SETTINGS_CHANGED:
locationSettingsChanged();
break;
+ case MSG_LOCATION_ACTIVE_CHANGED:
+ locationActiveChanged();
+ break;
+ }
+ }
+
+ private void locationActiveChanged() {
+ for (LocationChangeCallback cb : mSettingsChangeCallbacks) {
+ cb.onLocationActiveChanged(mAreActiveLocationRequests);
}
}
private void locationSettingsChanged() {
boolean isEnabled = isLocationEnabled();
- for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) {
+ for (LocationChangeCallback cb : mSettingsChangeCallbacks) {
cb.onLocationSettingsChanged(isEnabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index ba91ffd..b5db78d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -495,12 +495,12 @@
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
for (int i = childCount - 1; i >= 0; i--) {
- updateChildZValue(i, childrenOnTop,
+ childrenOnTop = updateChildZValue(i, childrenOnTop,
resultState, algorithmState, ambientState);
}
}
- protected void updateChildZValue(int i, float childrenOnTop,
+ protected float updateChildZValue(int i, float childrenOnTop,
StackScrollState resultState, StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
ExpandableView child = algorithmState.visibleChildren.get(i);
@@ -538,6 +538,7 @@
} else {
childViewState.zTranslation = baseZ;
}
+ return childrenOnTop;
}
public void setIsExpanded(boolean isExpanded) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
index 6b47ada..e1f56a47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -53,7 +53,7 @@
@Test
public void testDrawImageButNoTextIfPluggedIn() {
mBatteryMeter.setBatteryLevel(0);
- mBatteryMeter.setPluggedIn(true);
+ mBatteryMeter.setCharging(true);
final Canvas canvas = mock(Canvas.class);
mBatteryMeter.draw(canvas);
verify(canvas, atLeastOnce()).drawPath(any(), any());
@@ -63,7 +63,7 @@
@Test
public void testDrawTextIfNotPluggedIn() {
mBatteryMeter.setBatteryLevel(0);
- mBatteryMeter.setPluggedIn(false);
+ mBatteryMeter.setCharging(false);
final Canvas canvas = mock(Canvas.class);
mBatteryMeter.draw(canvas);
verify(canvas, times(1)).drawText(anyString(), anyFloat(), anyFloat(), any());
@@ -72,7 +72,7 @@
@Test
public void testDrawNoTextIfPowerSaveEnabled() {
mBatteryMeter.setBatteryLevel(0);
- mBatteryMeter.setPluggedIn(false);
+ mBatteryMeter.setCharging(false);
mBatteryMeter.setPowerSave(true);
final Canvas canvas = mock(Canvas.class);
mBatteryMeter.draw(canvas);
@@ -84,7 +84,7 @@
int criticalLevel = mResources.getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mBatteryMeter.setBatteryLevel(criticalLevel);
- mBatteryMeter.setPluggedIn(false);
+ mBatteryMeter.setCharging(false);
final Canvas canvas = mock(Canvas.class);
mBatteryMeter.draw(canvas);
String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
@@ -96,7 +96,7 @@
int criticalLevel = mResources.getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mBatteryMeter.setBatteryLevel(criticalLevel + 1);
- mBatteryMeter.setPluggedIn(false);
+ mBatteryMeter.setCharging(false);
final Canvas canvas = mock(Canvas.class);
mBatteryMeter.draw(canvas);
String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
new file mode 100644
index 0000000..1b5d4a4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ExpandHelperTest extends SysuiTestCase {
+
+ private ExpandableNotificationRow mRow;
+ private ExpandHelper mExpandHelper;
+ private ExpandHelper.Callback mCallback;
+
+ @Before
+ @UiThreadTest
+ public void setUp() {
+ Context context = getContext();
+ mRow = new NotificationTestHelper(context).createRow();
+ mCallback = mock(ExpandHelper.Callback.class);
+ mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testAnimationDoesntClearViewIfNewExpansionStarted() {
+ when(mCallback.getMaxExpandHeight(any())).thenReturn(100);
+ mExpandHelper.startExpanding(mRow, 0);
+ mExpandHelper.finishExpanding(false, 0);
+ mExpandHelper.startExpanding(mRow, 0);
+ ObjectAnimator scaleAnimation = mExpandHelper.getScaleAnimation();
+ scaleAnimation.end();
+ mExpandHelper.updateExpansion();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index e7cdcb5..53d842a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,12 +14,14 @@
package com.android.systemui.statusbar.phone;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.LeakCheck.Tracker;
import android.view.Display;
import android.view.WindowManager;
@@ -29,11 +31,18 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.utils.leaks.BaseLeakChecker;
+
import android.testing.TestableLooper.RunWithLooper;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -56,6 +65,20 @@
when(windowManager.getDefaultDisplay()).thenReturn(
defaultDisplay);
mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
+
+ Tracker tracker = mLeakCheck.getTracker("accessibility_manager");
+ AccessibilityManagerWrapper wrapper = new AccessibilityManagerWrapper(mContext) {
+ @Override
+ public void addCallback(AccessibilityServicesStateChangeListener listener) {
+ tracker.getLeakInfo(listener).addAllocation(new Throwable());
+ }
+
+ @Override
+ public void removeCallback(AccessibilityServicesStateChangeListener listener) {
+ tracker.getLeakInfo(listener).clearAllocations();
+ }
+ };
+ mDependency.injectTestDependency(AccessibilityManagerWrapper.class, wrapper);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
index 29d7f1c..838a273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -17,15 +17,20 @@
import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
-public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
+public class FakeLocationController extends BaseLeakChecker<LocationChangeCallback>
implements LocationController {
public FakeLocationController(LeakCheck test) {
super(test, "location");
}
@Override
+ public boolean isLocationActive() {
+ return false;
+ }
+
+ @Override
public boolean isLocationEnabled() {
return false;
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 579c54c..f56cbdd 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3906,6 +3906,11 @@
// OS: O
FINGERPRINT_REMOVE_SIDECAR = 934;
+ // OPEN: Settings > Storage > Movies & TV
+ // CATEGORY: SETTINGS
+ // OS: O
+ APPLICATIONS_STORAGE_MOVIES = 935;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/Android.mk b/services/Android.mk
index 4452543..a4c891b 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -40,8 +40,8 @@
# The convention is to name each service module 'services.$(module_name)'
LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services)) \
- android.hidl.base@1.0-java-static \
- android.hardware.biometrics.fingerprint@2.1-java-static
+ android.hidl.base-V1.0-java-static \
+ android.hardware.biometrics.fingerprint-V2.1-java-static
ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
LOCAL_EMMA_INSTRUMENT := true
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67c2314c..67b5282 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -370,6 +370,23 @@
}
}
+ // AutoFillUiCallback
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
+ synchronized (mLock) {
+ removeSelfLocked();
+ }
+ mHandlerCaller.getHandler().post(() -> {
+ try {
+ synchronized (mLock) {
+ mClient.startIntentSender(intentSender);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error launching auth intent", e);
+ }
+ });
+ }
+
public void setAuthenticationResultLocked(Bundle data) {
if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) {
removeSelf();
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 0f18c87..4449da9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -67,6 +67,7 @@
void requestShowFillUi(AutofillId id, int width, int height,
IAutofillWindowPresenter presenter);
void requestHideFillUi(AutofillId id);
+ void startIntentSender(IntentSender intentSender);
}
public AutoFillUI(@NonNull Context context) {
@@ -201,6 +202,13 @@
mCallback.requestHideFillUi(focusedId);
}
}
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
+ if (mCallback != null) {
+ mCallback.startIntentSender(intentSender);
+ }
+ }
});
});
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index a89df92..b68e3b1 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -17,7 +17,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
import android.graphics.Rect;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
@@ -55,6 +58,7 @@
void requestShowFillUi(int width, int height,
IAutofillWindowPresenter windowPresenter);
void requestHideFillUi();
+ void startIntentSender(IntentSender intentSender);
}
private final @NonNull AutofillWindowPresenter mWindowPresenter =
@@ -84,13 +88,24 @@
final ViewGroup decor = (ViewGroup) inflater.inflate(
R.layout.autofill_dataset_picker, null);
+ final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
+ @Override
+ public boolean onClickHandler(View view, PendingIntent pendingIntent,
+ Intent fillInIntent) {
+ if (pendingIntent != null) {
+ mCallback.startIntentSender(pendingIntent.getIntentSender());
+ }
+ return true;
+ }
+ };
+
if (response.getAuthentication() != null) {
mListView = null;
mAdapter = null;
final View content;
try {
- content = response.getPresentation().apply(context, decor);
+ content = response.getPresentation().apply(context, decor, interceptionHandler);
decor.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
@@ -101,7 +116,7 @@
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
content.measure(widthMeasureSpec, heightMeasureSpec);
- content.setOnClickListener(v -> mCallback.onResponsePicked(response));
+ decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
// TODO(b/33197203 , b/36660292): temporary limiting maximum height and minimum width
mContentWidth = Math.max(content.getMeasuredWidth(), 1000);
mContentHeight = Math.min(content.getMeasuredHeight(), 500);
@@ -118,7 +133,7 @@
final RemoteViews presentation = dataset.getFieldPresentation(index);
final View view;
try {
- view = presentation.apply(context, null);
+ view = presentation.apply(context, null, interceptionHandler);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 8003d21..f896478 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -20,17 +20,17 @@
LOCAL_JAVA_LIBRARIES := \
services.net \
- android.hardware.light@2.0-java \
- android.hardware.power@1.0-java \
- android.hardware.tv.cec@1.0-java \
- android.hidl.manager@1.0-java
+ android.hardware.light-V2.0-java \
+ android.hardware.power-V1.0-java \
+ android.hardware.tv.cec-V1.0-java \
+ android.hidl.manager-V1.0-java
LOCAL_STATIC_JAVA_LIBRARIES := \
tzdata_shared2 \
tzdata_update2 \
- android.hidl.base@1.0-java-static \
- android.hardware.biometrics.fingerprint@2.1-java-static \
- android.hardware.vibrator@1.0-java-constants \
+ android.hidl.base-V1.0-java-static \
+ android.hardware.biometrics.fingerprint-V2.1-java-static \
+ android.hardware.vibrator-V1.0-java-constants \
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 9218c25..d25b3cc 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -84,6 +84,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
@@ -106,6 +107,7 @@
private final Object mServiceConnectionLock = new Object();
private final Handler mHandler;
private final DispatchingContentObserver mContentObserver;
+ private final Function<NetworkScorerAppData, ScoringServiceConnection> mServiceConnProducer;
@GuardedBy("mPackageMonitorLock")
private NetworkScorerPackageMonitor mPackageMonitor;
@@ -238,11 +240,13 @@
}
public NetworkScoreService(Context context) {
- this(context, new NetworkScorerAppManager(context), Looper.myLooper());
+ this(context, new NetworkScorerAppManager(context),
+ ScoringServiceConnection::new, Looper.myLooper());
}
@VisibleForTesting
NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager,
+ Function<NetworkScorerAppData, ScoringServiceConnection> serviceConnProducer,
Looper looper) {
mContext = context;
mNetworkScorerAppManager = networkScoreAppManager;
@@ -257,6 +261,7 @@
mRecommendationRequestTimeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
mHandler = new ServiceHandler(looper);
mContentObserver = new DispatchingContentObserver(context, mHandler);
+ mServiceConnProducer = serviceConnProducer;
}
/** Called when the system is ready to run third-party code but before it actually does so. */
@@ -356,17 +361,17 @@
synchronized (mServiceConnectionLock) {
// If we're connected to a different component then drop it.
if (mServiceConnection != null
- && !mServiceConnection.mAppData.equals(appData)) {
+ && !mServiceConnection.getAppData().equals(appData)) {
unbindFromScoringServiceIfNeeded();
}
// If we're not connected at all then create a new connection.
if (mServiceConnection == null) {
- mServiceConnection = new ScoringServiceConnection(appData);
+ mServiceConnection = mServiceConnProducer.apply(appData);
}
// Make sure the connection is connected (idempotent)
- mServiceConnection.connect(mContext);
+ mServiceConnection.bind(mContext);
}
} else { // otherwise make sure it isn't bound.
unbindFromScoringServiceIfNeeded();
@@ -377,9 +382,9 @@
if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
synchronized (mServiceConnectionLock) {
if (mServiceConnection != null) {
- mServiceConnection.disconnect(mContext);
+ mServiceConnection.unbind(mContext);
if (DBG) Log.d(TAG, "Disconnected from: "
- + mServiceConnection.mAppData.getRecommendationServiceComponent());
+ + mServiceConnection.getAppData().getRecommendationServiceComponent());
}
mServiceConnection = null;
}
@@ -687,7 +692,7 @@
public boolean isCallerActiveScorer(int callingUid) {
synchronized (mServiceConnectionLock) {
return mServiceConnection != null
- && mServiceConnection.mAppData.packageUid == callingUid;
+ && mServiceConnection.getAppData().packageUid == callingUid;
}
}
@@ -720,7 +725,7 @@
if (isCallerSystemProcess(getCallingUid()) || callerCanRequestScores()) {
synchronized (mServiceConnectionLock) {
if (mServiceConnection != null) {
- return mServiceConnection.mAppData;
+ return mServiceConnection.getAppData();
}
}
} else {
@@ -1020,7 +1025,9 @@
mReqRecommendationCallerRef.set(new RequestRecommendationCaller(timeoutMs));
}
- private static class ScoringServiceConnection implements ServiceConnection {
+ // The class and methods need to be public for Mockito to work.
+ @VisibleForTesting
+ public static class ScoringServiceConnection implements ServiceConnection {
private final NetworkScorerAppData mAppData;
private volatile boolean mBound = false;
private volatile boolean mConnected = false;
@@ -1030,7 +1037,8 @@
mAppData = appData;
}
- void connect(Context context) {
+ @VisibleForTesting
+ public void bind(Context context) {
if (!mBound) {
Intent service = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
service.setComponent(mAppData.getRecommendationServiceComponent());
@@ -1039,13 +1047,15 @@
UserHandle.SYSTEM);
if (!mBound) {
Log.w(TAG, "Bind call failed for " + service);
+ context.unbindService(this);
} else {
if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
}
}
}
- void disconnect(Context context) {
+ @VisibleForTesting
+ public void unbind(Context context) {
try {
if (mBound) {
mBound = false;
@@ -1056,17 +1066,30 @@
Log.e(TAG, "Unbind failed.", e);
}
+ mConnected = false;
mRecommendationProvider = null;
}
- INetworkRecommendationProvider getRecommendationProvider() {
+ @VisibleForTesting
+ public NetworkScorerAppData getAppData() {
+ return mAppData;
+ }
+
+ @VisibleForTesting
+ public INetworkRecommendationProvider getRecommendationProvider() {
return mRecommendationProvider;
}
- String getPackageName() {
+ @VisibleForTesting
+ public String getPackageName() {
return mAppData.getRecommendationServiceComponent().getPackageName();
}
+ @VisibleForTesting
+ public boolean isAlive() {
+ return mBound && mConnected;
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5f585cc..eb58e4c 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -959,6 +959,12 @@
mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
}
+ private void sendAccountRemovedBroadcast(int userId) {
+ Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
+ intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -1111,6 +1117,7 @@
notifyPackage(packageToVisibility.getKey(), accounts);
}
}
+ sendAccountRemovedBroadcast(accounts.userId);
} else {
ArrayList<String> accountNames = accountNamesByType.get(account.type);
if (accountNames == null) {
@@ -1971,6 +1978,7 @@
sendNotificationAccountUpdated(resultAccount, accounts);
sendAccountsChangedBroadcast(accounts.userId);
+ sendAccountRemovedBroadcast(accounts.userId);
}
}
return resultAccount;
@@ -2206,6 +2214,7 @@
// Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occurred.
sendAccountsChangedBroadcast(accounts.userId);
+ sendAccountRemovedBroadcast(accounts.userId);
String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
: AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index df250b1..55ee183 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -32,16 +32,10 @@
*/
final class ActivityManagerConstants extends ContentObserver {
// Key names stored in the settings value.
- private static final String KEY_ENFORCE_BG_CHECK = "enforce_bg_check";
private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
- private static final boolean DEFAULT_ENFORCE_BG_CHECK = SystemProperties.getBoolean(
- "debug.bgcheck", true);
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
- // Enforce background check on apps targeting O?
- public boolean ENFORCE_BG_CHECK = DEFAULT_ENFORCE_BG_CHECK;
-
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -115,7 +109,6 @@
// with defaults.
Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e);
}
- ENFORCE_BG_CHECK = mParser.getBoolean(KEY_ENFORCE_BG_CHECK, DEFAULT_ENFORCE_BG_CHECK);
MAX_CACHED_PROCESSES = mParser.getInt(KEY_MAX_CACHED_PROCESSES,
DEFAULT_MAX_CACHED_PROCESSES);
updateMaxCachedProcesses();
@@ -139,9 +132,6 @@
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
- pw.print(" "); pw.print(KEY_ENFORCE_BG_CHECK); pw.print("=");
- pw.println(ENFORCE_BG_CHECK);
-
pw.print(" "); pw.print(KEY_MAX_CACHED_PROCESSES); pw.print("=");
pw.println(MAX_CACHED_PROCESSES);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 96f5c3b..2be5313 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -71,7 +71,6 @@
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.os.Process.getFreeMemory;
-import static android.os.Process.getThreadPriority;
import static android.os.Process.getTotalMemory;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
@@ -1688,6 +1687,7 @@
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64;
static final int NOTIFY_VR_SLEEPING_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
+ static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int START_USER_SWITCH_FG_MSG = 712;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1956,6 +1956,18 @@
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;
+ case DISPATCH_PENDING_INTENT_CANCEL_MSG: {
+ RemoteCallbackList<IResultReceiver> callbacks
+ = (RemoteCallbackList<IResultReceiver>)msg.obj;
+ int N = callbacks.beginBroadcast();
+ for (int i = 0; i < N; i++) {
+ try {
+ callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
+ } catch (RemoteException e) {
+ }
+ }
+ callbacks.finishBroadcast();
+ } break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -2705,17 +2717,6 @@
mConstants = new ActivityManagerConstants(this, mHandler);
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.d(TAG, "Enforcing O+ bg restrictions: " + mConstants.ENFORCE_BG_CHECK);
- StringBuilder sb = new StringBuilder(200);
- sb.append(" ");
- for (String a : getBackgroundLaunchBroadcasts()) {
- sb.append(' '); sb.append(a);
- }
- Slog.d(TAG, "Background implicit broadcasts:");
- Slog.d(TAG, sb.toString());
- }
-
/* static; one-time init here */
if (sKillHandler == null) {
sKillThread = new ServiceThread(TAG + ":kill",
@@ -6423,7 +6424,7 @@
}
didSomething = true;
it.remove();
- pir.canceled = true;
+ makeIntentSenderCanceledLocked(pir);
if (pir.key.activity != null && pir.key.activity.pendingResults != null) {
pir.key.activity.pendingResults.remove(pir.ref);
}
@@ -7433,7 +7434,7 @@
}
return rec;
}
- rec.canceled = true;
+ makeIntentSenderCanceledLocked(rec);
mIntentSenderRecords.remove(key);
}
if (noCreate) {
@@ -7537,7 +7538,7 @@
String msg = "Permission Denial: cancelIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " is not allowed to cancel packges "
+ + " is not allowed to cancel package "
+ rec.key.packageName;
Slog.w(TAG, msg);
throw new SecurityException(msg);
@@ -7550,13 +7551,21 @@
}
void cancelIntentSenderLocked(PendingIntentRecord rec, boolean cleanActivity) {
- rec.canceled = true;
+ makeIntentSenderCanceledLocked(rec);
mIntentSenderRecords.remove(rec.key);
if (cleanActivity && rec.key.activity != null) {
rec.key.activity.pendingResults.remove(rec.ref);
}
}
+ void makeIntentSenderCanceledLocked(PendingIntentRecord rec) {
+ rec.canceled = true;
+ RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
+ if (callbacks != null) {
+ mHandler.obtainMessage(DISPATCH_PENDING_INTENT_CANCEL_MSG, callbacks).sendToTarget();
+ }
+ }
+
@Override
public String getPackageForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
@@ -7571,6 +7580,27 @@
}
@Override
+ public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
+ if (!(sender instanceof PendingIntentRecord)) {
+ return;
+ }
+ synchronized(this) {
+ ((PendingIntentRecord)sender).registerCancelListenerLocked(receiver);
+ }
+ }
+
+ @Override
+ public void unregisterIntentSenderCancelListener(IIntentSender sender,
+ IResultReceiver receiver) {
+ if (!(sender instanceof PendingIntentRecord)) {
+ return;
+ }
+ synchronized(this) {
+ ((PendingIntentRecord)sender).unregisterCancelListenerLocked(receiver);
+ }
+ }
+
+ @Override
public int getUidForIntentSender(IIntentSender sender) {
if (sender instanceof PendingIntentRecord) {
try {
@@ -7834,9 +7864,7 @@
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint();
- final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
- aspectRatio);
- mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, destBounds,
+ mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
stack.setPictureInPictureAspectRatio(aspectRatio);
@@ -7897,7 +7925,7 @@
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
final PinnedActivityStack stack = r.getStack();
- if (!stack.isBoundsAnimatingToFullscreen()) {
+ if (!stack.isAnimatingBoundsToFullscreen()) {
stack.setPictureInPictureAspectRatio(
r.pictureInPictureArgs.getAspectRatio());
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
@@ -8268,7 +8296,7 @@
// Unified app-op and target sdk check
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
- if (mConstants.ENFORCE_BG_CHECK && packageTargetSdk >= Build.VERSION_CODES.O) {
+ if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
@@ -16214,23 +16242,45 @@
pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
if (mIntentSenderRecords.size() > 0) {
- Iterator<WeakReference<PendingIntentRecord>> it
+ // Organize these by package name, so they are easier to read.
+ final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
+ final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
+ final Iterator<WeakReference<PendingIntentRecord>> it
= mIntentSenderRecords.values().iterator();
while (it.hasNext()) {
WeakReference<PendingIntentRecord> ref = it.next();
- PendingIntentRecord rec = ref != null ? ref.get(): null;
- if (dumpPackage != null && (rec == null
- || !dumpPackage.equals(rec.key.packageName))) {
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec == null) {
+ weakRefs.add(ref);
continue;
}
+ if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
+ continue;
+ }
+ ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
+ if (list == null) {
+ list = new ArrayList<>();
+ byPackage.put(rec.key.packageName, list);
+ }
+ list.add(rec);
+ }
+ for (int i = 0; i < byPackage.size(); i++) {
+ ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
printed = true;
- if (rec != null) {
- pw.print(" * "); pw.println(rec);
+ pw.print(" * "); pw.print(byPackage.keyAt(i));
+ pw.print(": "); pw.print(intents.size()); pw.println(" items");
+ for (int j = 0; j < intents.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
if (dumpAll) {
- rec.dump(pw, " ");
+ intents.get(j).dump(pw, " ");
}
- } else {
- pw.print(" * "); pw.println(ref);
+ }
+ }
+ if (weakRefs.size() > 0) {
+ printed = true;
+ pw.println(" * WEAK REFS:");
+ for (int i = 0; i < weakRefs.size(); i++) {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
}
}
}
@@ -23359,7 +23409,7 @@
Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
return;
}
- ((PendingIntentRecord) target).setWhitelistDuration(duration);
+ ((PendingIntentRecord) target).setWhitelistDurationLocked(duration);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5d474eb..4c84d98 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -116,6 +116,7 @@
import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
@@ -230,9 +231,10 @@
// activities and there is a specific combination of stacks.
private static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
+ @VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
// Task is being completely removed from all stacks in the system.
- private static final int REMOVE_TASK_MODE_DESTROYING = 0;
+ protected static final int REMOVE_TASK_MODE_DESTROYING = 0;
// Task is being removed from this stack so we can add it to another stack. In the case we are
// moving we don't want to perform some operations on the task like removing it from window
// manager or recents.
@@ -3990,7 +3992,6 @@
// where we are destroying the task, move this back into removeTask()
mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
!REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY);
- task.removeWindowContainer();
}
removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
}
@@ -5082,6 +5083,8 @@
mRecentTasks.remove(task);
task.removedFromRecents();
}
+
+ task.removeWindowContainer();
}
if (mTaskHistory.isEmpty()) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 68e25c3..4d16e33 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -174,6 +174,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
@@ -2492,11 +2493,21 @@
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- final ActivityStack stack = getStack(PINNED_STACK_ID);
+ final PinnedActivityStack stack = getStack(PINNED_STACK_ID);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
}
+
+ // It is possible for the bounds animation from the WM to call this but be delayed by
+ // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
+ // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
+ // for the AMS lock to be freed. So check and make sure these bounds are still good.
+ final PinnedStackWindowController stackController = stack.getWindowContainerController();
+ if (stackController.pinnedStackResizeAllowed()) {
+ return;
+ }
+
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
mWindowManager.deferSurfaceLayout();
try {
@@ -2857,12 +2868,12 @@
return false;
}
- moveActivityToPinnedStackLocked(r, null /* sourceBounds */, destBounds,
+ moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
true /* moveHomeStackToFront */, "moveTopActivityToPinnedStack");
return true;
}
- void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, Rect destBounds,
+ void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, float aspectRatio,
boolean moveHomeStackToFront, String reason) {
mWindowManager.deferSurfaceLayout();
@@ -2932,6 +2943,11 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
+ // Calculate the default bounds (don't use existing stack bounds as we may have just created
+ // the stack
+ final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
+ aspectRatio, false /* useExistingStackBounds */);
+
// TODO(b/36099777): Schedule the PiP mode change here immediately until we can defer all
// callbacks until after the bounds animation
scheduleUpdatePictureInPictureModeIfNeeded(r.getTask(), destBounds, true /* immediate */);
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
index 4a87941..c2f1890 100644
--- a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -46,6 +46,7 @@
/**
* Writes the contents of a BatteryStats.Uid into a HealthStatsWriter.
*/
+ @SuppressWarnings("deprecation")
public void writeUid(HealthStatsWriter uidWriter, BatteryStats bs, BatteryStats.Uid uid) {
int N;
BatteryStats.Timer timer;
@@ -365,8 +366,7 @@
uid.getSystemCpuTimeUs(STATS_SINCE_UNPLUGGED)/1000);
// MEASUREMENT_CPU_POWER_MAMS
- uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_CPU_POWER_MAMS,
- uid.getCpuPowerMaUs(STATS_SINCE_UNPLUGGED)/1000);
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_CPU_POWER_MAMS, 0);
}
/**
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index f05bfb6..c697f28 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -28,12 +28,14 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.os.IResultReceiver;
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
import java.io.PrintWriter;
@@ -50,6 +52,7 @@
boolean sent = false;
boolean canceled = false;
private long whitelistDuration = 0;
+ private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
String stringName;
String lastTagPrefix;
@@ -191,11 +194,31 @@
ref = new WeakReference<PendingIntentRecord>(this);
}
- void setWhitelistDuration(long duration) {
+ void setWhitelistDurationLocked(long duration) {
this.whitelistDuration = duration;
this.stringName = null;
}
+ public void registerCancelListenerLocked(IResultReceiver receiver) {
+ if (mCancelCallbacks == null) {
+ mCancelCallbacks = new RemoteCallbackList<>();
+ }
+ mCancelCallbacks.register(receiver);
+ }
+
+ public void unregisterCancelListenerLocked(IResultReceiver receiver) {
+ mCancelCallbacks.unregister(receiver);
+ if (mCancelCallbacks.getRegisteredCallbackCount() <= 0) {
+ mCancelCallbacks = null;
+ }
+ }
+
+ public RemoteCallbackList<IResultReceiver> detachCancelListenersLocked() {
+ RemoteCallbackList<IResultReceiver> listeners = mCancelCallbacks;
+ mCancelCallbacks = null;
+ return listeners;
+ }
+
public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
String requiredPermission, Bundle options) {
sendInner(code, intent, resolvedType, finishedReceiver,
@@ -234,7 +257,6 @@
sent = true;
if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {
owner.cancelIntentSenderLocked(this, true);
- canceled = true;
}
Intent finalIntent = key.requestIntent != null
@@ -398,6 +420,13 @@
TimeUtils.formatDuration(whitelistDuration, pw);
pw.println();
}
+ if (mCancelCallbacks != null) {
+ pw.print(prefix); pw.println("mCancelCallbacks:");
+ for (int i = 0; i < mCancelCallbacks.getRegisteredCallbackCount(); i++) {
+ pw.print(prefix); pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(mCancelCallbacks.getRegisteredCallbackItem(i));
+ }
+ }
}
public String toString() {
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 394e902..cd9c42c 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -21,7 +21,7 @@
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
import com.android.server.wm.PinnedStackWindowController;
-import com.android.server.wm.StackWindowController;
+import com.android.server.wm.PinnedStackWindowListener;
import java.util.ArrayList;
import java.util.List;
@@ -29,7 +29,8 @@
/**
* State and management of the pinned stack of activities.
*/
-class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
+class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
+ implements PinnedStackWindowListener {
PinnedActivityStack(ActivityContainer activityContainer,
RecentTasks recentTasks, boolean onTop) {
@@ -55,8 +56,8 @@
getWindowContainerController().setPictureInPictureActions(actions);
}
- boolean isBoundsAnimatingToFullscreen() {
- return getWindowContainerController().isBoundsAnimatingToFullscreen();
+ boolean isAnimatingBoundsToFullscreen() {
+ return getWindowContainerController().isAnimatingBoundsToFullscreen();
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c11f531..2687242 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -23,6 +23,7 @@
import static android.os.Process.FIRST_APPLICATION_UID;
import android.Manifest;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -968,7 +969,8 @@
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
- streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
+ streams[i] =
+ new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
checkAllFixedVolumeDevices();
@@ -1020,7 +1022,14 @@
}
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
- mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
+ final int oldStreamA11yAlias = mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY];
+ if (oldStreamA11yAlias != a11yStreamAlias) {
+ mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
+ System.VOLUME_SETTINGS_INT[a11yStreamAlias];
+ // restore the value from the settings when the alias changes
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
+ }
if (updateVolumes) {
mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
@@ -3992,13 +4001,19 @@
return devices;
}
- public String getSettingNameForDevice(int device) {
- String name = mVolumeIndexSettingName;
- String suffix = AudioSystem.getOutputDeviceName(device);
- if (suffix.isEmpty()) {
- return name;
+ public @Nullable String getSettingNameForDevice(int device) {
+ if (!hasValidSettingsName()) {
+ return null;
}
- return name + "_" + suffix;
+ final String suffix = AudioSystem.getOutputDeviceName(device);
+ if (suffix.isEmpty()) {
+ return mVolumeIndexSettingName;
+ }
+ return mVolumeIndexSettingName + "_" + suffix;
+ }
+
+ private boolean hasValidSettingsName() {
+ return (mVolumeIndexSettingName != null && !mVolumeIndexSettingName.isEmpty());
}
public void readSettings() {
@@ -4033,13 +4048,18 @@
remainingDevices &= ~device;
// retrieve current volume for device
- String name = getSettingNameForDevice(device);
// if no volume stored for current stream and device, use default volume if default
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
- int index = Settings.System.getIntForUser(
- mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ int index;
+ if (!hasValidSettingsName()) {
+ index = defaultIndex;
+ } else {
+ String name = getSettingNameForDevice(device);
+ index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ }
if (index == -1) {
continue;
}
@@ -4420,10 +4440,12 @@
if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {
return;
}
- System.putIntForUser(mContentResolver,
- streamState.getSettingNameForDevice(device),
- (streamState.getIndex(device) + 5)/ 10,
- UserHandle.USER_CURRENT);
+ if (streamState.hasValidSettingsName()) {
+ System.putIntForUser(mContentResolver,
+ streamState.getSettingNameForDevice(device),
+ (streamState.getIndex(device) + 5)/ 10,
+ UserHandle.USER_CURRENT);
+ }
}
private void persistRingerMode(int ringerMode) {
@@ -6059,6 +6081,7 @@
mVolumeController.setA11yMode(sIndependentA11yVolume ?
VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
+ mVolumeController.postVolumeChanged(AudioManager.STREAM_ACCESSIBILITY, 0);
}
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index ad66faa..5dee91d 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -23,9 +23,6 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
-import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
-import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
@@ -34,6 +31,7 @@
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
+import android.net.metrics.ConnectStats;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
@@ -41,7 +39,12 @@
import android.net.metrics.ValidationProbeEvent;
import android.os.Parcelable;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -77,20 +80,51 @@
}
public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) {
- final IpConnectivityEvent out = new IpConnectivityEvent();
+ final IpConnectivityEvent out = buildEvent(ev.netId, ev.transports, ev.ifname);
+ out.timeMs = ev.timestamp;
if (!setEvent(out, ev.data)) {
return null;
}
- out.timeMs = ev.timestamp;
- out.networkId = ev.netId;
- out.transports = ev.transports;
- if (ev.ifname != null) {
- out.ifName = ev.ifname;
- }
- inferLinkLayer(out);
return out;
}
+ public static IpConnectivityEvent toProto(ConnectStats in) {
+ IpConnectivityLogClass.ConnectStatistics stats =
+ new IpConnectivityLogClass.ConnectStatistics();
+ stats.connectCount = in.connectCount;
+ stats.connectBlockingCount = in.connectBlockingCount;
+ stats.ipv6AddrCount = in.ipv6ConnectCount;
+ stats.latenciesMs = in.latencies.toArray();
+ stats.errnosCounters = toPairArray(in.errnos);
+ final IpConnectivityEvent out = buildEvent(in.netId, in.transports, null);
+ out.setConnectStatistics(stats);
+ return out;
+ }
+
+
+ public static IpConnectivityEvent toProto(DnsEvent in) {
+ IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch =
+ new IpConnectivityLogClass.DNSLookupBatch();
+ in.resize(in.eventCount);
+ dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
+ dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
+ dnsLookupBatch.latenciesMs = in.latenciesMs;
+ final IpConnectivityEvent out = buildEvent(in.netId, in.transports, null);
+ out.setDnsLookupBatch(dnsLookupBatch);
+ return out;
+ }
+
+ private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
+ final IpConnectivityEvent ev = new IpConnectivityEvent();
+ ev.networkId = netId;
+ ev.transports = transports;
+ if (ifname != null) {
+ ev.ifName = ifname;
+ }
+ inferLinkLayer(ev);
+ return ev;
+ }
+
private static boolean setEvent(IpConnectivityEvent out, Parcelable in) {
if (in instanceof DhcpErrorEvent) {
setDhcpErrorEvent(out, (DhcpErrorEvent) in);
@@ -102,11 +136,6 @@
return true;
}
- if (in instanceof DnsEvent) {
- setDnsEvent(out, (DnsEvent) in);
- return true;
- }
-
if (in instanceof IpManagerEvent) {
setIpManagerEvent(out, (IpManagerEvent) in);
return true;
@@ -163,16 +192,6 @@
out.setDhcpEvent(dhcpEvent);
}
- private static void setDnsEvent(IpConnectivityEvent out, DnsEvent in) {
- IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch =
- new IpConnectivityLogClass.DNSLookupBatch();
- dnsLookupBatch.networkId = netIdOf(in.netId);
- dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
- dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
- dnsLookupBatch.latenciesMs = in.latenciesMs;
- out.setDnsLookupBatch(dnsLookupBatch);
- }
-
private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) {
IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent =
new IpConnectivityLogClass.IpProvisioningEvent();
@@ -268,6 +287,18 @@
return out;
}
+ private static Pair[] toPairArray(SparseIntArray counts) {
+ final int s = counts.size();
+ Pair[] pairs = new Pair[s];
+ for (int i = 0; i < s; i++) {
+ Pair p = new Pair();
+ p.key = counts.keyAt(i);
+ p.value = counts.valueAt(i);
+ pairs[i] = p;
+ }
+ return pairs;
+ }
+
private static NetworkId netIdOf(int netid) {
final NetworkId ni = new NetworkId();
ni.networkId = netid;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index da56a07..475d786 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -76,7 +76,8 @@
@VisibleForTesting
public final Impl impl = new Impl();
- private NetdEventListenerService mNetdListener;
+ @VisibleForTesting
+ NetdEventListenerService mNetdListener;
@GuardedBy("mLock")
private ArrayList<ConnectivityMetricsEvent> mBuffer;
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 7b9c60c..214cfce 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -18,10 +18,9 @@
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetdEventCallback;
import android.net.Network;
-import android.net.NetworkRequest;
+import android.net.NetworkCapabilities;
import android.net.metrics.ConnectStats;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
@@ -29,17 +28,17 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.BitUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.TokenBucket;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.function.Function;
+import java.util.function.IntFunction;
/**
* Implementation of the INetdEventListener interface.
@@ -52,8 +51,7 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
- // TODO: read this constant from system property
- private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
+ private static final int INITIAL_DNS_BATCH_SIZE = 100;
// Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
// bursts of 5000 measurements.
@@ -61,81 +59,17 @@
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
- // Stores the results of a number of consecutive DNS lookups on the same network.
- // This class is not thread-safe and it is the responsibility of the service to call its methods
- // on one thread at a time.
- private class DnsEventBatch {
- private final int mNetId;
-
- private final byte[] mEventTypes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
- private final byte[] mReturnCodes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
- private final int[] mLatenciesMs = new int[MAX_LOOKUPS_PER_DNS_EVENT];
- private int mEventCount;
-
- public DnsEventBatch(int netId) {
- mNetId = netId;
- }
-
- public void addResult(byte eventType, byte returnCode, int latencyMs) {
- mEventTypes[mEventCount] = eventType;
- mReturnCodes[mEventCount] = returnCode;
- mLatenciesMs[mEventCount] = latencyMs;
- mEventCount++;
- if (mEventCount == MAX_LOOKUPS_PER_DNS_EVENT) {
- logAndClear();
- }
- }
-
- public void logAndClear() {
- // Did we lose a race with addResult?
- if (mEventCount == 0) {
- return;
- }
-
- // Only log as many events as we actually have.
- byte[] eventTypes = Arrays.copyOf(mEventTypes, mEventCount);
- byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount);
- int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount);
- mMetricsLog.log(new DnsEvent(mNetId, eventTypes, returnCodes, latenciesMs));
- maybeLog("Logging %d results for netId %d", mEventCount, mNetId);
- mEventCount = 0;
- }
-
- // For debugging and unit tests only.
- public String toString() {
- return String.format("%s %d %d", getClass().getSimpleName(), mNetId, mEventCount);
- }
- }
-
- // Only sorted for ease of debugging. Because we only typically have a handful of networks up
- // at any given time, performance is not a concern.
+ // Sparse arrays of DNS and connect events, grouped by net id.
@GuardedBy("this")
- private final SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
+ private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
+ @GuardedBy("this")
+ private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
- // We register a NetworkCallback to ensure that when a network disconnects, we flush the DNS
- // queries we've logged on that network. Because we do not do this periodically, we might lose
- // up to MAX_LOOKUPS_PER_DNS_EVENT lookup stats on each network when the system is shutting
- // down. We believe this to be sufficient for now.
private final ConnectivityManager mCm;
- private final IpConnectivityLog mMetricsLog;
- private final NetworkCallback mNetworkCallback = new NetworkCallback() {
- @Override
- public void onLost(Network network) {
- synchronized (NetdEventListenerService.this) {
- DnsEventBatch batch = mEventBatches.remove(network.netId);
- if (batch != null) {
- batch.logAndClear();
- }
- }
- }
- };
@GuardedBy("this")
private final TokenBucket mConnectTb =
new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
- @GuardedBy("this")
- private ConnectStats mConnectStats = makeConnectStats();
-
// Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
// by the device owner. It's DevicePolicyManager's responsibility to ensure that.
@GuardedBy("this")
@@ -152,16 +86,13 @@
}
public NetdEventListenerService(Context context) {
- this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog());
+ this(context.getSystemService(ConnectivityManager.class));
}
@VisibleForTesting
- public NetdEventListenerService(ConnectivityManager cm, IpConnectivityLog log) {
+ public NetdEventListenerService(ConnectivityManager cm) {
// We are started when boot is complete, so ConnectivityService should already be running.
mCm = cm;
- mMetricsLog = log;
- final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
- mCm.registerNetworkCallback(request, mNetworkCallback);
}
@Override
@@ -172,16 +103,16 @@
throws RemoteException {
maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
- DnsEventBatch batch = mEventBatches.get(netId);
- if (batch == null) {
- batch = new DnsEventBatch(netId);
- mEventBatches.put(netId, batch);
+ DnsEvent dnsEvent = mDnsEvents.get(netId);
+ if (dnsEvent == null) {
+ dnsEvent = makeDnsEvent(netId);
+ mDnsEvents.put(netId, dnsEvent);
}
- batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
+ dnsEvent.addResult((byte) eventType, (byte) returnCode, latencyMs);
if (mNetdEventCallback != null) {
- mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount,
- System.currentTimeMillis(), uid);
+ long timestamp = System.currentTimeMillis();
+ mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
}
}
@@ -192,7 +123,12 @@
int port, int uid) throws RemoteException {
maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
- mConnectStats.addEvent(error, latencyMs, ipAddr);
+ ConnectStats connectStats = mConnectEvents.get(netId);
+ if (connectStats == null) {
+ connectStats = makeConnectStats(netId);
+ mConnectEvents.put(netId, connectStats);
+ }
+ connectStats.addEvent(error, latencyMs, ipAddr);
if (mNetdEventCallback != null) {
mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
@@ -200,21 +136,8 @@
}
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
- events.add(flushConnectStats());
- // TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies
- }
-
- private IpConnectivityEvent connectStatsProto() {
- // TODO: add transport information
- IpConnectivityEvent ev = new IpConnectivityEvent();
- ev.setConnectStatistics(mConnectStats.toProto());
- return ev;
- }
-
- private IpConnectivityEvent flushConnectStats() {
- IpConnectivityEvent ev = connectStatsProto();
- mConnectStats = makeConnectStats();
- return ev;
+ flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
+ flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
}
public synchronized void dump(PrintWriter writer) {
@@ -226,18 +149,47 @@
}
public synchronized void list(PrintWriter pw) {
- for (DnsEventBatch batch : mEventBatches.values()) {
- pw.println(batch.toString());
- }
- pw.println(mConnectStats.toString());
+ listEvents(pw, mConnectEvents, (x) -> x);
+ listEvents(pw, mDnsEvents, (x) -> x);
}
public synchronized void listAsProtos(PrintWriter pw) {
- pw.println(connectStatsProto().toString());
+ listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto);
+ listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto);
}
- private ConnectStats makeConnectStats() {
- return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
+ private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
+ Function<T, IpConnectivityEvent> mapper) {
+ for (int i = 0; i < in.size(); i++) {
+ out.add(mapper.apply(in.valueAt(i)));
+ }
+ in.clear();
+ }
+
+ public static <T> void listEvents(
+ PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
+ for (int i = 0; i < events.size(); i++) {
+ pw.println(mapper.apply(events.valueAt(i)).toString());
+ }
+ }
+
+ private ConnectStats makeConnectStats(int netId) {
+ long transports = getTransports(netId);
+ return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
+ }
+
+ private DnsEvent makeDnsEvent(int netId) {
+ long transports = getTransports(netId);
+ return new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE);
+ }
+
+ private long getTransports(int netId) {
+ // TODO: directly query ConnectivityService instead of going through Binder interface.
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId));
+ if (nc == null) {
+ return 0;
+ }
+ return BitUtils.packBits(nc.getTransportTypes());
}
private static void maybeLog(String s, Object... args) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 3e53aca..c4ad455 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -1061,7 +1061,7 @@
}
private void logValidationProbe(long durationMs, int probeType, int probeResult) {
- long transports = mNetworkAgentInfo.networkCapabilities.getTransports();
+ int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
boolean isFirstValidation = validationStage().isFirstValidation;
ValidationProbeEvent ev = new ValidationProbeEvent();
ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d8e7e7d..f9dff3e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3071,32 +3071,34 @@
public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
int userId) {
checkCallerIsSystem();
- synchronized (mNotificationLock) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- NotificationRecord r =
- findNotificationLocked(pkg, null, notificationId, userId);
- if (r == null) {
- Log.d(TAG,
- "stripForegroundServiceFlag: Could not find notification with "
- + "pkg=" + pkg + " / id=" + notificationId
- + " / userId=" + userId);
- return;
- }
- StatusBarNotification sbn = r.sbn;
- // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
- // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
- // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
- // initially *and* force remove FLAG_FOREGROUND_SERVICE.
- sbn.getNotification().flags =
- (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
- mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
- mGroupHelper.onNotificationPosted(sbn);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mNotificationLock) {
+ removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId);
+ removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId);
}
- });
+ }
+ });
+ }
+
+ private void removeForegroundServiceFlagByListLocked(
+ ArrayList<NotificationRecord> notificationList, String pkg, int notificationId, int userId) {
+ NotificationRecord r =
+ findNotificationByListLocked(notificationList, pkg, null, notificationId, userId);
+ if (r == null) {
+ return;
}
+ StatusBarNotification sbn = r.sbn;
+ // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
+ // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
+ // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
+ // initially *and* force remove FLAG_FOREGROUND_SERVICE.
+ sbn.getNotification().flags =
+ (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
+ mRankingHelper.sort(mNotificationList);
+ mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
+ mGroupHelper.onNotificationPosted(sbn);
}
};
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c25ab37..650d2f6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1833,6 +1833,10 @@
extras.putInt(Intent.EXTRA_UID, res.uid);
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ } else {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
@@ -7095,17 +7099,23 @@
// used when either 1) the service is in an instant application and the
// caller is not the same instant application or 2) the calling package is
// ephemeral and the activity is not visible to ephemeral applications.
+ final boolean matchInstantApp =
+ (flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
+ final boolean isTargetInstantApp =
+ (si.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(si.flags & ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0;
final boolean blockResolution =
!isTargetSameInstantApp
- && ((matchVisibleToInstantAppOnly && isCallerInstantApp
+ && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+ || (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
@@ -7192,6 +7202,7 @@
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
+ final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
ComponentName comp = intent.getComponent();
@@ -7205,9 +7216,33 @@
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
- final ResolveInfo ri = new ResolveInfo();
- ri.providerInfo = pi;
- list.add(ri);
+ // When specifying an explicit component, we prevent the provider from being
+ // used when either 1) the provider is in an instant application and the
+ // caller is not the same instant application or 2) the calling package is an
+ // instant application and the provider is not visible to instant applications.
+ final boolean matchInstantApp =
+ (flags & PackageManager.MATCH_INSTANT) != 0;
+ final boolean matchVisibleToInstantAppOnly =
+ (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isCallerInstantApp =
+ instantAppPkgName != null;
+ final boolean isTargetSameInstantApp =
+ comp.getPackageName().equals(instantAppPkgName);
+ final boolean isTargetInstantApp =
+ (pi.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+ final boolean isTargetHiddenFromInstantApp =
+ (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0;
+ final boolean blockResolution =
+ !isTargetSameInstantApp
+ && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+ || (matchVisibleToInstantAppOnly && isCallerInstantApp
+ && isTargetHiddenFromInstantApp));
+ if (!blockResolution) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.providerInfo = pi;
+ list.add(ri);
+ }
}
return list;
}
@@ -7216,17 +7251,67 @@
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return mProviders.queryIntent(intent, resolvedType, flags, userId);
+ return applyPostContentProviderResolutionFilter(
+ mProviders.queryIntent(intent, resolvedType, flags, userId),
+ instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return mProviders.queryIntentForPackage(
- intent, resolvedType, flags, pkg.providers, userId);
+ return applyPostContentProviderResolutionFilter(
+ mProviders.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.providers, userId),
+ instantAppPkgName);
}
return Collections.emptyList();
}
}
+ private List<ResolveInfo> applyPostContentProviderResolutionFilter(
+ List<ResolveInfo> resolveInfos, String instantAppPkgName) {
+ // TODO: When adding on-demand split support for non-instant applications, remove
+ // this check and always apply post filtering
+ if (instantAppPkgName == null) {
+ return resolveInfos;
+ }
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ final ResolveInfo info = resolveInfos.get(i);
+ final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
+ // allow providers that are defined in the provided package
+ if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
+ if (info.providerInfo.splitName != null
+ && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
+ info.providerInfo.splitName)) {
+ // requested provider is defined in a split that hasn't been installed yet.
+ // add the installer to the resolve list
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
+ installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+ info.providerInfo.packageName, info.providerInfo.splitName,
+ info.providerInfo.applicationInfo.versionCode);
+ // make sure this resolver is the default
+ installerInfo.isDefault = true;
+ installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ installerInfo.filter = new IntentFilter();
+ // load resources from the correct package
+ installerInfo.resolvePackageName = info.getComponentInfo().packageName;
+ resolveInfos.set(i, installerInfo);
+ }
+ continue;
+ }
+ // allow providers that have been explicitly exposed to instant applications
+ if (!isEphemeralApp
+ && ((info.providerInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0)) {
+ continue;
+ }
+ resolveInfos.remove(i);
+ }
+ return resolveInfos;
+ }
+
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
@@ -7558,17 +7643,38 @@
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, name);
+ final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProvidersByAuthority.get(name);
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
- return ps != null
- && mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)
- ? PackageParser.generateProviderInfo(provider, flags,
- ps.readUserState(userId), userId)
- : null;
+ if (ps != null) {
+ final boolean isInstantApp = ps.getInstantApp(userId);
+ // normal application; filter out instant application provider
+ if (instantAppPkgName == null && isInstantApp) {
+ return null;
+ }
+ // instant application; filter out other instant applications
+ if (instantAppPkgName != null
+ && isInstantApp
+ && !provider.owner.packageName.equals(instantAppPkgName)) {
+ return null;
+ }
+ // instant application; filter out non-exposed provider
+ if (instantAppPkgName != null
+ && (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0) {
+ return null;
+ }
+ // provider not enabled
+ if (!mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)) {
+ return null;
+ }
+ return PackageParser.generateProviderInfo(
+ provider, flags, ps.readUserState(userId), userId);
+ }
+ return null;
}
}
@@ -12772,8 +12878,26 @@
if (ps == null) {
return null;
}
+ final PackageUserState userState = ps.readUserState(userId);
+ final boolean matchVisibleToInstantApp =
+ (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+ final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+ // throw out filters that aren't visible to instant applications
+ if (matchVisibleToInstantApp
+ && !(info.isVisibleToInstantApp() || userState.instantApp)) {
+ return null;
+ }
+ // throw out instant application filters if we're not explicitly requesting them
+ if (!isInstantApp && userState.instantApp) {
+ return null;
+ }
+ // throw out instant application filters if updates are available; will trigger
+ // instant application resolution
+ if (userState.instantApp && ps.isUpdateAvailable()) {
+ return null;
+ }
ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags,
- ps.readUserState(userId), userId);
+ userState, userId);
if (pi == null) {
return null;
}
@@ -13271,8 +13395,10 @@
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userIds[0], appId));
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, 0, null, null, userIds);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, userIds);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0, null, null, userIds);
if (isSystem) {
mHandler.post(() -> {
for (int userId : userIds) {
@@ -23355,7 +23481,6 @@
intent, resolvedType, flags, userId, callingUid, true /*includeInstantApps*/);
}
-
@Override
public void addIsolatedUid(int isolatedUid, int ownerUid) {
synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 95fb5af..e6df49a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5238,7 +5238,7 @@
@Override
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
- final boolean visible = !win.isGoneForLayoutLw();
+ final boolean visible = !win.isGoneForLayoutLw() && win.getAttrs().alpha > 0f;
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible);
applyKeyguardPolicyLw(win, imeTarget);
final int fl = PolicyControl.getWindowFlags(win, attrs);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a519acc..25a0772 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1954,6 +1954,33 @@
}
@Override
+ public void requestChannelBrowsable(Uri channelUri, int userId)
+ throws RemoteException {
+ final String callingPackageName = getCallingPackageName();
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "requestChannelBrowsable");
+ try {
+ Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
+ List<ResolveInfo> list = getContext().getPackageManager()
+ .queryBroadcastReceivers(intent, 0);
+ if (list != null) {
+ for (ResolveInfo info : list) {
+ String receiverPackageName = info.activityInfo.packageName;
+ intent.putExtra(TvContract.EXTRA_CHANNEL_ID, ContentUris.parseId(
+ channelUri));
+ intent.putExtra(TvContract.EXTRA_PACKAGE_NAME, callingPackageName);
+ intent.setPackage(receiverPackageName);
+ getContext().sendBroadcastAsUser(intent, new UserHandle(resolvedUserId));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
@SuppressWarnings("resource")
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 2811145..f41eed5 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -33,6 +33,8 @@
import android.view.animation.Interpolator;
import android.view.WindowManagerInternal;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Enables animating bounds of objects.
*
@@ -103,7 +105,8 @@
com.android.internal.R.interpolator.fast_out_slow_in);
}
- private final class BoundsAnimator extends ValueAnimator
+ @VisibleForTesting
+ final class BoundsAnimator extends ValueAnimator
implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
private final AnimateBoundsUser mTarget;
private final Rect mFrom = new Rect();
@@ -113,10 +116,12 @@
private final boolean mMoveToFullScreen;
// True if this this animation was cancelled and will be replaced the another animation from
// the same {@link #AnimateBoundsUser} target.
- private boolean mSkipAnimationEnd;
+ private boolean mSkipFinalResize;
// True if this animation replaced a previous animation of the same
// {@link #AnimateBoundsUser} target.
private final boolean mSkipAnimationStart;
+ // True if this animation was cancelled by the user, not as a part of a replacing animation
+ private boolean mSkipAnimationEnd;
// True if this animation is not replacing a previous animation, or if the previous
// animation is animating to a different fullscreen state than the current animation.
// We use this to ensure that we always provide a consistent set/order of callbacks when we
@@ -200,7 +205,11 @@
// Whoops, the target doesn't feel like animating anymore. Let's immediately finish
// any further animation.
if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
- animation.cancel();
+
+ // Since we are cancelling immediately without a replacement animation, send the
+ // animation end to maintain callback parity, but also skip any further resizes
+ prepareCancel(false /* skipAnimationEnd */, true /* skipFinalResize */);
+ cancel();
}
}
@@ -208,7 +217,7 @@
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
+ " mMoveToFullScreen=" + mMoveToFullScreen
- + " mSkipAnimationEnd=" + mSkipAnimationEnd
+ + " mSkipFinalResize=" + mSkipFinalResize
+ " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
+ " mAppTransitionIsRunning=" + mAppTransition.isRunning());
@@ -222,10 +231,15 @@
return;
}
+ if (!mSkipFinalResize) {
+ // If not cancelled, resize the pinned stack to the final size. All calls to
+ // setPinnedStackSize() must be done between onAnimationStart() and onAnimationEnd()
+ mTarget.setPinnedStackSize(mTo, null);
+ }
+
finishAnimation();
- mTarget.setPinnedStackSize(mTo, null);
- if (mMoveToFullScreen && !mSkipAnimationEnd) {
+ if (mMoveToFullScreen && !mSkipFinalResize) {
mTarget.moveToFullscreen();
}
}
@@ -235,10 +249,16 @@
finishAnimation();
}
+ public void prepareCancel(boolean skipAnimationEnd, boolean skipFinalResize) {
+ if (DEBUG) Slog.d(TAG, "prepareCancel: skipAnimationEnd=" + skipAnimationEnd
+ + " skipFinalResize=" + skipFinalResize);
+ mSkipAnimationEnd = skipAnimationEnd;
+ mSkipFinalResize = skipFinalResize;
+ }
+
@Override
public void cancel() {
- mSkipAnimationEnd = true;
- if (DEBUG) Slog.d(TAG, "cancel: willReplace mTarget=" + mTarget);
+ if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
super.cancel();
}
@@ -273,19 +293,15 @@
public interface AnimateBoundsUser {
/**
- * Asks the target to directly (without any intermediate steps, like scheduling animation)
- * resize its bounds.
- *
- * @return Whether the target still wants to be animated and successfully finished the
- * operation. If it returns false, the animation will immediately be cancelled. The target
- * should return false when something abnormal happened, e.g. it was completely removed
- * from the hierarchy and is not valid anymore.
- */
- boolean setSize(Rect bounds);
- /**
- * Behaves as setSize, but freezes the bounds of any tasks in the target at taskBounds,
+ * Sets the size of the target (without any intermediate steps, like scheduling animation)
+ * but freezes the bounds of any tasks in the target at taskBounds,
* to allow for more flexibility during resizing. Only works for the pinned stack at the
* moment.
+ *
+ * @return Whether the target should continue to be animated and this call was successful.
+ * If false, the animation will be cancelled because the user has determined that the
+ * animation is now invalid and not required. In such a case, the cancel will trigger the
+ * animation end callback as well, but will not send any further size changes.
*/
boolean setPinnedStackSize(Rect bounds, Rect taskBounds);
@@ -310,11 +326,20 @@
*/
void onAnimationEnd();
+ /**
+ * Callback for the target to inform it to reparent to the fullscreen stack.
+ */
void moveToFullscreen();
}
- void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration,
- boolean moveToFullscreen) {
+ public void animateBounds(final AnimateBoundsUser target, Rect from, Rect to,
+ int animationDuration, boolean moveToFullscreen) {
+ animateBoundsImpl(target, from, to, animationDuration, moveToFullscreen);
+ }
+
+ @VisibleForTesting
+ BoundsAnimator animateBoundsImpl(final AnimateBoundsUser target, Rect from, Rect to,
+ int animationDuration, boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
final boolean animatingToNewFullscreenState = (existing == null) ||
@@ -326,12 +351,15 @@
if (replacing) {
if (existing.isAnimatingTo(to)) {
- // Just les the current animation complete if it has the same destination as the
+ // Just let the current animation complete if it has the same destination as the
// one we are trying to start.
if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
+ " ignoring...");
- return;
+ return existing;
}
+ // Since we are replacing, we skip both animation start and end callbacks, and don't
+ // animate to the final bounds when cancelling
+ existing.prepareCancel(true /* skipAnimationEnd */, true /* skipFinalResize */);
existing.cancel();
}
final BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen,
@@ -342,5 +370,6 @@
: DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.start();
+ return animator;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2f64cd4..058fdae 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1420,6 +1420,13 @@
changedStackList.add(stack.mStackId);
}
}
+
+ // If there was no pinned stack, we still need to notify the controller of the display info
+ // update as a result of the config change. We do this here to consolidate the flow between
+ // changes when there is and is not a stack.
+ if (getStackById(PINNED_STACK_ID) == null) {
+ mPinnedStackControllerLocked.onDisplayInfoChanged();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 3ce61f0..1684878 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -147,7 +147,6 @@
void onConfigurationChanged() {
reloadResources();
- notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
/**
@@ -241,13 +240,31 @@
}
/**
+ * In the case where the display rotation is changed but there is no stack, we can't depend on
+ * onTaskStackBoundsChanged() to be called. But we still should update our known display info
+ * with the new state so that we can update SystemUI.
+ */
+ synchronized void onDisplayInfoChanged() {
+ mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+ }
+
+ /**
* Updates the display info, calculating and returning the new stack and movement bounds in the
* new orientation of the device if necessary.
*/
- void onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
+ boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
if (mDisplayInfo.equals(displayInfo)) {
- return;
+ // We are already in the right orientation, ignore
+ outBounds.setEmpty();
+ return false;
+ } else if (targetBounds.isEmpty()) {
+ // The stack is null, we are just initializing the stack, so just store the display info
+ // and ignore
+ mDisplayInfo.copyFrom(displayInfo);
+ outBounds.setEmpty();
+ return false;
}
mTmpRect.set(targetBounds);
@@ -272,6 +289,7 @@
notifyMovementBoundsChanged(false /* fromImeAdjustment */);
outBounds.set(postChangeStackBounds);
+ return true;
}
/**
@@ -371,7 +389,7 @@
final Rect animatingBounds = mTmpAnimatingBoundsRect;
final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
if (pinnedStack != null) {
- pinnedStack.getAnimatingBounds(animatingBounds);
+ pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
} else {
animatingBounds.set(normalBounds);
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 0145454..135832e 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -32,8 +32,8 @@
private Rect mTmpBoundsRect = new Rect();
- public PinnedStackWindowController(int stackId, StackWindowListener listener, int displayId,
- boolean onTop, Rect outBounds) {
+ public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
+ int displayId, boolean onTop, Rect outBounds) {
super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
}
@@ -63,7 +63,7 @@
final Rect originalBounds = new Rect();
mContainer.getBounds(originalBounds);
- mContainer.setAnimatingBounds(sourceBounds, toBounds);
+ mContainer.setAnimationFinalBounds(sourceBounds, toBounds);
UiThread.getHandler().post(() -> {
if (mContainer == null) {
return;
@@ -84,9 +84,10 @@
}
final int displayId = mContainer.getDisplayContent().getDisplayId();
- final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+ final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio,
+ true /* useExistingStackBounds */);
final Rect targetBounds = new Rect();
- mContainer.getAnimatingBounds(targetBounds);
+ mContainer.getAnimationOrCurrentBounds(targetBounds);
final PinnedStackController pinnedStackController =
mContainer.getDisplayContent().getPinnedStackController();
@@ -117,8 +118,12 @@
/**
* @return whether the bounds are currently animating to fullscreen.
*/
- public boolean isBoundsAnimatingToFullscreen() {
- return mContainer.isBoundsAnimatingToFullscreen();
+ public boolean isAnimatingBoundsToFullscreen() {
+ return mContainer.isAnimatingBoundsToFullscreen();
+ }
+
+ public boolean pinnedStackResizeAllowed() {
+ return mContainer.pinnedStackResizeAllowed();
}
/**
@@ -132,4 +137,16 @@
}
return bounds;
}
+
+ /**
+ * The following calls are made from WM to AM.
+ */
+
+ /** Calls directly into activity manager so window manager lock shouldn't held. */
+ public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+ if (mListener != null) {
+ PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
+ listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
new file mode 100644
index 0000000..12b9c1f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.graphics.Rect;
+
+/**
+ * Interface used by the creator of {@link PinnedStackWindowController} to listen to changes with
+ * the stack container.
+ */
+public interface PinnedStackWindowListener extends StackWindowListener {
+
+ /**
+ * Called when the stack container pinned stack animation will change the picture-in-picture
+ * mode. This is a direct call into ActivityManager.
+ */
+ default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index bf024cf..b927e67 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -368,13 +368,6 @@
}
}
- /** Calls directly into activity manager so window manager lock shouldn't held. */
- void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
- if (mListener != null) {
- mListener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
- }
- }
-
void requestResize(Rect bounds) {
mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java
index a55f9df..c763c17 100644
--- a/services/core/java/com/android/server/wm/StackWindowListener.java
+++ b/services/core/java/com/android/server/wm/StackWindowListener.java
@@ -26,10 +26,4 @@
/** Called when the stack container would like its controller to resize. */
void requestResize(Rect bounds);
-
- /**
- * Called when the stack container pinned stack animation will change the picture-in-picture
- * mode. This is a direct call into ActivityManager.
- */
- default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b816d81..5c46ca9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -595,8 +595,7 @@
* we will have a jump at the end.
*/
boolean isFloating() {
- return StackId.tasksAreFloating(mStack.mStackId)
- && !mStack.isBoundsAnimatingToFullscreen();
+ return StackId.tasksAreFloating(mStack.mStackId) && !mStack.isAnimatingBoundsToFullscreen();
}
WindowState getTopVisibleAppMainWindow() {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1a67ac7..d7c41d3 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -77,6 +77,7 @@
/** For comparison with DisplayContent bounds. */
private Rect mTmpRect = new Rect();
private Rect mTmpRect2 = new Rect();
+ private Rect mTmpRect3 = new Rect();
/** Content limits relative to the DisplayContent this sits in. */
private Rect mBounds = new Rect();
@@ -125,7 +126,11 @@
// perfectly fit the region it would have been cropped to. We may also avoid certain logic we
// would otherwise apply while resizing, while resizing in the bounds animating mode.
private boolean mBoundsAnimating = false;
+ // Set when an animation has been requested but has not yet started from the UI thread. This is
+ // cleared when the animation actually starts.
+ private boolean mBoundsAnimatingRequested = false;
private boolean mBoundsAnimatingToFullscreen = false;
+ private boolean mCancelCurrentBoundsAnimation = false;
private Rect mBoundsAnimationTarget = new Rect();
private Rect mBoundsAnimationSourceBounds = new Rect();
@@ -262,12 +267,6 @@
if (mDisplayContent != null) {
mDisplayContent.mDimLayerController.updateDimLayer(this);
- if (mStackId == PINNED_STACK_ID) {
- // Update the bounds based on any changes to the display info
- getAnimatingBounds(mTmpRect2);
- mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(mTmpRect2,
- bounds);
- }
mAnimationBackgroundSurface.setBounds(bounds);
}
@@ -320,10 +319,11 @@
}
/**
- * Sets the bounds animation target bounds. This can't currently be done in onAnimationStart()
- * since that is started on the UiThread.
+ * Sets the bounds animation target bounds ahead of an animation. This can't currently be done
+ * in onAnimationStart() since that is started on the UiThread.
*/
- void setAnimatingBounds(Rect sourceBounds, Rect destBounds) {
+ void setAnimationFinalBounds(Rect sourceBounds, Rect destBounds) {
+ mBoundsAnimatingRequested = true;
if (sourceBounds != null) {
mBoundsAnimationSourceBounds.set(sourceBounds);
} else {
@@ -337,23 +337,26 @@
}
/**
- * @return the source bounds for the bounds animation.
+ * @return the final bounds for the bounds animation.
*/
- void getAnimatingSourceBounds(Rect outBounds) {
- if (!mBoundsAnimationSourceBounds.isEmpty()) {
- outBounds.set(mBoundsAnimationSourceBounds);
- return;
- }
- outBounds.setEmpty();
+ void getFinalAnimationBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationTarget);
}
/**
- * @return the bounds that the task stack is currently being animated towards, or the current
- * stack bounds if there is no animation in progress.
+ * @return the final source bounds for the bounds animation.
*/
- void getAnimatingBounds(Rect outBounds) {
- if (!mBoundsAnimationTarget.isEmpty()) {
- outBounds.set(mBoundsAnimationTarget);
+ void getFinalAnimationSourceBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationSourceBounds);
+ }
+
+ /**
+ * @return the final animation bounds if the task stack is currently being animated, or the
+ * current stack bounds otherwise.
+ */
+ void getAnimationOrCurrentBounds(Rect outBounds) {
+ if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
+ getFinalAnimationBounds(outBounds);
return;
}
getBounds(outBounds);
@@ -398,6 +401,24 @@
// as it's going away soon anyway.
return false;
}
+
+ if (mStackId == PINNED_STACK_ID) {
+ getAnimationOrCurrentBounds(mTmpRect2);
+ boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3);
+ if (updated) {
+ mBoundsAfterRotation.set(mTmpRect3);
+
+ // Once we've set the bounds based on the rotation of the old bounds in the new
+ // orientation, clear the animation target bounds since they are obsolete, and
+ // cancel any currently running animations
+ mBoundsAnimationTarget.setEmpty();
+ mBoundsAnimationSourceBounds.setEmpty();
+ mCancelCurrentBoundsAnimation = true;
+ return true;
+ }
+ }
+
final int newRotation = getDisplayInfo().rotation;
final int newDensity = getDisplayInfo().logicalDensityDpi;
@@ -413,20 +434,6 @@
return false;
}
- if (StackId.tasksAreFloating(mStackId)) {
- // Update stack bounds again since the display info has changed since updateDisplayInfo,
- // however, for floating tasks, we don't need to apply the new rotation to the bounds,
- // we can just update and return them here
- setBounds(mBounds);
- mBoundsAfterRotation.set(mBounds);
-
- // Once we've set the bounds based on the rotation of the old bounds in the new
- // orientation, clear the animation target bounds since they are obsolete
- mBoundsAnimationTarget.setEmpty();
- mBoundsAnimationSourceBounds.setEmpty();
- return true;
- }
-
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
switch (mStackId) {
@@ -692,6 +699,14 @@
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
+ } else if (mStackId == PINNED_STACK_ID) {
+ // Update the bounds based on any changes to the display info
+ getAnimationOrCurrentBounds(mTmpRect2);
+ boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3);
+ if (updated) {
+ bounds = new Rect(mTmpRect3);
+ }
}
updateDisplayInfo(bounds);
@@ -1443,21 +1458,11 @@
}
}
- @Override // AnimatesBounds
- public boolean setSize(Rect bounds) {
- synchronized (mService.mWindowMap) {
- if (mDisplayContent == null) {
- return false;
- }
- }
- try {
- mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1);
- } catch (RemoteException e) {
- }
- return true;
- }
-
public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
+ if (mCancelCurrentBoundsAnimation) {
+ return false;
+ }
+
try {
mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
} catch (RemoteException e) {
@@ -1469,8 +1474,10 @@
@Override // AnimatesBounds
public void onAnimationStart(boolean toFullscreen) {
synchronized (mService.mWindowMap) {
+ mBoundsAnimatingRequested = false;
mBoundsAnimating = true;
mBoundsAnimatingToFullscreen = toFullscreen;
+ mCancelCurrentBoundsAnimation = false;
}
if (mStackId == PINNED_STACK_ID) {
@@ -1484,7 +1491,8 @@
@Override // AnimatesBounds
public void updatePictureInPictureMode(Rect targetStackBounds) {
- final StackWindowController controller = getController();
+ final PinnedStackWindowController controller =
+ (PinnedStackWindowController) getController();
if (controller != null) {
controller.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
}
@@ -1523,14 +1531,21 @@
return mBoundsAnimating;
}
- public boolean getBoundsAnimating() {
+ public boolean isAnimatingBounds() {
return mBoundsAnimating;
}
- public boolean isBoundsAnimatingToFullscreen() {
+ public boolean isAnimatingBoundsToFullscreen() {
return mBoundsAnimating && mBoundsAnimatingToFullscreen;
}
+ public boolean pinnedStackResizeAllowed() {
+ if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
+ return true;
+ }
+ return false;
+ }
+
/** Returns true if a removal action is still being deferred. */
boolean checkCompleteDeferredRemoval() {
if (isAnimating()) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0f4707e..0049585 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -31,7 +31,6 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.myPid;
-import static android.os.Process.myTid;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -148,7 +147,6 @@
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -2758,7 +2756,12 @@
mDockedStackCreateBounds = bounds;
}
- public Rect getPictureInPictureBounds(int displayId, float aspectRatio) {
+ /**
+ * @param useExistingStackBounds Apply {@param aspectRatio} to the existing target stack bounds
+ * if possible
+ */
+ public Rect getPictureInPictureBounds(int displayId, float aspectRatio,
+ boolean useExistingStackBounds) {
synchronized (mWindowMap) {
if (!mSupportsPictureInPicture) {
return null;
@@ -2773,11 +2776,11 @@
final PinnedStackController pinnedStackController =
displayContent.getPinnedStackController();
final TaskStack stack = displayContent.getStackById(PINNED_STACK_ID);
- if (stack != null) {
+ if (stack != null && useExistingStackBounds) {
// If the stack exists, then use its final bounds to calculate the new aspect ratio
// bounds.
stackBounds = new Rect();
- stack.getAnimatingBounds(stackBounds);
+ stack.getAnimationOrCurrentBounds(stackBounds);
} else {
// Otherwise, just calculate the aspect ratio bounds from the default bounds
stackBounds = pinnedStackController.getDefaultBounds();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 979af7e..0b96f3f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1090,7 +1090,7 @@
// notify the client of frame changes in this case. Not only is it a lot of churn, but
// the frame may not correspond to the surface size or the onscreen area at various
// phases in the animation, and the client will become sad and confused.
- if (task != null && task.mStack.getBoundsAnimating()) {
+ if (task != null && task.mStack.isAnimatingBounds()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ae17d08..fa35336 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1360,11 +1360,11 @@
int posX = mTmpSize.left;
int posY = mTmpSize.top;
task.mStack.getDimBounds(mTmpStackBounds);
- task.mStack.getAnimatingSourceBounds(mTmpSourceBounds);
+ task.mStack.getFinalAnimationSourceBounds(mTmpSourceBounds);
if (!mTmpSourceBounds.isEmpty()) {
// Get the final target stack bounds, if we are not animating, this is just the
// current stack bounds
- task.mStack.getAnimatingBounds(mTmpAnimatingBounds);
+ task.mStack.getFinalAnimationBounds(mTmpAnimatingBounds);
// Calculate the current progress and interpolate the difference between the target
// and source bounds
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 353199a..985e5ea 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -23,7 +23,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -37,7 +36,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.INetworkRecommendationProvider;
@@ -94,8 +92,6 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -138,11 +134,13 @@
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
- @Mock private IBinder mIBinder, mIBinder2;
+ @Mock private IBinder mScoreCacheIBinder, mScoreCacheIBinder2;
+ @Mock private IBinder mServiceConnectionIBinder;
@Mock private INetworkRecommendationProvider mRecommendationProvider;
@Mock private UnaryOperator<List<ScoredNetwork>> mCurrentNetworkFilter;
@Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter;
@Mock private WifiInfo mWifiInfo;
+ @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection;
@Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
private ContentResolver mContentResolver;
@@ -160,17 +158,22 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mNetworkScoreCache.asBinder()).thenReturn(mIBinder);
- when(mNetworkScoreCache2.asBinder()).thenReturn(mIBinder2);
+ when(mNetworkScoreCache.asBinder()).thenReturn(mScoreCacheIBinder);
+ when(mNetworkScoreCache2.asBinder()).thenReturn(mScoreCacheIBinder2);
+ when(mServiceConnectionIBinder.queryLocalInterface(anyString()))
+ .thenReturn(mRecommendationProvider);
mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
when(mWifiInfo.getSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.ssid);
when(mWifiInfo.getBSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.bssid);
+ when(mServiceConnection.getAppData()).thenReturn(NEW_SCORER);
+ when(mServiceConnection.getRecommendationProvider()).thenReturn(mRecommendationProvider);
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
mHandlerThread = new HandlerThread("NetworkScoreServiceTest");
mHandlerThread.start();
mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager,
- mHandlerThread.getLooper());
+ networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper());
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = "NetworkScoreServiceTest_SSID";
configuration.BSSID = "NetworkScoreServiceTest_BSSID";
@@ -209,12 +212,10 @@
mNetworkScoreService.onUserUnlocked(0);
- verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
- new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
- .setComponent(RECOMMENDATION_SERVICE_COMP)),
- any(ServiceConnection.class),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
- eq(UserHandle.SYSTEM));
+ verify(mNetworkScorerAppManager).updateState();
+ verify(mNetworkScorerAppManager).migrateNetworkScorerAppSettingIfNeeded();
+ verify(mServiceConnection).bind(mContext);
+
}
@Test
@@ -238,16 +239,16 @@
@Test
public void testRequestScores_providerThrowsRemoteException() throws Exception {
- injectProvider();
doThrow(new RemoteException()).when(mRecommendationProvider)
.requestScores(any(NetworkKey[].class));
+ mNetworkScoreService.onUserUnlocked(0);
assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
}
@Test
public void testRequestScores_providerAvailable() throws Exception {
- injectProvider();
+ mNetworkScoreService.onUserUnlocked(0);
final NetworkKey[] networks = new NetworkKey[0];
assertTrue(mNetworkScoreService.requestScores(networks));
@@ -291,11 +292,11 @@
@Test
public void testRequestRecommendation_providerThrowsRemoteException() throws Exception {
- injectProvider();
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
doThrow(new RemoteException()).when(mRecommendationProvider)
.requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
anyInt());
+ mNetworkScoreService.onUserUnlocked(0);
final RecommendationResult result =
mNetworkScoreService.requestRecommendation(mRecommendationRequest);
@@ -306,7 +307,6 @@
@Test
public void testRequestRecommendation_resultReturned() throws Exception {
- injectProvider();
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
final WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = "testRequestRecommendation_resultReturned_SSID";
@@ -322,6 +322,7 @@
}).when(mRecommendationProvider)
.requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
anyInt());
+ mNetworkScoreService.onUserUnlocked(0);
final RecommendationResult result =
mNetworkScoreService.requestRecommendation(mRecommendationRequest);
@@ -357,7 +358,7 @@
@Test
public void testRequestRecommendationAsync_requestTimesOut() throws Exception {
- injectProvider();
+ mNetworkScoreService.onUserUnlocked(0);
Settings.Global.putLong(mContentResolver,
Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, 1L);
mNetworkScoreService.refreshRecommendationRequestTimeoutMs();
@@ -378,7 +379,7 @@
@Test
public void testRequestRecommendationAsync_requestSucceeds() throws Exception {
- injectProvider();
+ mNetworkScoreService.onUserUnlocked(0);
final Bundle bundle = new Bundle();
doAnswer(invocation -> {
invocation.<IRemoteCallback>getArgument(1).sendResult(bundle);
@@ -397,7 +398,7 @@
@Test
public void testRequestRecommendationAsync_requestThrowsRemoteException() throws Exception {
- injectProvider();
+ mNetworkScoreService.onUserUnlocked(0);
doThrow(new RemoteException()).when(mRecommendationProvider)
.requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
anyInt());
@@ -657,9 +658,13 @@
@Test
public void testIsCallerActiveScorer_noBoundService() throws Exception {
+ // No active scorer.
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null);
mNetworkScoreService.onUserUnlocked(0);
- assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid()));
+ mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid());
+
+ verify(mServiceConnection, never()).getAppData();
}
@Test
@@ -678,9 +683,13 @@
@Test
public void testGetActiveScorerPackage_notActive() throws Exception {
+ // No active scorer.
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null);
mNetworkScoreService.onUserUnlocked(0);
- assertNull(mNetworkScoreService.getActiveScorerPackage());
+ mNetworkScoreService.getActiveScorerPackage();
+
+ verify(mServiceConnection, never()).getPackageName();
}
@Test
@@ -688,10 +697,9 @@
when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
mNetworkScoreService.onUserUnlocked(0);
- assertEquals(NEW_SCORER.getRecommendationServicePackageName(),
- mNetworkScoreService.getActiveScorerPackage());
- assertEquals(NEW_SCORER.getEnableUseOpenWifiActivity(),
- mNetworkScoreService.getActiveScorer().getEnableUseOpenWifiActivity());
+ mNetworkScoreService.getActiveScorerPackage();
+
+ verify(mServiceConnection).getPackageName();
}
@Test
@@ -931,7 +939,12 @@
public void testGetActiveScorer_notConnected_canRequestScores() throws Exception {
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- assertNull(mNetworkScoreService.getActiveScorer());
+ when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(null);
+ mNetworkScoreService.onUserUnlocked(0);
+
+ mNetworkScoreService.getActiveScorer();
+
+ verify(mServiceConnection, never()).getAppData();
}
@Test
@@ -951,11 +964,11 @@
throws Exception {
when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- NetworkScorerAppData expectedAppData = new NetworkScorerAppData(Binder.getCallingUid(),
- RECOMMENDATION_SERVICE_COMP, RECOMMENDATION_SERVICE_LABEL,
- USE_WIFI_ENABLE_ACTIVITY_COMP, NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID);
- bindToScorer(expectedAppData);
- assertEquals(expectedAppData, mNetworkScoreService.getActiveScorer());
+ mNetworkScoreService.onUserUnlocked(0);
+
+ mNetworkScoreService.getActiveScorer();
+
+ verify(mServiceConnection).getAppData();
}
@Test
@@ -972,23 +985,98 @@
}
}
- // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
- private void injectProvider() {
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
- when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
- isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- IBinder mockBinder = mock(IBinder.class);
- when(mockBinder.queryLocalInterface(anyString()))
- .thenReturn(mRecommendationProvider);
- invocation.<ServiceConnection>getArgument(1)
- .onServiceConnected(NEW_SCORER.getRecommendationServiceComponent(),
- mockBinder);
- return true;
- }
- });
- mNetworkScoreService.onUserUnlocked(0);
+ @Test
+ public void testServiceConnection_bind_notBound() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+ connection.bind(mContext);
+
+ verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(RECOMMENDATION_SERVICE_COMP)),
+ eq(connection),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM));
+ }
+
+ @Test
+ public void testServiceConnection_bind_alreadyBound() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+
+ when(mContext.bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(RECOMMENDATION_SERVICE_COMP)),
+ eq(connection),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM))).thenReturn(true /*bound*/);
+
+ // Calling bind more than once should only result in 1 bindService call.
+ connection.bind(mContext);
+ connection.bind(mContext);
+
+ verify(mContext, times(1)).bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(RECOMMENDATION_SERVICE_COMP)),
+ eq(connection),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM));
+ }
+
+ @Test
+ public void testServiceConnection_bindFails() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+
+ when(mContext.bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(RECOMMENDATION_SERVICE_COMP)),
+ eq(connection),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM))).thenReturn(false /*bound*/);
+
+ connection.bind(mContext);
+
+ verify(mContext).unbindService(connection);
+ }
+
+ @Test
+ public void testServiceConnection_unbind_notBound() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+
+ connection.unbind(mContext);
+
+ verify(mContext, never()).unbindService(connection);
+ }
+
+ @Test
+ public void testServiceConnection_unbind_alreadyBound() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+
+ when(mContext.bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(RECOMMENDATION_SERVICE_COMP)),
+ eq(connection),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(UserHandle.SYSTEM))).thenReturn(true /*bound*/);
+
+ connection.bind(mContext);
+ connection.unbind(mContext);
+
+ verify(mContext).unbindService(connection);
+ }
+
+ @Test
+ public void testServiceConnection_dump_doesNotCrash() throws Exception {
+ NetworkScoreService.ScoringServiceConnection connection = new NetworkScoreService
+ .ScoringServiceConnection(NEW_SCORER);
+ final StringWriter stringWriter = new StringWriter();
+
+ connection.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+
+ assertFalse(stringWriter.toString().isEmpty());
}
private void bindToScorer(boolean callerIsScorer) {
@@ -996,13 +1084,7 @@
NetworkScorerAppData appData = new NetworkScorerAppData(callingUid,
RECOMMENDATION_SERVICE_COMP, RECOMMENDATION_SERVICE_LABEL,
USE_WIFI_ENABLE_ACTIVITY_COMP, NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID);
- bindToScorer(appData);
- }
-
- private void bindToScorer(NetworkScorerAppData appData) {
- when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(appData);
- when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
- isA(UserHandle.class))).thenReturn(true);
+ when(mServiceConnection.getAppData()).thenReturn(appData);
mNetworkScoreService.onUserUnlocked(0);
}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 00f6273..20839c5 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -117,6 +117,7 @@
@Captor private ArgumentCaptor<Bundle> mBundleCaptor;
private int mVisibleAccountsChangedBroadcasts;
private int mLoginAccountsChangedBroadcasts;
+ private int mAccountRemovedBroadcasts;
private static final int LATCH_TIMEOUT_MS = 500;
private static final String PREN_DB = "pren.db";
@@ -2510,6 +2511,7 @@
updateBroadcastCounters(2);
assertEquals(mVisibleAccountsChangedBroadcasts, 0); // broadcast was not sent
assertEquals(mLoginAccountsChangedBroadcasts, 2);
+ assertEquals(mAccountRemovedBroadcasts, 0);
}
@SmallTest
@@ -2533,9 +2535,10 @@
mAms.registerAccountListener( null /* accountTypes */, "testpackage");
mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE);
- updateBroadcastCounters(6);
+ updateBroadcastCounters(8);
assertEquals(mVisibleAccountsChangedBroadcasts, 2);
assertEquals(mLoginAccountsChangedBroadcasts, 4);
+ assertEquals(mAccountRemovedBroadcasts, 2);
}
@SmallTest
@@ -2560,17 +2563,19 @@
"testpackage3"); // opPackageName
// Remove account with 2 active listeners.
mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS);
- updateBroadcastCounters(7);
+ updateBroadcastCounters(8);
assertEquals(mVisibleAccountsChangedBroadcasts, 5);
assertEquals(mLoginAccountsChangedBroadcasts, 2); // 3 add, 2 remove
+ assertEquals(mAccountRemovedBroadcasts, 1);
// Add account of another type.
mAms.addAccountExplicitly(
AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_TYPE_2, "p11", null);
- updateBroadcastCounters(8);
+ updateBroadcastCounters(9);
assertEquals(mVisibleAccountsChangedBroadcasts, 5);
assertEquals(mLoginAccountsChangedBroadcasts, 3);
+ assertEquals(mAccountRemovedBroadcasts, 1);
}
@SmallTest
@@ -2602,16 +2607,20 @@
private void updateBroadcastCounters (int expectedBroadcasts){
mVisibleAccountsChangedBroadcasts = 0;
mLoginAccountsChangedBroadcasts = 0;
+ mAccountRemovedBroadcasts = 0;
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(),
any(UserHandle.class));
for (Intent intent : captor.getAllValues()) {
- if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED. equals(intent.getAction())) {
+ if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED.equals(intent.getAction())) {
mVisibleAccountsChangedBroadcasts++;
}
- if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION. equals(intent.getAction())) {
+ if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) {
mLoginAccountsChangedBroadcasts++;
}
+ if (AccountManager.ACTION_ACCOUNT_REMOVED.equals(intent.getAction())) {
+ mAccountRemovedBroadcasts++;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
new file mode 100644
index 0000000..1d80b33
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.ActivityStackTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStackTests extends ActivityTestsBase {
+ private final ComponentName testActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity");
+
+ @Test
+ public void testEmptyTaskCleanupOnRemove() throws Exception {
+ final ActivityManagerService service = createActivityManagerService();
+ final TestActivityStack testStack = new ActivityStackBuilder(service).build();
+ final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ assertNotNull(task.getWindowContainerController());
+ testStack.removeTask(task, "testEmptyTaskCleanupOnRemove",
+ ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+ assertNull(task.getWindowContainerController());
+ }
+ @Test
+ public void testOccupiedTaskCleanupOnRemove() throws Exception {
+ final ActivityManagerService service = createActivityManagerService();
+ final TestActivityStack testStack = new ActivityStackBuilder(service).build();
+ final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
+ assertNotNull(task.getWindowContainerController());
+ testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove",
+ ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+ assertNotNull(task.getWindowContainerController());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 5240586..3bf0e5f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -18,6 +18,7 @@
import static org.mockito.Mockito.mock;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -62,14 +63,16 @@
}
protected ActivityManagerService createActivityManagerService() {
- return new TestActivityManagerService(mContext);
+ final ActivityManagerService service = new TestActivityManagerService(mContext);
+ service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext);
+ return service;
}
protected static TestActivityStack createActivityStack(ActivityManagerService service,
int stackId, int displayId, boolean onTop) {
if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor)
- .createTestStack(stackId, onTop);
+ .createTestStack(service, stackId, onTop);
return stack;
}
@@ -109,7 +112,7 @@
intent.setComponent(component);
final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
- null /*_taskDescription*/, null /*thumbnailInfo*/);
+ null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
stack.addTask(task, true, "creating test task");
task.setStack(stack);
task.createWindowContainer(true, true);
@@ -146,25 +149,39 @@
void updateUIDsPresentOnDisplay() {
}
- public TestActivityStack createTestStack(int stackId, boolean onTop) {
+ public TestActivityStack createTestStack(ActivityManagerService service, int stackId,
+ boolean onTop) {
final ActivityDisplay display = new ActivityDisplay();
final TestActivityContainer container =
- new TestActivityContainer(stackId, display, onTop);
+ new TestActivityContainer(service, stackId, display, onTop);
return container.getStack();
}
private class TestActivityContainer extends ActivityContainer {
+ private ActivityManagerService mService;
private TestActivityStack mStack;
- TestActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) {
+ private boolean mOnTop;
+
+ TestActivityContainer(ActivityManagerService service, int stackId,
+ ActivityDisplay activityDisplay, boolean onTop) {
super(stackId, activityDisplay, onTop);
+ mService = service;
}
@Override
protected void createStack(int stackId, boolean onTop) {
- mStack = new TestActivityStack(this, null /*recentTasks*/, onTop);
+ // normally stack creation is done here. However we need to do it on demand since
+ // we cannot set {@link mService} by the time the super constructor calling this
+ // method is invoked.
+ mOnTop = onTop;
}
public TestActivityStack getStack() {
+ if (mStack == null) {
+ mStack = new TestActivityStack(this,
+ new RecentTasks(mService, mService.mStackSupervisor), mOnTop);
+ }
+
return mStack;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
new file mode 100644
index 0000000..85dc712
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManagerInternal.AppTransitionListener;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
+
+/**
+ * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
+ * depending on the various interactions.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BoundsAnimationControllerTests extends WindowTestsBase {
+
+ /**
+ * Mock value animator to simulate updates with.
+ */
+ private class MockValueAnimator extends ValueAnimator {
+
+ private float mFraction;
+
+ public MockValueAnimator getWithValue(float fraction) {
+ mFraction = fraction;
+ return this;
+ }
+
+ @Override
+ public Object getAnimatedValue() {
+ return mFraction;
+ }
+ }
+
+ /**
+ * Mock app transition to fire notifications to the bounds animator.
+ */
+ private class MockAppTransition extends AppTransition {
+
+ private AppTransitionListener mListener;
+
+ MockAppTransition(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ void registerListenerLocked(AppTransitionListener listener) {
+ mListener = listener;
+ }
+
+ public void notifyTransitionPending() {
+ mListener.onAppTransitionPendingLocked();
+ }
+
+ public void notifyTransitionCancelled(int transit) {
+ mListener.onAppTransitionCancelledLocked(transit);
+ }
+
+ public void notifyTransitionStarting(int transit) {
+ mListener.onAppTransitionStartingLocked(transit, null, null, null, null);
+ }
+
+ public void notifyTransitionFinished() {
+ mListener.onAppTransitionFinishedLocked(null);
+ }
+ }
+
+ /**
+ * A test animate bounds user to track callbacks from the bounds animation.
+ */
+ private class TestAnimateBoundsUser implements BoundsAnimationController.AnimateBoundsUser {
+
+ boolean mMovedToFullscreen;
+ boolean mAnimationStarted;
+ boolean mAnimationStartedToFullscreen;
+ boolean mAnimationEnded;
+ boolean mUpdatedPictureInPictureModeWithBounds;
+ boolean mBoundsUpdated;
+ Rect mStackBounds;
+ Rect mTaskBounds;
+
+ boolean mRequestCancelAnimation = false;
+
+ void reinitialize(Rect stackBounds, Rect taskBounds) {
+ mMovedToFullscreen = false;
+ mAnimationStarted = false;
+ mAnimationStartedToFullscreen = false;
+ mAnimationEnded = false;
+ mUpdatedPictureInPictureModeWithBounds = false;
+ mStackBounds = stackBounds;
+ mTaskBounds = taskBounds;
+ mBoundsUpdated = false;
+ mRequestCancelAnimation = false;
+ }
+
+ @Override
+ public void onAnimationStart(boolean toFullscreen) {
+ mAnimationStarted = true;
+ mAnimationStartedToFullscreen = toFullscreen;
+ }
+
+ @Override
+ public void updatePictureInPictureMode(Rect targetStackBounds) {
+ mUpdatedPictureInPictureModeWithBounds = true;
+ }
+
+ @Override
+ public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
+ // TODO: Once we break the runs apart, we should fail() here if this is called outside
+ // of onAnimationStart() and onAnimationEnd()
+ if (mRequestCancelAnimation) {
+ return false;
+ } else {
+ mBoundsUpdated = true;
+ mStackBounds = stackBounds;
+ mTaskBounds = taskBounds;
+ return true;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd() {
+ mAnimationEnded = true;
+ }
+
+ @Override
+ public void moveToFullscreen() {
+ mMovedToFullscreen = true;
+ }
+ }
+
+ // Constants
+ private static final boolean MOVE_TO_FULLSCREEN = true;
+
+ // Some dummy bounds to represent fullscreen and floating bounds
+ private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
+ private static final Rect BOUNDS_FLOATING = new Rect(80, 80, 95, 95);
+ private static final Rect BOUNDS_ALT_FLOATING = new Rect(60, 60, 95, 95);
+
+ // Some dummy duration
+ private static final int DURATION = 100;
+
+ // Common
+ private MockAppTransition mAppTransition;
+ private MockValueAnimator mAnimator;
+ private TestAnimateBoundsUser mTarget;
+ private BoundsAnimationController mController;
+
+ // Temp
+ private Rect mTmpRect = new Rect();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Handler handler = new Handler(Looper.getMainLooper());
+ mAppTransition = new MockAppTransition(context);
+ mAnimator = new MockValueAnimator();
+ mTarget = new TestAnimateBoundsUser();
+ mController = new BoundsAnimationController(context, mAppTransition, handler);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFullscreenToFloatingTransition() throws Exception {
+ // Create and start the animation
+ mTarget.reinitialize(BOUNDS_FULL, null);
+ final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+
+ // Assert that when we are started, and that we are not going to fullscreen
+ assertTrue(mTarget.mAnimationStarted);
+ assertFalse(mTarget.mAnimationStartedToFullscreen);
+ // Ensure we are not triggering a PiP mode change
+ assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ // Ensure that the task stack bounds are already frozen to the larger source stack bounds
+ assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+
+ // Drive some animation updates, ensure that only the stack bounds change and the task
+ // bounds are frozen to the original stack bounds (adjusted for the offset)
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+ assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
+ assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+
+ // Finish the animation, ensure that it reaches the final bounds with the given state
+ boundsAnimator.end();
+ assertTrue(mTarget.mAnimationEnded);
+ assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
+ assertNull(mTarget.mTaskBounds);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToFullscreenTransition() throws Exception {
+ // Create and start the animation
+ mTarget.reinitialize(BOUNDS_FULL, null);
+ final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FLOATING,
+ BOUNDS_FULL, DURATION, MOVE_TO_FULLSCREEN);
+
+ // Assert that when we are started, and that we are going to fullscreen
+ assertTrue(mTarget.mAnimationStarted);
+ assertTrue(mTarget.mAnimationStartedToFullscreen);
+ // Ensure that we update the PiP mode change with the new fullscreen bounds
+ assertTrue(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ // Ensure that the task stack bounds are already frozen to the larger target stack bounds
+ assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+
+ // Drive some animation updates, ensure that only the stack bounds change and the task
+ // bounds are frozen to the original stack bounds (adjusted for the offset)
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+ assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
+ assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
+ assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
+
+ // Finish the animation, ensure that it reaches the final bounds with the given state
+ boundsAnimator.end();
+ assertTrue(mTarget.mAnimationEnded);
+ assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
+ assertNull(mTarget.mTaskBounds);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInterruptAnimationFromUser() throws Exception {
+ // Create and start the animation
+ mTarget.reinitialize(BOUNDS_FULL, null);
+ final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+
+ // Cancel the animation on the next update from the user
+ mTarget.mRequestCancelAnimation = true;
+ mTarget.mBoundsUpdated = false;
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+ // Ensure that we got no more updates after returning false and the bounds are not updated
+ // to the end value
+ assertFalse(mTarget.mBoundsUpdated);
+ assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
+ assertNotEquals(BOUNDS_FLOATING, mTarget.mTaskBounds);
+ // Ensure that we received the animation end call
+ assertTrue(mTarget.mAnimationEnded);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testCancelAnimationFromNewAnimationToExistingBounds() throws Exception {
+ // Create and start the animation
+ mTarget.reinitialize(BOUNDS_FULL, null);
+ final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+
+ // Drive some animation updates
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+
+ // Cancel the animation as a restart to the same bounds
+ mTarget.reinitialize(null, null);
+ final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+ // Ensure the animator is the same
+ assertSame(boundsAnimator, altBoundsAnimator);
+ // Ensure we haven't restarted or finished the animation
+ assertFalse(mTarget.mAnimationStarted);
+ assertFalse(mTarget.mAnimationEnded);
+ // Ensure that we haven't tried to update the PiP mode
+ assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testCancelAnimationFromNewAnimationToNewBounds() throws Exception {
+ // Create and start the animation
+ mTarget.reinitialize(BOUNDS_FULL, null);
+ final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+
+ // Drive some animation updates
+ boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+
+ // Cancel the animation as a restart to new bounds
+ mTarget.reinitialize(null, null);
+ final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
+ BOUNDS_ALT_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+ // Ensure the animator is not the same
+ assertNotSame(boundsAnimator, altBoundsAnimator);
+ // Ensure that we did not get an animation start/end callback
+ assertFalse(mTarget.mAnimationStarted);
+ assertFalse(mTarget.mAnimationEnded);
+ // Ensure that we haven't tried to update the PiP mode
+ assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ }
+
+ /**
+ * @return the bounds offset to zero/zero.
+ */
+ private Rect offsetToZero(Rect bounds) {
+ mTmpRect.set(bounds);
+ mTmpRect.offsetTo(0, 0);
+ return mTmpRect;
+ }
+}
diff --git a/services/usb/Android.mk b/services/usb/Android.mk
index f560e71..f6d212b 100644
--- a/services/usb/Android.mk
+++ b/services/usb/Android.mk
@@ -8,7 +8,7 @@
$(call all-java-files-under,java)
LOCAL_JAVA_LIBRARIES := services.core
-LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb@1.0-java-static \
-android.hidl.manager@1.0-java-static
+LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb-V1.0-java-static \
+android.hidl.manager-V1.0-java-static
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f9b754b..397aa00 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2710,19 +2710,12 @@
* @param phoneAccountHandle the phone account to change the client state
* @param enabled the new state of the client
* @hide
+ * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+ * be implemented instead.
*/
@SystemApi
public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.setVisualVoicemailEnabled(mContext.getOpPackageName(), phoneAccountHandle,
- enabled);
- }
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- }
+
}
/**
@@ -2734,19 +2727,11 @@
* @param phoneAccountHandle the phone account to check for.
* @return {@code true} when the visual voicemail client is enabled for this client
* @hide
+ * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+ * be implemented instead.
*/
@SystemApi
public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.isVisualVoicemailEnabled(
- mContext.getOpPackageName(), phoneAccountHandle);
- }
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- }
return false;
}
@@ -6478,5 +6463,33 @@
return null;
}
+
+ /**
+ * Check if phone is in emergency callback mode
+ * @return true if phone is in emergency callback mode
+ * @hide
+ */
+ public boolean getEmergencyCallbackMode() {
+ return getEmergencyCallbackMode(getSubId());
+ }
+
+ /**
+ * Check if phone is in emergency callback mode
+ * @return true if phone is in emergency callback mode
+ * @param subId the subscription ID that this action applies to.
+ * @hide
+ */
+ public boolean getEmergencyCallbackMode(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return false;
+ }
+ return telephony.getEmergencyCallbackMode(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getEmergencyCallbackMode", e);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 13a25ca5..dd08f67 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -498,12 +498,6 @@
*/
boolean isConcurrentVoiceAndDataAllowed(int subId);
- oneway void setVisualVoicemailEnabled(String callingPackage,
- in PhoneAccountHandle accountHandle, boolean enabled);
-
- boolean isVisualVoicemailEnabled(String callingPackage,
- in PhoneAccountHandle accountHandle);
-
String getVisualVoicemailPackageName(String callingPackage, int subId);
// Not oneway, caller needs to make sure the vaule is set before receiving a SMS
@@ -1316,4 +1310,12 @@
* @param appType the icc application type, like {@link #APPTYPE_USIM}
*/
String[] getForbiddenPlmns(int subId, int appType);
+
+ /**
+ * Check if phone is in emergency callback mode
+ * @return true if phone is in emergency callback mode
+ * @param subId the subscription ID that this action applies to.
+ * @hide
+ */
+ boolean getEmergencyCallbackMode(int subId);
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index d819b96..d11565a 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -45,7 +45,9 @@
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import java.util.Arrays;
+import java.util.List;
import junit.framework.TestCase;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
@@ -57,7 +59,7 @@
aType(IpReachabilityEvent.class),
anInt(IpReachabilityEvent.NUD_FAILED));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -70,13 +72,13 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.netId = 123;
ev.transports = 3; // transports have priority for inferrence of link layer
ev.ifname = "wlan0";
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -89,12 +91,12 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.transports = 1;
ev.ifname = null;
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -107,12 +109,12 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.transports = 0;
ev.ifname = "not_inferred";
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"not_inferred\"",
@@ -125,11 +127,11 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.ifname = "bt-pan";
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -142,11 +144,11 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.ifname = "rmnet_ipa0";
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -159,11 +161,11 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
ev.ifname = "wlan0";
- want = joinLines(
+ want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -176,7 +178,7 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -190,7 +192,7 @@
aBool(true),
aBool(false));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -211,7 +213,7 @@
" transport_types: 3",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -223,7 +225,7 @@
aString("SomeState"),
anInt(192));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -237,7 +239,7 @@
" state_transition: \"SomeState\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -248,7 +250,7 @@
aType(DhcpErrorEvent.class),
anInt(DhcpErrorEvent.L4_NOT_UDP));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -262,59 +264,7 @@
" error_code: 50397184",
" >",
">",
- "version: 2");
-
- verifySerialization(want, ev);
- }
-
- @SmallTest
- public void testDnsEventSerialization() {
- ConnectivityMetricsEvent ev = describeIpEvent(
- aType(DnsEvent.class),
- anInt(101),
- aByteArray(b(1), b(1), b(2), b(1), b(1), b(1), b(2), b(2)),
- aByteArray(b(0), b(0), b(22), b(3), b(1), b(0), b(200), b(178)),
- anIntArray(3456, 267, 1230, 45, 2111, 450, 638, 1300));
-
- String want = joinLines(
- "dropped_events: 0",
- "events <",
- " if_name: \"\"",
- " link_layer: 0",
- " network_id: 0",
- " time_ms: 1",
- " transports: 0",
- " dns_lookup_batch <",
- " event_types: 1",
- " event_types: 1",
- " event_types: 2",
- " event_types: 1",
- " event_types: 1",
- " event_types: 1",
- " event_types: 2",
- " event_types: 2",
- " latencies_ms: 3456",
- " latencies_ms: 267",
- " latencies_ms: 1230",
- " latencies_ms: 45",
- " latencies_ms: 2111",
- " latencies_ms: 450",
- " latencies_ms: 638",
- " latencies_ms: 1300",
- " network_id <",
- " network_id: 101",
- " >",
- " return_codes: 0",
- " return_codes: 0",
- " return_codes: 22",
- " return_codes: 3",
- " return_codes: 1",
- " return_codes: 0",
- " return_codes: 200",
- " return_codes: 178",
- " >",
- ">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -326,7 +276,7 @@
anInt(IpManagerEvent.PROVISIONING_OK),
aLong(5678));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -340,7 +290,7 @@
" latency_ms: 5678",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -351,7 +301,7 @@
aType(IpReachabilityEvent.class),
anInt(IpReachabilityEvent.NUD_FAILED));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -364,7 +314,7 @@
" if_name: \"\"",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -377,7 +327,7 @@
anInt(5),
aLong(20410));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -393,7 +343,7 @@
" >",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -406,7 +356,7 @@
anInt(ValidationProbeEvent.PROBE_HTTP),
anInt(204));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -420,7 +370,7 @@
" probe_type: 1",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -436,7 +386,7 @@
anInt(2048),
anInt(3));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -454,7 +404,7 @@
" program_length: 2048",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -474,7 +424,7 @@
anInt(3),
anInt(2048));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -495,7 +445,7 @@
" zero_lifetime_ras: 1",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
@@ -511,7 +461,7 @@
aLong(1000),
aLong(-1));
- String want = joinLines(
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -528,28 +478,20 @@
" router_lifetime: 2000",
" >",
">",
- "version: 2");
+ "version: 2\n");
verifySerialization(want, ev);
}
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
try {
- byte[] got = IpConnectivityEventBuilder.serialize(0,
- IpConnectivityEventBuilder.toProto(Arrays.asList(input)));
+ List<IpConnectivityEvent> proto =
+ IpConnectivityEventBuilder.toProto(Arrays.asList(input));
+ byte[] got = IpConnectivityEventBuilder.serialize(0, proto);
IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
fail(e.toString());
}
}
-
- static String joinLines(String ... elems) {
- StringBuilder b = new StringBuilder();
- for (String s : elems) {
- b.append(s);
- b.append("\n");
- }
- return b.toString();
- }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 68786d0..e01469b 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -16,12 +16,22 @@
package com.android.server.connectivity;
+import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
+import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.DefaultNetworkEvent;
@@ -31,7 +41,9 @@
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.system.OsConstants;
import android.os.Parcelable;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
@@ -41,26 +53,38 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import junit.framework.TestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class IpConnectivityMetricsTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConnectivityMetricsTest {
static final IpReachabilityEvent FAKE_EV =
new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
+ private static final String EXAMPLE_IPV4 = "192.0.2.1";
+ private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
+
@Mock Context mCtx;
@Mock IIpConnectivityMetrics mMockService;
+ @Mock ConnectivityManager mCm;
IpConnectivityMetrics mService;
+ NetdEventListenerService mNetdListener;
+ @Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
+ mNetdListener = new NetdEventListenerService(mCm);
+ mService.mNetdListener = mNetdListener;
}
- @SmallTest
+ @Test
public void testLoggingEvents() throws Exception {
IpConnectivityLog logger = new IpConnectivityLog(mMockService);
@@ -74,7 +98,7 @@
assertEventsEqual(expectedEvent(3), got.get(2));
}
- @SmallTest
+ @Test
public void testLoggingEventsWithMultipleCallers() throws Exception {
IpConnectivityLog logger = new IpConnectivityLog(mMockService);
@@ -91,7 +115,7 @@
}.start();
}
- List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 100);
+ List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
Collections.sort(got, EVENT_COMPARATOR);
Iterator<ConnectivityMetricsEvent> iter = got.iterator();
for (int i = 0; i < nCallers; i++) {
@@ -102,7 +126,7 @@
}
}
- @SmallTest
+ @Test
public void testBufferFlushing() {
String output1 = getdump("flush");
assertEquals("", output1);
@@ -115,7 +139,7 @@
assertEquals("", output3);
}
- @SmallTest
+ @Test
public void testRateLimiting() {
final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
final ApfProgramEvent ev = new ApfProgramEvent();
@@ -137,11 +161,19 @@
assertEquals("", output2);
}
- @SmallTest
- public void testEndToEndLogging() {
+ @Test
+ public void testEndToEndLogging() throws Exception {
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
+ NetworkCapabilities ncWifi = new NetworkCapabilities();
+ NetworkCapabilities ncCell = new NetworkCapabilities();
+ ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+ when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
+ when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
+
ApfStats apfStats = new ApfStats();
apfStats.durationMs = 45000;
apfStats.receivedRas = 10;
@@ -177,7 +209,22 @@
logger.log(ev);
}
- String want = joinLines(
+ // netId, errno, latency, destination
+ connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4);
+ connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6);
+ connectEvent(100, 0, 110, EXAMPLE_IPV4);
+ connectEvent(101, 0, 23, EXAMPLE_IPV4);
+ connectEvent(101, 0, 45, EXAMPLE_IPV6);
+ connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4);
+
+ // netId, type, return code, latency
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
+ dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
+ dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
+
+ String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
@@ -279,7 +326,71 @@
" router_lifetime: 2000",
" >",
">",
- "version: 2");
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 2",
+ " connect_statistics <",
+ " connect_blocking_count: 1",
+ " connect_count: 3",
+ " errnos_counters <",
+ " key: 11",
+ " value: 1",
+ " >",
+ " ipv6_addr_count: 1",
+ " latencies_ms: 110",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 1",
+ " connect_statistics <",
+ " connect_blocking_count: 2",
+ " connect_count: 2",
+ " ipv6_addr_count: 1",
+ " latencies_ms: 23",
+ " latencies_ms: 45",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 2",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " latencies_ms: 3456",
+ " latencies_ms: 45",
+ " latencies_ms: 638",
+ " return_codes: 0",
+ " return_codes: 3",
+ " return_codes: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 1",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 2",
+ " latencies_ms: 56",
+ " latencies_ms: 34",
+ " return_codes: 0",
+ " return_codes: 0",
+ " >",
+ ">",
+ "version: 2\n");
verifySerialization(want, getdump("flush"));
}
@@ -291,6 +402,14 @@
return buffer.toString();
}
+ void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception {
+ mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1);
+ }
+
+ void dnsEvent(int netId, int type, int result, int latency) throws Exception {
+ mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+ }
+
List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
ArgumentCaptor<ConnectivityMetricsEvent> captor =
ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 0ab4406..f98ab3d 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -16,190 +16,182 @@
package com.android.server.connectivity;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.metrics.ConnectStats;
-import android.net.metrics.DnsEvent;
-import android.net.metrics.INetdEventListener;
-import android.net.metrics.IpConnectivityLog;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.system.OsConstants;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-import junit.framework.TestCase;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
+import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
+import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-public class NetdEventListenerServiceTest extends TestCase {
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.OsConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
- // TODO: read from NetdEventListenerService after this constant is read from system property
- static final int BATCH_SIZE = 100;
- static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
- // TODO: read from INetdEventListener
- static final int RETURN_CODE = 1;
-
- static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
- static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
- static final int[] LATENCIES = new int[BATCH_SIZE];
- static {
- for (int i = 0; i < BATCH_SIZE; i++) {
- EVENT_TYPES[i] = EVENT_TYPE;
- RETURN_CODES[i] = RETURN_CODE;
- LATENCIES[i] = i;
- }
- }
-
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetdEventListenerServiceTest {
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
NetdEventListenerService mNetdEventListenerService;
+ ConnectivityManager mCm;
- @Mock ConnectivityManager mCm;
- @Mock IpConnectivityLog mLog;
- ArgumentCaptor<NetworkCallback> mCallbackCaptor;
- ArgumentCaptor<DnsEvent> mDnsEvCaptor;
-
+ @Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
- mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
- mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
- mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
+ NetworkCapabilities ncWifi = new NetworkCapabilities();
+ NetworkCapabilities ncCell = new NetworkCapabilities();
+ ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
+ mCm = mock(ConnectivityManager.class);
+ when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
+ when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
+
+ mNetdEventListenerService = new NetdEventListenerService(mCm);
}
- @SmallTest
- public void testOneDnsBatch() throws Exception {
- log(105, LATENCIES);
- log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
+ @Test
+ public void testDnsLogging() throws Exception {
+ asyncDump(100);
- verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 3456);
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 267);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230);
+ dnsEvent(100, EVENT_GETADDRINFO, 3, 45);
+ dnsEvent(100, EVENT_GETADDRINFO, 1, 2111);
+ dnsEvent(100, EVENT_GETADDRINFO, 0, 450);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638);
+ dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
+ dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 78);
+ dnsEvent(101, EVENT_GETADDRINFO, 0, 14);
- log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
-
- mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
- verifyLoggedDnsEvents(
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
+ String got = flushStatistics();
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 2",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 2",
+ " latencies_ms: 3456",
+ " latencies_ms: 267",
+ " latencies_ms: 1230",
+ " latencies_ms: 45",
+ " latencies_ms: 2111",
+ " latencies_ms: 450",
+ " latencies_ms: 638",
+ " latencies_ms: 1300",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 22",
+ " return_codes: 3",
+ " return_codes: 1",
+ " return_codes: 0",
+ " return_codes: 200",
+ " return_codes: 178",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 1",
+ " dns_lookup_batch <",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 1",
+ " event_types: 2",
+ " event_types: 1",
+ " event_types: 1",
+ " latencies_ms: 56",
+ " latencies_ms: 78",
+ " latencies_ms: 14",
+ " latencies_ms: 56",
+ " latencies_ms: 78",
+ " latencies_ms: 14",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " return_codes: 0",
+ " >",
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
}
- @SmallTest
- public void testSeveralDmsBatches() throws Exception {
- log(105, LATENCIES);
- log(106, LATENCIES);
- log(105, LATENCIES);
- log(107, LATENCIES);
-
- verifyLoggedDnsEvents(
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testDnsBatchAndNetworkLost() throws Exception {
- byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
- byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
- int[] latencies = Arrays.copyOf(LATENCIES, 20);
-
- log(105, LATENCIES);
- log(105, latencies);
- mCallbackCaptor.getValue().onLost(new Network(105));
- log(105, LATENCIES);
-
- verifyLoggedDnsEvents(
- new DnsEvent(105, eventTypes, returnCodes, latencies),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testConcurrentDnsBatchesAndDumps() throws Exception {
- final long stop = System.currentTimeMillis() + 100;
- final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
- new Thread() {
- public void run() {
- while (System.currentTimeMillis() < stop) {
- mNetdEventListenerService.dump(pw);
- }
- }
- }.start();
-
- logDnsAsync(105, LATENCIES);
- logDnsAsync(106, LATENCIES);
- logDnsAsync(107, LATENCIES);
-
- verifyLoggedDnsEvents(500,
- new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
- new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
- }
-
- @SmallTest
- public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
- logDnsAsync(105, LATENCIES);
- Thread.sleep(10L);
- // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
- mCallbackCaptor.getValue().onLost(new Network(105));
-
- // do not verify batch with unpredictable length
- verify(mLog, timeout(500).times(1)).log(any(Parcelable.class));
- }
-
- @SmallTest
+ @Test
public void testConnectLogging() throws Exception {
+ asyncDump(100);
+
final int OK = 0;
Thread[] logActions = {
// ignored
- connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6),
- connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
- connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
+ connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4),
+ connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6),
+ connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
+ connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
+ connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
// valid latencies
- connectEventAction(OK, 110, EXAMPLE_IPV4),
- connectEventAction(OK, 23, EXAMPLE_IPV4),
- connectEventAction(OK, 45, EXAMPLE_IPV4),
- connectEventAction(OK, 56, EXAMPLE_IPV4),
- connectEventAction(OK, 523, EXAMPLE_IPV6),
- connectEventAction(OK, 214, EXAMPLE_IPV6),
- connectEventAction(OK, 67, EXAMPLE_IPV6),
+ connectEventAction(100, OK, 110, EXAMPLE_IPV4),
+ connectEventAction(100, OK, 23, EXAMPLE_IPV4),
+ connectEventAction(100, OK, 45, EXAMPLE_IPV4),
+ connectEventAction(101, OK, 56, EXAMPLE_IPV4),
+ connectEventAction(101, OK, 523, EXAMPLE_IPV6),
+ connectEventAction(101, OK, 214, EXAMPLE_IPV6),
+ connectEventAction(101, OK, 67, EXAMPLE_IPV6),
// errors
- connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6),
- connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
- connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
- connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
- connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
+ connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4),
+ connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4),
+ connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
+ connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4),
+ connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4),
+ connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6),
+ connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
+ connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
+ connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
+ connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
+ connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
};
for (Thread t : logActions) {
@@ -209,113 +201,124 @@
t.join();
}
- List<IpConnectivityEvent> events = new ArrayList<>();
- mNetdEventListenerService.flushStatistics(events);
-
- IpConnectivityEvent got = events.get(0);
+ String got = flushStatistics();
String want = String.join("\n",
- "if_name: \"\"",
- "link_layer: 0",
- "network_id: 0",
- "time_ms: 0",
- "transports: 0",
- "connect_statistics <",
- " connect_blocking_count: 7",
- " connect_count: 12",
- " errnos_counters <",
- " key: 1",
- " value: 2",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 2",
+ " connect_statistics <",
+ " connect_blocking_count: 3",
+ " connect_count: 6",
+ " errnos_counters <",
+ " key: 1",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 11",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 13",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 98",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 110",
+ " value: 2",
+ " >",
+ " ipv6_addr_count: 1",
+ " latencies_ms: 23",
+ " latencies_ms: 45",
+ " latencies_ms: 110",
" >",
- " errnos_counters <",
- " key: 11",
- " value: 1",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 1",
+ " connect_statistics <",
+ " connect_blocking_count: 4",
+ " connect_count: 6",
+ " errnos_counters <",
+ " key: 1",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 13",
+ " value: 2",
+ " >",
+ " errnos_counters <",
+ " key: 110",
+ " value: 1",
+ " >",
+ " errnos_counters <",
+ " key: 111",
+ " value: 1",
+ " >",
+ " ipv6_addr_count: 5",
+ " latencies_ms: 56",
+ " latencies_ms: 67",
+ " latencies_ms: 214",
+ " latencies_ms: 523",
" >",
- " errnos_counters <",
- " key: 13",
- " value: 3",
- " >",
- " errnos_counters <",
- " key: 98",
- " value: 1",
- " >",
- " errnos_counters <",
- " key: 110",
- " value: 3",
- " >",
- " errnos_counters <",
- " key: 111",
- " value: 1",
- " >",
- " ipv6_addr_count: 6",
- " latencies_ms: 23",
- " latencies_ms: 45",
- " latencies_ms: 56",
- " latencies_ms: 67",
- " latencies_ms: 110",
- " latencies_ms: 214",
- " latencies_ms: 523",
- ">\n");
- verifyConnectEvent(want, got);
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
}
- Thread connectEventAction(int error, int latencyMs, String ipAddr) {
+ Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
return new Thread(() -> {
try {
- mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
+ mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
} catch (Exception e) {
fail(e.toString());
}
});
}
- void log(int netId, int[] latencies) {
- try {
- for (int l : latencies) {
- mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
- 0, 0);
+ void dnsEvent(int netId, int type, int result, int latency) throws Exception {
+ mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+ }
+
+ void asyncDump(long durationMs) throws Exception {
+ final long stop = System.currentTimeMillis() + durationMs;
+ final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
+ new Thread(() -> {
+ while (System.currentTimeMillis() < stop) {
+ mNetdEventListenerService.dump(pw);
}
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ }).start();
}
- void logDnsAsync(int netId, int[] latencies) {
- new Thread(() -> log(netId, latencies)).start();
- }
+ // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
+ String flushStatistics() throws Exception {
+ IpConnectivityMetrics metricsService =
+ new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
+ metricsService.mNetdListener = mNetdEventListenerService;
- void verifyLoggedDnsEvents(DnsEvent... expected) {
- verifyLoggedDnsEvents(0, expected);
- }
-
- void verifyLoggedDnsEvents(int wait, DnsEvent... expectedEvents) {
- verify(mLog, timeout(wait).times(expectedEvents.length)).log(mDnsEvCaptor.capture());
- for (DnsEvent got : mDnsEvCaptor.getAllValues()) {
- OptionalInt index = IntStream.range(0, expectedEvents.length)
- .filter(i -> dnsEventsEqual(expectedEvents[i], got))
- .findFirst();
- // Don't match same expected event more than once.
- index.ifPresent(i -> expectedEvents[i] = null);
- assertTrue(index.isPresent());
- }
- }
-
- /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
- static boolean dnsEventsEqual(DnsEvent expected, DnsEvent got) {
- return (expected == got) || ((expected != null) && (got != null)
- && (expected.netId == got.netId)
- && Arrays.equals(expected.eventTypes, got.eventTypes)
- && Arrays.equals(expected.returnCodes, got.returnCodes)
- && Arrays.equals(expected.latenciesMs, got.latenciesMs));
- }
-
- static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
- try {
- Arrays.sort(got.getConnectStatistics().latenciesMs);
- Arrays.sort(got.getConnectStatistics().errnosCounters,
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ metricsService.impl.dump(null, writer, new String[]{"flush"});
+ byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT);
+ IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes);
+ for (IpConnectivityEvent ev : log.events) {
+ if (ev.getConnectStatistics() == null) {
+ continue;
+ }
+ // Sort repeated fields of connect() events arriving in non-deterministic order.
+ Arrays.sort(ev.getConnectStatistics().latenciesMs);
+ Arrays.sort(ev.getConnectStatistics().errnosCounters,
Comparator.comparingInt((p) -> p.key));
- assertEquals(expected, got.toString());
- } catch (Exception e) {
- fail(e.toString());
}
+ return log.toString();
}
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 3d76439..b86188f 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -377,8 +377,8 @@
versioned_file_desc.config.sdkVersion = (uint16_t)sdk_level;
FileOperation new_file_op;
- new_file_op.xml_to_flatten =
- util::make_unique<xml::XmlResource>(versioned_file_desc, doc->root->Clone());
+ new_file_op.xml_to_flatten = util::make_unique<xml::XmlResource>(
+ versioned_file_desc, StringPool{}, doc->root->Clone());
new_file_op.config = versioned_file_desc.config;
new_file_op.entry = file_op->entry;
new_file_op.dst_path =
@@ -1873,7 +1873,8 @@
&options.extensions_to_not_compress)
.OptionalFlagList("--split",
"Split resources matching a set of configs out to a Split APK.\n"
- "Syntax: path/to/output.apk:<config>[,<config>[...]].",
+ "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
+ "On Windows, use a semicolon ';' separator instead.",
&split_args)
.OptionalSwitch("-v", "Enables verbose logging.", &verbose);
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 8f8e0c8..e99ee8a 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -302,7 +302,8 @@
&configs)
.OptionalFlagList("--split",
"Split resources matching a set of configs out to a "
- "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]].",
+ "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
+ "On Windows, use a semicolon ';' separator instead.",
&split_args)
.OptionalSwitch("--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index fd94bbc..14d4260 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -57,10 +57,17 @@
CHECK(out_path != nullptr);
CHECK(out_split != nullptr);
- std::vector<std::string> parts = util::Split(arg, ':');
+#ifdef _WIN32
+ const char sSeparator = ';';
+#else
+ const char sSeparator = ':';
+#endif
+
+ std::vector<std::string> parts = util::Split(arg, sSeparator);
if (parts.size() != 2) {
diag->Error(DiagMessage() << "invalid split parameter '" << arg << "'");
- diag->Note(DiagMessage() << "should be --split path/to/output.apk:<config>[,<config>...]");
+ diag->Note(DiagMessage() << "should be --split path/to/output.apk" << sSeparator
+ << "<config>[,<config>...].");
return false;
}
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 4a278f63..6055190 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -224,7 +224,8 @@
XML_ParserFree(parser);
if (stack.root) {
- return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, std::move(stack.root));
+ return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{},
+ std::move(stack.root));
}
return {};
}
@@ -357,7 +358,7 @@
}
}
}
- return util::make_unique<XmlResource>(ResourceFile{}, std::move(root), std::move(string_pool));
+ return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
}
std::unique_ptr<Node> Namespace::Clone() {
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index f1d0953..6950c30 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -128,8 +128,13 @@
class XmlResource {
public:
ResourceFile file;
- std::unique_ptr<xml::Node> root;
+
+ // StringPool must come before the xml::Node. Destructors are called in reverse order, and
+ // the xml::Node may have StringPool references that need to be destroyed before the StringPool
+ // is destroyed.
StringPool string_pool;
+
+ std::unique_ptr<xml::Node> root;
};
/**